<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[RSS Feed]]></title><description><![CDATA[LINE+ 서버 개발자로 재직중인 이민성(Haon) 입니다.]]></description><link>https://haon.site</link><generator>GatsbyJS</generator><lastBuildDate>Tue, 26 May 2026 08:41:51 GMT</lastBuildDate><item><title><![CDATA[Kafka Consumer 정리]]></title><description><![CDATA[…]]></description><link>https://haon.site/kafka/consumer/</link><guid isPermaLink="false">https://haon.site/kafka/consumer/</guid><pubDate>Sat, 23 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;컨슈머&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%EC%8A%88%EB%A8%B8&quot; aria-label=&quot;컨슈머 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨슈머&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d5df0aa77e70c123ded7a92320071406/a2b88/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.920245398773%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABl0lEQVR42o1Tx4pCQRDcH/cDVLx4MWBCxYNixqwHRRADGDAhKiJivBgQs7XbteiuC8IONI+Z6aqpKvp94Gvd7/eX+r2OxyNOpxMOhwPO5zMe/e9wH3izHg2hUAgKhQJKpRKVSoVnt9vtHeybcLvdotlsEtDtdrHZbJ6K7HY7VCoVNBoNIpEIFosFhsMh5vM5v/V6HbVajfsn4Wq1Qjweh9frRSaTwXg8ZgnYZrPB6XRCp9PB7Xazx2w2k6xarSKVSrFk/0Ioh0Kaz+fRarVQKBSo2GKxkECv15MwHA5T9WAwQDqdRiKRQCAQIOZJKBbltXK5jE6nw2atVkuA3++Hw+GAyWRCMBjEaDRCo9Ggg2KxiFgshmg0in6//0M4m80Yvs/nQzKZZCZiPZvNUp3L5aJlo9GIdrtNNdPplIQej4fYF8syFhKq1HK5xG63w2QywXq9ZmZWq5WWHwolHund7/e4Xq/EXy6X/42NKDAYDFCr1VSZy+WYq7iRB3/3vhD+HdDHrPV6PZRKJeYrNuVeVImivz+BEH4CTZrIEr08WIoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d5df0aa77e70c123ded7a92320071406/a6d36/image.png&quot;
        srcset=&quot;/static/d5df0aa77e70c123ded7a92320071406/222b7/image.png 163w,
/static/d5df0aa77e70c123ded7a92320071406/ff46a/image.png 325w,
/static/d5df0aa77e70c123ded7a92320071406/a6d36/image.png 650w,
/static/d5df0aa77e70c123ded7a92320071406/a2b88/image.png 908w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;컨슈머는 카프카 클러스터 내부 특정 토픽에 데이터가 유입되면, 이 토픽을 구독하며 데이터를 가져오게 되는 주체이다. 프로듀서가 전송한 데이터는 브로커에 적재되며, 컨슈머는 적재된 데이터를 사용하고자 브로커의 특정 토픽으로부터 데이터를 가져와서 내부적으로 필요한 처리를 수행한다.&lt;/p&gt;
&lt;h3 id=&quot;컨슈머-내부-구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%EC%8A%88%EB%A8%B8-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;컨슈머 내부 구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨슈머 내부 구조&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3d57fc217958ae8c2b1baf223c10f9ee/72aae/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.012269938650306%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABj0lEQVR42p2Ry4/BUBTG/cmWEiKxsrCx8IrI6EOLVmmHeLRIhZWERGIlIWHHwkjoLIz4JucIZiZWc26+e889zf2dR31X/F6XywXep4ejd8TJO7FPJ93JfyX6fv46g8xHELLr9Xau12tIsoRqtQqjaqBUKkGv6DBNE4qqQC2qHCuXyyxN0yAXZLgD9wncf+yx2+1wOBywWCyg6zqWyyXm8zn6/T7G4zE26w0c24EsyzeIJEOSJBQKBeTf8rBt+wk0LROhUAjxeBzdbheCIKDRaKDX68F1XY4Nh0MGBYNBxGIxWJaFWq3GIqj1bt2AtImiCL/fj2g0yq2m02nOOBqNMBgMWFQlVZ5IJJBKpfjNvcJMJsMjeQCn0ymazSa3N5vNYBgG2u02y3EcjhOU4jQ3Skogal9RFAh5AU7Xef1TVqsVcrkcstksV5JMJhGJRBAOhxEIBLgLapnGoqoqi3xK/gAS7A7cbrdcbafTQce+qVKpQNM1iJIIVVFRLBZ5xq1Wi0H1eh2T6eQH8M/6r9Hbb4YYXYVmfDfJAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/3d57fc217958ae8c2b1baf223c10f9ee/a6d36/image-1.png&quot;
        srcset=&quot;/static/3d57fc217958ae8c2b1baf223c10f9ee/222b7/image-1.png 163w,
/static/3d57fc217958ae8c2b1baf223c10f9ee/ff46a/image-1.png 325w,
/static/3d57fc217958ae8c2b1baf223c10f9ee/a6d36/image-1.png 650w,
/static/3d57fc217958ae8c2b1baf223c10f9ee/72aae/image-1.png 964w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;패쳐(Fetcher)&lt;/code&gt; : 패쳐(Fetcher) 란 리더 파티션으로부터 레코드들을 미리 가져와서 대기시키는 역할을 한다. 카프카 클러스터에서, 즉 리더 파티션에 있는 브로커가 데이터를 컨슈머 애플리케이션에 보내게 되면, 가장 앞단에서 패쳐(Fetcher) 가 우선적으로 데이터를 전달받게 된다. 패쳐 내부적으로는 데이터를 충분히 전달받게 되면 &lt;code class=&quot;language-text&quot;&gt;completedFetches&lt;/code&gt; 상태로 변하게 되며, 이 상태일 때 &lt;code class=&quot;language-text&quot;&gt;poll()&lt;/code&gt; 메소드를 호출하게 되면 &lt;code class=&quot;language-text&quot;&gt;ConsumerRecords&lt;/code&gt; 라고 하는 객체로 변환하게 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;ConsumerRecords&lt;/code&gt; : 처리하고자 하는 레코드(ConsumerRecord)들의 한 묶음으로, ConsumerRecord 의 복수 형태이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;poll()&lt;/code&gt; : 패쳐에 있는 레코드들을 리턴하는 메소드이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;데이터 처리 속도&lt;/h4&gt;
&lt;p&gt;특징 중 하나로, 이 &lt;code class=&quot;language-text&quot;&gt;poll()&lt;/code&gt; 메소드를 호출하기 전에 이미 패쳐는 데이터를 브로커로부터 가져온다. 따라서, 우리가 &lt;code class=&quot;language-text&quot;&gt;poll()&lt;/code&gt; 메소드를 조금 늦게 호출하더라도, 즉 레코드의 처리 속도가 조금 늦더라도 처리 완료 못한 레코드가 쌓여있다는 점에 대해 걱정할 필요가 없다. 이미 패쳐에서 알아서 충분히 데이터를 가져왔고 대기 시켜놓고 쌓아놓았기 떄문에 천천히 데이터를 꺼내와서 처리해도 된다.&lt;/p&gt;
&lt;p&gt;비슷한 원리로, 브로커의 리더 파티션으로 부터 패쳐가 데이터를 가져올 때도 배치로 데이터를 미리 다 가져오는 형태이기 때문에, &lt;code class=&quot;language-text&quot;&gt;poll()&lt;/code&gt; 을 간혈적으로 호출하며 데이터 처리 속도가 매우 빠른 상황이더라도 크게 걱정할 필요가 없다.&lt;/p&gt;
&lt;h2 id=&quot;consumerrecord-의-offset-과-커밋&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#consumerrecord-%EC%9D%98-offset-%EA%B3%BC-%EC%BB%A4%EB%B0%8B&quot; aria-label=&quot;consumerrecord 의 offset 과 커밋 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ConsumerRecord 의 offset 과 커밋&lt;/h2&gt;
&lt;p&gt;프로듀서에서 보낸 레코드는 브로커(파티션)에 저장될 때 오프셋(offset) 이 지정 된다고 했었다. 다시 표현하자면, 파티션에 쌓이는 레코드는 타임스탬프, 메시지 키, 메시지 값, 오프셋, 헤더 이렇게 5가지 정보를 가지고 있다.&lt;/p&gt;
&lt;p&gt;여기서 오프셋은 0 이상의 숫자로 설정되어 있다. 오프셋은 직접 지정이 불가능하며, 브로커에 저장된 마지막 레코드의 오프셋 값에 +1 한 값으로 저장된다.&lt;/p&gt;
&lt;p&gt;지정된 offset 값은 이 ConsumerRecord 에서 확인할 수 있게 되고, 해당 레코드에 대한 처리가 완료되었다면 &lt;code class=&quot;language-text&quot;&gt;커밋(Commit)&lt;/code&gt; 이라는 과정을 수행하게 된다. &lt;code class=&quot;language-text&quot;&gt;커밋(Commit)&lt;/code&gt; 정확히 어떤(몇번 offset 을 가진) 레코드까지 처리를 완료했음을 표식하는 과정이다. 즉, 커밋을 통해 어떤 레코드까지(몇번 오프셋을 가진 레코드까지) 처리 완료했는지 판단하게 된다.&lt;/p&gt;
&lt;p&gt;다시 말하자면, 커밋이 정상적으로 수행 및 완료되어야지 컨슈머가 레코드를 정상적으로 처리 완료했다는 것을 보장할 수 있게된다.&lt;/p&gt;
&lt;h3 id=&quot;커밋commit&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EB%B0%8Bcommit&quot; aria-label=&quot;커밋commit permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커밋(Commit)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e841b9cb612133283e8cf6d9d98c83c0/d43b4/image-8.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 26.380368098159508%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA0UlEQVR42m1QMQqDQBD0/y+wEbQTsVBEbBREG7VJsLEWo9wFbBLMFcabsAtJDHGW4W6Xm7lhDY1vbXrDc3sy6b7viVpr7KEPyngbEYZhQFEUaJoGURQhSRLUdc2zy3jhN9M0oe97jOMIpdSfMRvS74TT+QTHcRAEAVzXhe/7iOOYZ2TcdR0sy4Jt2zBNE23bso7THyUkQRiGSNMUeZ5/SMZZlqGqKnieh7IsmZSUQPofQyrCQz0gpYS8SggpIITgk/plWXC73zDPM9Z15X0e7fAFVnNwe6vhR8wAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e841b9cb612133283e8cf6d9d98c83c0/a6d36/image-8.png&quot;
        srcset=&quot;/static/e841b9cb612133283e8cf6d9d98c83c0/222b7/image-8.png 163w,
/static/e841b9cb612133283e8cf6d9d98c83c0/ff46a/image-8.png 325w,
/static/e841b9cb612133283e8cf6d9d98c83c0/a6d36/image-8.png 650w,
/static/e841b9cb612133283e8cf6d9d98c83c0/e548f/image-8.png 975w,
/static/e841b9cb612133283e8cf6d9d98c83c0/d43b4/image-8.png 1202w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;컨슈머는 카프카 브로커로부터 레코드를 어디까지 가져갔는지 커밋(commit) 을 통해 기록한다.&lt;/strong&gt; 즉, 컨슈머가 파티션으로 부터 레코드를 가져온 뒤 해당 데이터를 처리하고, 마지막으로 N번째 레코드까지 처리 완료 했음을 표식으로 남기는 과정이 커밋(commit) 이다. 커밋을 수행하면, 특정 토픽의 파티션을 어떤 컨슈머 그룹이 몇 번째(몇번 Offset) 까지 가져갔는지를 카프카 브로커 시스템에 내장된 &lt;code class=&quot;language-text&quot;&gt;__consumer_offsets&lt;/code&gt; 이라는 특별한 토픽에 기록한다.&lt;/p&gt;
&lt;p&gt;어떠한 문제로 컨슈머에 장애가 발생하여 커밋에 재대로 수행되지 않은 경우, 컨슈머는 데이터를 중복 컨슘할 가능성도 있다. 따라서, 데이터 처리에 중복이 발생하지 않도록 만들기 위해선, 즉 컨슈머 애플리케이션 로직을 구성할 떄 특정 레코드를 처리 완료했다고 하는 커밋을 직접 수행하는 로직(&lt;code class=&quot;language-text&quot;&gt;commit()&lt;/code&gt; 이라는 메소드를 직접 수동으로 호출)을 구성해두어 명확히 커밋을 수행하도록 만드는 것이 좋다. 또는 오프셋 커밋이 제대로 수행되었는지 검증하는 로직을 만들어두자. (다만, 실제로는 Dead Letter 재처리등 동일한 내용의 데이터를 재처리 할 일은 많기 때문에, 컨슈머 로직을 멱등하게 구성하는 것이 마음 편할 것이다.)&lt;/p&gt;
&lt;h4&gt;비명시적 오프셋 커밋&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;enable.auto.commit&lt;/code&gt; 옵션의 기본값은 true 인데, 이 상태에서 컨슈머 애플리케이션은 비명시적 오프셋 커밋을 수행한다. 비명시적 오프셋 커밋은 &lt;code class=&quot;language-text&quot;&gt;poll()&lt;/code&gt; 메소드가 실행된 이후 일정 시간이 지나면 그 시점까지 읽은 오프셋을 커밋하는 방식이다. 이때 일정 시간은 &lt;code class=&quot;language-text&quot;&gt;auto.commit.internval.ms&lt;/code&gt; 옵션을 통해 설정한다. 이 방식은 &lt;code class=&quot;language-text&quot;&gt;poll()&lt;/code&gt; 호출 이후에 리밸런싱이 발생하거나, 컨슈머가 비정상적으로 종료되었을 때 메시지가 중복 처리가되거나 유실될 가능성이 있으므로, 이를 허용하지 않는 서비스에선 사용하면 안된다.&lt;/p&gt;
&lt;h4&gt;명시적 오프셋 커밋&lt;/h4&gt;
&lt;p&gt;명시적 방식을 사용하려면, &lt;code class=&quot;language-text&quot;&gt;enable.auto.commit&lt;/code&gt; 을 false 으로 설정하여 자동으로 커밋하지 않도록 하고, &lt;code class=&quot;language-text&quot;&gt;poll()&lt;/code&gt; 메소드 호출 이후에 commitSync() 메소드를 호출한다. commitSync() 메서드는 poll() 메서드를 통해 반환된 레코드의 가장 마지막 오프셋을 기준으로 커밋한다.&lt;/p&gt;
&lt;p&gt;단, commitSync() 는 동기적으로 커밋하는 방식이라서 비명시적 방식에 비해 단위 시간당 처리량이 낮다. 처리량을 높이고 싶으면, 비동기적으로 커밋을 하는 commitAsync() 메서드를 사용하면 된다. commitAsync() 를 사용하면 커밋 요청 후 응답이 올 때 까지 데이터를 처리할 수 있다. 다만, 커밋이 실패한 경우 순서 보장이 불가능하며, 중복 처리가 될 수도 있다. 또 commitSync() 는 실패시 성공하거나 재시도할 수 없는 오류가 발생할 때 까지 재시도하지만, commitAsync() 는 그렇지 않다.&lt;/p&gt;
&lt;h4&gt;초기 오프셋 전략&lt;/h4&gt;
&lt;p&gt;컨슈머 그룹이 파티션의 레코드를 읽으려고 하는데, 오프셋이 지정되어 있지 않다면 어떻게 처리할까? &lt;code class=&quot;language-text&quot;&gt;auto.offset.reset&lt;/code&gt; 옵션을 통해 latest , earliest , none 중 하나를 선택할 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;latest&lt;/code&gt; : 가장 최근의 (오프셋이 가장 높은) 레코드부터 처리한다. 이 옵션이 기본값이다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;earliest&lt;/code&gt; : 가장 오래된 (오프셋이 가장 낮은) 레코드부터 처리한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;none&lt;/code&gt; : 커밋 기록이 없다면 오류를 반환하고, 커밋 기록이 있다면 마지막 커밋 이후 오프셋부터 읽기 시작한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;컨슈머-그룹&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%EC%8A%88%EB%A8%B8-%EA%B7%B8%EB%A3%B9&quot; aria-label=&quot;컨슈머 그룹 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨슈머 그룹&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f4e489270be8ed23eaab29ba028c0cda/669eb/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.607361963190186%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAABF0lEQVR42j1R246CUAzk/5+IEUIQZCMQDRdFLkFNTPglFeV2NrM73Zx9IKedmU5LawzDgGEcsKgF0zzhM3wkn5cZ4zSCvFJKYs1Ry5eYjsmN4wjDdV14noemaZCmKVarFTabDcqqRBRFEh+PR4RhiPV6jeArQF3X2G63wmdZJhrHccTHOJ1OuF6voLFlWbhcLqiqSoR5nqMsS8FpervdhEuSBOfzWTDbtlEUBe73O3a7HQwmFPq+jyAIsN/vpTOLaMjJTdOUCdu2BQfQhoy1Ydd1f4bsSCF/g9McDof/AuYUc1pi5NiQeFVWoqGJHiqOYxiP5wPP11MW2797vPqX5Fwyj8RFL8vvwaZJcn7MiTNW30pq9ZF+AHPhisIXLEGlAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/f4e489270be8ed23eaab29ba028c0cda/a6d36/image-2.png&quot;
        srcset=&quot;/static/f4e489270be8ed23eaab29ba028c0cda/222b7/image-2.png 163w,
/static/f4e489270be8ed23eaab29ba028c0cda/ff46a/image-2.png 325w,
/static/f4e489270be8ed23eaab29ba028c0cda/a6d36/image-2.png 650w,
/static/f4e489270be8ed23eaab29ba028c0cda/e548f/image-2.png 975w,
/static/f4e489270be8ed23eaab29ba028c0cda/669eb/image-2.png 1244w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;컨슈머 그룹은 어떤 특정 토픽의 데이터를 처리하는 컨슈머들을 묶은 그룹이다. &lt;strong&gt;즉, 컨슈머 그룹에 묶인 컨슈머들은 동일한 토픽을 구독하고 처리한다. 동일한 컨슈머 그룹을 가진 컨슈머들은 기본적으로는 다 동일한 내부 로직을 가지고 있는 컨슈머이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;특정 하나의 토픽에 대해선 2개 이상의 여러 컨슈머 그룹에서 동일한 토픽을 구독할 수 있다. 또한, 동일한 토픽을 구독중인 컨슈머 그룹은 다른 컨슈머 그룹과 서로 영향을 주고 받지 않는다.&lt;/strong&gt;  이전에 학습했기를, 파티션에 있는 데이터를 컨슈머가 가져간다고 해서 파티션에 쌓인 데이터가 사라지지는 않는다고 했었다. 이 원리에 따라서, 여러개의 컨슈머 그룹에서 특정 동일한 토픽을 구독하고 하는 상황이고, 이때 컨슈머 그룹 중 하나가 파티션에서 레코드를 가져간다고 한들 파티션에서 레코드가 사라지지 않기 때문에 다른 컨슈머 그룹에서도 해당 파티션에 있는 동일한 레코드를 가져갈 수 있게 되는 것이다.&lt;/p&gt;
&lt;p&gt;카프카가 아닌 일반적인 메시지 브로커는 메시지를 컨슈머에 푸시(push) 하는 방식으로 동작하는데, 이 방식의 단점은 컨슈머가 자기 자신의 가용량을 엄두할 수 없다는 점이다. 반면, &lt;strong&gt;카프카의 컨슈머는 다른 메시지 브로커와 다르게, 직접 레코드를 풀링(Polling) 하여 읽어오는 방식으로 동작한다.&lt;/strong&gt; 따라서 컨슈머 각자의 가용량에 따라 유연하게 메시지를 처리할 수 있다. 컨슈머 애플리케이션이 풀링하기 위해선 &lt;code class=&quot;language-text&quot;&gt;poll()&lt;/code&gt; 메소드를 실행한다.&lt;/p&gt;
&lt;h3 id=&quot;컨슈머-개수와-파티션-개수-비율&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%EC%8A%88%EB%A8%B8-%EA%B0%9C%EC%88%98%EC%99%80-%ED%8C%8C%ED%8B%B0%EC%85%98-%EA%B0%9C%EC%88%98-%EB%B9%84%EC%9C%A8&quot; aria-label=&quot;컨슈머 개수와 파티션 개수 비율 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨슈머 개수와 파티션 개수 비율&lt;/h3&gt;
&lt;p&gt;컨슈머 그룹으로 묶인 컨슈머들은 구독하고 있는 특정 토픽 안의 여러개의 파티션들에 동시에 할당되어 데이터를 가져갈 수 있다. &lt;strong&gt;반면 1개의 파티션은 최대 1개의 컨슈머에게만 할당될 수 있다.&lt;/strong&gt; 이러한 특징에 따라, &lt;strong&gt;컨슈머 그룹의 컨슈머 개수는 구독중인 토픽안의 파티션 개수보다 같거나 작게 만드는 것이 좋다. 가급적이면, 컨슈머와 파티션 개수가 1:1 로 매칭되도록 개수를 동일하게 맞춰주는 것이 베스트이다.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;컨슈머 그룹의 컨슈머 개수가 파티션 개수보다 많을 경우&lt;/h4&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/641d498f293795bb3d29d7b2add6cc6b/84ee5/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.717791411042946%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABEUlEQVR42o2RS2uDQBDH8/kTPEZQISK+CRHfQfAi6C0lPSS9BHKKeIpG7VH9l9mS0kJL3WHYmWXnN6/FhJ8yjAMe7QP3+o6madD1HeqmZj4p2f17j+kPWTyNcRpBhwCqqoLjOIiiiDAMsdlssFqtsFwusV6vEUYhhmmYCew6OI6D/X4PwzAgSRJM00SapkiSBEEQwPO8r/+zKrQsiwVpmgae56EoCqIoYkrvT+CsCtuuZQFZlmG327H2t9st8/M8Z1DXdecDqUJqkWCyLEMQBOi6jjiO2Twpme/7/wO/b/l2u+F6vaIsSxRFAdu22UwJSolIZwNJ2LCnz7uqKhxeDjidTzi/nXF8PeJyufy6EAJ+AGMhRKnsflKeAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/641d498f293795bb3d29d7b2add6cc6b/a6d36/image-4.png&quot;
        srcset=&quot;/static/641d498f293795bb3d29d7b2add6cc6b/222b7/image-4.png 163w,
/static/641d498f293795bb3d29d7b2add6cc6b/ff46a/image-4.png 325w,
/static/641d498f293795bb3d29d7b2add6cc6b/a6d36/image-4.png 650w,
/static/641d498f293795bb3d29d7b2add6cc6b/e548f/image-4.png 975w,
/static/641d498f293795bb3d29d7b2add6cc6b/84ee5/image-4.png 1076w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;컨슈머가 4개, 파티션이 3개로 컨슈머가 더 많은 상황을 가정해보자. 파티션은 최대 1개의 컨슈머에게만 할당될 수 있다고 했었기 때문에, 파티션 3개에 컨슈머 3개가 1:1 으로 할당되고, 나머지 1개의 여분 컨슈머는 유휴 상태(idle) 가 된다.&lt;/p&gt;
&lt;p&gt;이러한 특징으로 보아, 계속 강조하였듯이 파티션 개수만큼만 컨슈머를 딱 맞추어 운영하는 것이 좋다. (정 필요하다면, 컨슈머 개수를 파티션 개수보다 더 늘려서 운영하는 것이 좋다.)&lt;/p&gt;
&lt;h3 id=&quot;컨슈머-그룹을-활용하는-이유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%EC%8A%88%EB%A8%B8-%EA%B7%B8%EB%A3%B9%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0&quot; aria-label=&quot;컨슈머 그룹을 활용하는 이유 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨슈머 그룹을 활용하는 이유&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/59cd314ea086ace7883ad493f68d9cc3/26a94/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.19631901840491%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABD0lEQVR42o1RwWqDQBTM96c3A4rEiyZEIUoSvSgoeLPgTQho1l69SmyaJrrrlH2lkNC0ZB7LW3Z482bYyYj7EqNAP/TggmPgw3cfBnq/5YUQGB/U5FZI4vRxwtpdwzRNmJaJ+XyOlb1C0zTEy14UBaqqooUS/woeuyMURUEYhvB9H4EfYLvdkgDnnO6qqsJaWLRc4sf9Q8HuvYOmaTQoTxAEcBwHeZ4TzxiDYRhIkuS5yNKhruvwPA+u62Kz2cC2bZRlSfx+v0eapsiyDJf+8lzk2WxGDna7HaIoIpfS4fV6RRzHmE5fsFwucP48/x1ZloT8wbquwWpG8dgbQ3Wo0HUd8W3bInvNcGCHX+6k4BdqzgD9eN6r2QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/59cd314ea086ace7883ad493f68d9cc3/a6d36/image-5.png&quot;
        srcset=&quot;/static/59cd314ea086ace7883ad493f68d9cc3/222b7/image-5.png 163w,
/static/59cd314ea086ace7883ad493f68d9cc3/ff46a/image-5.png 325w,
/static/59cd314ea086ace7883ad493f68d9cc3/a6d36/image-5.png 650w,
/static/59cd314ea086ace7883ad493f68d9cc3/e548f/image-5.png 975w,
/static/59cd314ea086ace7883ad493f68d9cc3/26a94/image-5.png 1102w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;컨슈머 그룹은 왜 등장하였으며, 언제 어떻게 활용하는 것일까? 이를 위해, 컨슈머 그룹이 없는 상황을 가정해보자. 운영 서버의 주요 리소스인 CPU, 메모리 정보를 수집하는 데이터 파이프라인을 구축한다고 가정해보자. 실시간 리소스를 시간 순으로 확인하기 위해서 데이터를 Elastic Search 에 저장하고, 이와 동시에 대용량 적재를 위해 하둡에 적재할 것이다.&lt;/p&gt;
&lt;p&gt;만약 카프카를 활용한 파이프라인이 아니라면, 서버에서 실행되는 리소스 수집 및 전송 에이전트는 수집한 리소스를 엘라스틱 서치와 하둡에 적재하기 위해 동기적으로 적재를 요청 할 것이다. 이렇게 동기로 실행되는 에이전트는 엘라스틱 서치 또는 하둡 둘 중 하나에 장애가 발생한다면 더 이상은 적재가 불가능할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8406fc7c7e219904ff826d2ed7e779f0/a3767/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.355828220858896%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABCklEQVR42o2RsY+CUAzG/euZTVyYgEEQEBQhCgsQGCA5Lu6ElQlPbhDOi/D4LnTwHDhybZq+Ju0vX/sWA3791txwra9o2gbtV/t81581Lh8XVJcK9+87etZTDBO+eC222y04joPneYjjGIIgYLfbYblcQpIkrFYrHI9H+L6PIAjABjYNHK3rO6iqio2ygeu6iKIIh8MBPM9TjLVlWQRSFAW2bdPcLHBsGlWe3BOpNAyDlGmaBtM0Ked5Tv19/4+Vx6au62igLEsURYE0TUmh4zgQRZHAWfaG7D0DA5sHvhpj7AkebxmGIXRdx3q9hizL2Fv7v1eeA1ZVBduxkSQJneR8PuPRPej3p4A/7Jb6drFBKyEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/8406fc7c7e219904ff826d2ed7e779f0/a6d36/image-6.png&quot;
        srcset=&quot;/static/8406fc7c7e219904ff826d2ed7e779f0/222b7/image-6.png 163w,
/static/8406fc7c7e219904ff826d2ed7e779f0/ff46a/image-6.png 325w,
/static/8406fc7c7e219904ff826d2ed7e779f0/a6d36/image-6.png 650w,
/static/8406fc7c7e219904ff826d2ed7e779f0/e548f/image-6.png 975w,
/static/8406fc7c7e219904ff826d2ed7e779f0/a3767/image-6.png 1210w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이런 상황은 어떻게 해결할 수 있을까? 바로 카프카 토픽과 컨슈머 그룹으로 데이터를 나누는 것이다. 리소스를 수집하는 특정 에이전트(애플리케이션) 가 프로듀서(Producer) 역할을 하도록 만들고, 엘라스틱서치와 하둡 각각에 대해 컨슈머 그룹을 생성해두는 것이다. 프로듀서에선 데이터를 일단 카프카에 보내고, 카프카내 저장된 데이터를 서로 다른 목적을 가진 컨슈머 그룹들(엘라스틱 서치 적재 / 하둡 적재 컨슈머 그룹) 이 각각 데이터를 가져가도록 한다. 이렇게 구성하면 만약 장애가 터져도 문제가 없다. 가령 엘라스틱 서치에 장애가 텨져도 데이터를 조금 늦게 쌓고, FailOver 한 뒤에 마지막으로 적재된 예전 데이터부터 다시 천천히 적재를 수행하고 시계열화하면 되는 것이다.&lt;/p&gt;
&lt;p&gt;그리고 이렇게 목적에 따른 컨슈머 그룹을 따로 구분해 운영하면 또 다른 이점이 생긴다. &lt;strong&gt;위 예시처럼 컨슈머 애플리케이션 특성에 따라 컨슈머 개수를 조절하여 처리량을 각기 다르게 조절할 수 있다는 점이다.&lt;/strong&gt; 위의 경우 엘라스틱 서치에 데이터 적재시 처리량이 낮아도 되기 때문에 컨슈머를 1개만 운영하였고, 반면 하둡 적재시에는 처리량이 높아야해서 컨슈머를 3개로 여러개 운영하고 있다.&lt;/p&gt;
&lt;h3 id=&quot;thread-safe-를-보장할-것&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#thread-safe-%EB%A5%BC-%EB%B3%B4%EC%9E%A5%ED%95%A0-%EA%B2%83&quot; aria-label=&quot;thread safe 를 보장할 것 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Thread-Safe 를 보장할 것&lt;/h3&gt;
&lt;p&gt;Thread-Safe 측면에서 주의할 점은, &lt;strong&gt;동일한 컨슈머 그룹이라면 하나의 쓰레드에서 여러개의 컨슈머를 실행해서는 안된다는 점이다.&lt;/strong&gt; 한 애플리케이션에서 동일한 컨슈머 그룹의 여러 컨슈머를 실행하려면, 별도의 쓰레드에서 실행해야한다.&lt;/p&gt;
&lt;h2 id=&quot;리밸런싱rebalancing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%EB%B0%B8%EB%9F%B0%EC%8B%B1rebalancing&quot; aria-label=&quot;리밸런싱rebalancing permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리밸런싱(rebalancing)&lt;/h2&gt;
&lt;p&gt;카프카 컨슈머는 리밸런싱이라는 FailOver 방식이 있다. 컨슈머 그룹에 속한 컨슈머 중에 특정 컨슈머에 장애가 터지면, 해당 컨슈머는 일시적으로 컨슈머 그룹에서 내쫓기며 파티션으로부터 유입되는 데이터를 처리하지 않고 유휴(idle) 상태로 넘어간 뒤에 회복이 완료되면 다시 컨슈머 그룹에 참여하여 파티션의 데이터를 처리하게 된다. (이때, 내쫓긴 컨슈머에 매핑되어 있던 파티션은 새로운 컨슈머에 임시적으로 매핑될 것이다.) 이렇게 컨슈머 그룹이 FailOver 하는 과정을 리밸런싱(rebalancing) 이라고 한다.&lt;/p&gt;
&lt;h3 id=&quot;리밸런스-리스너rebalance-listener&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%EB%B0%B8%EB%9F%B0%EC%8A%A4-%EB%A6%AC%EC%8A%A4%EB%84%88rebalance-listener&quot; aria-label=&quot;리밸런스 리스너rebalance listener permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리밸런스 리스너(rebalance listener)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d3ee8cf8981f726adbab368a77611061/73caa/image-7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 28.22085889570552%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAABBUlEQVR42m1R22qDUBDML/sxCqKgIl4eBK/xHsU++qKQkj6kqSCk3xAtOu3Z0NAUd1nOcGZ3dmB3K+45f824TTdM80T1i9n/si4Pnr3/+fVP7lgzi+alAc/z0HUdtm0TZpUkCfFlWT540zQJC4KAQ3kgnuk8CdZ1Ddd1IUkSFEVBmqYIwxCWaSGKIsRxDM/zIIoiieZ5Dt/36X9TsGkacqNpGg1UVUVLVFUFx3E0XBQFuWLusixDEATYx/ttwaIs4DgONcuyDE3VSIy5YpHECWzLhmEYaNuWljOX18/rtuA4juj7HsfXI05vJ8KsLh8X4odhQNd1OL+fsSzL/SDzz0HW9eko38g/n23Zpa9UAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d3ee8cf8981f726adbab368a77611061/a6d36/image-7.png&quot;
        srcset=&quot;/static/d3ee8cf8981f726adbab368a77611061/222b7/image-7.png 163w,
/static/d3ee8cf8981f726adbab368a77611061/ff46a/image-7.png 325w,
/static/d3ee8cf8981f726adbab368a77611061/a6d36/image-7.png 650w,
/static/d3ee8cf8981f726adbab368a77611061/e548f/image-7.png 975w,
/static/d3ee8cf8981f726adbab368a77611061/73caa/image-7.png 1110w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이런 리밸런싱은 컨슈머가 데이터를 처리하는 도중에 언제든지 발생할 수 있다. 따라서 이 리밸런싱 발생시 어떻게 내부적으로 동작할 것인지에 대한, 즉 리밸런싱에 대한 로직을 추가로 메소드로 구현할 수 있다. 이를 리밸런스 리스너(rebalance listener) 라고 한다.&lt;/p&gt;
&lt;h3 id=&quot;파티션-개수와-리밸런싱-소요시간&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%ED%8B%B0%EC%85%98-%EA%B0%9C%EC%88%98%EC%99%80-%EB%A6%AC%EB%B0%B8%EB%9F%B0%EC%8B%B1-%EC%86%8C%EC%9A%94%EC%8B%9C%EA%B0%84&quot; aria-label=&quot;파티션 개수와 리밸런싱 소요시간 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파티션 개수와 리밸런싱 소요시간&lt;/h3&gt;
&lt;p&gt;파티션 개수가 많아질수록, 그만큼 리밸런싱의 소요 시간은 길어진다. 가령 토픽에 파티션이 1천개 있고, 컨슈머 그룹에도 동일하게 컨슈머가 1천개 있는 상황을 가정해보자. 컨슈머 1개에 장애가 터지면, 토픽에 있는 파티션 1천개는 컨슈머 999개와 새롭게 매핑 관계를 성립해야하는 내부적인 리밸런싱 과정을 거치게 된다.&lt;/p&gt;
&lt;p&gt;따라서, 리밸런싱이 일어나는 상황 자체가 사실상 장애와 비슷한 상황이라고도 볼 수 있다. 따라서 리밸런싱이 자주 발생하지 않도록 운영하는 것이 가장 바람직하지만, 만에 하나 발생할 수도 있는 리밸런싱을 대비하여, 그에 대응하는 로직을 리밸런스 리스너(rebalance listener) 를 통해 미리 구성해두는 것이 좋다.&lt;/p&gt;
&lt;h2 id=&quot;assginor&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#assginor&quot; aria-label=&quot;assginor permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Assginor&lt;/h2&gt;
&lt;p&gt;Assginor 는 컨슈머와 파티션을 어떻게 매핑할지에 대한 정책을 담고있는 주체이다. 카프카에선 &lt;code class=&quot;language-text&quot;&gt;RangeAssignor&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;RoundRobinAssignor&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;StickyAssignor&lt;/code&gt; 3가지를 제공하며, 카프카 2.5.0 기준으로는 &lt;code class=&quot;language-text&quot;&gt;RangeAssignor&lt;/code&gt; 가 디폴트 어사이너로 설정되어 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;RangeAssignor&lt;/code&gt; : 각 토픽에서 파티션을 숫자로 정렬, 컨슈머를 사전 순서로 정렬하여 할당하는 방식&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;RoundRobinAssignor&lt;/code&gt; : 모든 파티션을 컨슈머에서 번갈아가면서 할당하는 방식&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;StickyAssignor&lt;/code&gt; : 최대한 파티션을 균등하게 배분하면서 할당하는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;컨슈머-랙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%EC%8A%88%EB%A8%B8-%EB%9E%99&quot; aria-label=&quot;컨슈머 랙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨슈머 랙&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fa2080ee429f0e34c09c5be6b870aad3/99661/image-9.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.74233128834356%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA1UlEQVR42o2RPQ9EQBCG/f+fQKNUKEgkFKKh8LGIEInC7fWOQkIQ9r3Ya+4jjplM3t3Z5JnZGYHh04+MXXThO1HfapCYIEkTfp7mCfMyY13XS4V+gI7jQBRFSJIEXddBKUVZljyWZcEwDBjHkete6BRoWRZUVYXv+wjDEIQQRFEE13Vf9737JEEQBCiKgne5se3/l/M8R1VVXG3bRhzHSNMUnufBMAxomgbTNEHv9Bz4MR/G0HUd2rZF3/fIsgyKokCWZT6O5tGcA/fH99ihh5tnv1t+AkEzEY9es3QLAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/fa2080ee429f0e34c09c5be6b870aad3/a6d36/image-9.png&quot;
        srcset=&quot;/static/fa2080ee429f0e34c09c5be6b870aad3/222b7/image-9.png 163w,
/static/fa2080ee429f0e34c09c5be6b870aad3/ff46a/image-9.png 325w,
/static/fa2080ee429f0e34c09c5be6b870aad3/a6d36/image-9.png 650w,
/static/fa2080ee429f0e34c09c5be6b870aad3/e548f/image-9.png 975w,
/static/fa2080ee429f0e34c09c5be6b870aad3/99661/image-9.png 1098w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;컨슈머는 본인의 상황 또는 처리 성능에 따라서 파티션에 쌓인 레코드를 가져와 빠르게 처리 못하는 상황이 발생할 수 있다. 이렇게 컨슈머가 파티션에 쌓인 레코드를 처리 못하고 지연이 발생하여 데이터가 쌓인 상황을 &quot;컨슈머 랙이 쌓였다(발생했다)&quot; 라고 표현한다.&lt;/p&gt;
&lt;p&gt;컨슈머 랙이 많이 쌓였다는 것은, 그만큼 컨슈머가 프로듀서에 비해 처리량이 낮거나 또는 정상 동작하지 않는 상황임으로 알 수 있다. 따라서, 서비스를 운영할 떄 컨슈머 랙이 쌓여있는지 모니터링 시스템을 구축해두는 것이 좋다. 이어서, &lt;strong&gt;파티션 개수와 컨슈머 개수를 늘려서 처리량을 늘리는 방법으로 쌓여있는 랙을 해결하는 것이 바람직할 것이다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;아파치 카프카 애플리케이션 프로그래밍 - 최원영&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/&quot;&gt;https://kafka.apache.org/documentation/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/kafka-consumer/&quot;&gt;https://hudi.blog/kafka-consumer/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Kafka Producer 정리]]></title><description><![CDATA[…]]></description><link>https://haon.site/kafka/producer/</link><guid isPermaLink="false">https://haon.site/kafka/producer/</guid><pubDate>Fri, 15 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;프로듀서&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A1%9C%EB%93%80%EC%84%9C&quot; aria-label=&quot;프로듀서 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프로듀서&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d258fb2e9477559b1a40ee890d3c99a7/5a190/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.69325153374233%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB4klEQVR42mVS3XLSYBDtG6sv0HHqTRln1Itiq9NAfghQQggJIQFaKJ0KHfVCrXVGetHxEQokoUjjcXdD0dHMnHyT3ZOzZ3e/rfuf97hL7rBIFhni/5FEieSYl8RJFo+yb/7/b2wtF0v0ul00bBtNpwm3mcFzXUHTcdCik/OMlutR3hFO0A5EfLVcCUSQA3XLgq5pKBnG5tRUFQVFgVWr4evlJT5+eC/48vmTcDhnlkrinJ80TTPBeB7juNtDr9NB/+SYBI6gHCpShB2pxSIC38f52ZkgbLelmF2vwyLO9eQa49EYk+8TpKs0E+yGHVTLJlyngSJVfrq9La6+XV0hv7eH4WBAnADnwyF8zyNnBn7c3KBaqeB1Po8njx/BpK5SdsgDb5MDraDAtmo4qlahkoNOGMps3745EPcXoxHG70bwWx51cEjdnIjL4eBUTJz2+5lDnqFN1tVCgaroMNYzNHRdwAV4FIHfIuGQBPo0QxUa8dihzPAXRCxbCgWcRkNEyqZJ7ZjrsySCvABeDAs/bNpcc1mQR8Zimy3zXeIrYOjaRqhSLuNgfx/PcznkdnfxbGcHr16+kKKcZx4X40Lc4YM7EeRXNIswu70lzNaYYj6dSTyaR4ijOONM/+QZHPv3Yv8G09OZzcUpCIgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d258fb2e9477559b1a40ee890d3c99a7/a6d36/image.png&quot;
        srcset=&quot;/static/d258fb2e9477559b1a40ee890d3c99a7/222b7/image.png 163w,
/static/d258fb2e9477559b1a40ee890d3c99a7/ff46a/image.png 325w,
/static/d258fb2e9477559b1a40ee890d3c99a7/a6d36/image.png 650w,
/static/d258fb2e9477559b1a40ee890d3c99a7/5a190/image.png 800w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;카프카에서 데이터의 시작점은 프로듀서이다. 프로듀서 애플리케이션은 카프카에 필요한 데이터를 선언하고, 브로커의 특정 토픽 안의 파티션에다 데이터를 전송한다. 프로듀서는 데이터를 전송할 때 리더 파티션을 가지고 있는 카프카 브로커와 직접 통신한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;이때, 프로듀서와 컨슈머 모두 이 카프카 클러스터에 있는 토픽에 접근할 때는 반드시 리더 파티션이 있는 브로커와 통신한다.&lt;/strong&gt; 이전에 학습했기를, 리더 파티션은 프로듀서가 보낸 데이터를 저장하는 역할을 수행하며, 팔로워 파티션은 리더에 있는 데이터를 실시간으로 복제(Replication) 하며, 장애 발생시 FailOver 하는 역할을 한다고 했었다. 즉, 프로듀서는 실질적으로 통신을 하는 브로커가 위치한 리더 파티션과 통신을 하게 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;프로듀서는 내부적으로 카프카 브로커와 통신할 때, &lt;strong&gt;내부적으로 파티셔너(Partitioner), 배치 생성 단계를 거치게 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;프로듀서-내부-구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A1%9C%EB%93%80%EC%84%9C-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;프로듀서 내부 구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프로듀서 내부 구조&lt;/h3&gt;
&lt;p&gt;프로듀서 내부는 다음과 같은 컴포넌트들로 구성된다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8ceb032b4fb7d0c0276aea8ae6d0a49d/5a6dd/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.852760736196316%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAACC0lEQVR42lVSa2/aQBD0j+YjEhJC/IBQEUFrwMaElw3hnYSq4ABJoWqCKRAw/IM+IKQh4ZXp7kHS1tLo7nbXc7OzJ23WWzwtn96wfDys/8Qefy8pvhS5/f5vbr3aYLvZvkF6WDwgkUgIJJNJpFKp/8CxbCYDXdeRTqfFnuN8jkajuLsbgb/1agUWJ93PF1BVFclEUpByEYNjEUWBSnur28Xn62u0mk10u7fI5U6hqAoCgQAsyxKEL7sX7LY7SLPZXBRUymWUikUiTuwvIGWGoSMcDqFcKhFZA1dE2Go0KBamXAaRSAQ3X28w6A/Qbnfw4/tPIvw1QzpF6uInMPQ03geDcLvd6LTbGI9GOD72o5DP47xSRi6bRcOs453Ph9FwiOrFBeLxOFwuFxwOhyAWhBnDQExTcZoxhEJWcH52BtM0EaQLioU8rlpNNC5NwiX8fj9qtRoJ0FGtfoQsy/AdHcEe25Dm1LJKXoVCMjTyixGLxfYeUktMbtbrqNc+0SUVfOm0cXLIs4f9b30yEGL6q+c1pMX9gn5UiEQTw9A0TRByKzwkPhfJW/aZFRULBZFjj2X5A3q9nhgKT1g8GyaUQyGh5FWRQop95JPX64XH44HT6RQrtx+iWgbXcOtW1zo8m/WecPW8wnQyFf1P7Alse7+OR2MMB0MBNpvX1xrG1J6KGp4BP5nNev/A/wB9vo3J9qzVNwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/8ceb032b4fb7d0c0276aea8ae6d0a49d/a6d36/image-2.png&quot;
        srcset=&quot;/static/8ceb032b4fb7d0c0276aea8ae6d0a49d/222b7/image-2.png 163w,
/static/8ceb032b4fb7d0c0276aea8ae6d0a49d/ff46a/image-2.png 325w,
/static/8ceb032b4fb7d0c0276aea8ae6d0a49d/a6d36/image-2.png 650w,
/static/8ceb032b4fb7d0c0276aea8ae6d0a49d/5a6dd/image-2.png 802w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;ProducerRecord&lt;/code&gt; : 프로듀서가 전송하는 레코드 타입이다. 다시 말해, 이 클래스를 통해 선언된 레코드 객체를 프로듀서가 프로듀싱 할 수 있다. &lt;strong&gt;내부적으로는 토픽(topic), 파티션(partition), 타임스탬프(timestamp), 메시지 키(message key), 메시지 값(message value) 으로 구성 및 정의할 수 있다.&lt;/strong&gt; 단, 오프셋은 별도로 존재(지정)하지 않는다. 오프셋은 카프카의 특정 파티션에 데이터가 저장된 이후에 지정된다. 그리고 기본적으로는, 프로듀서에서 레코드에 토픽과 메시지 값만 지정되어 있더라도 레코드를 전송할 수 있다. (나머지는 모두 Optional 하다.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;send()&lt;/code&gt; : 레코드를 전송 요청하는 메소드이다. 즉, Producer Record 는 &lt;code class=&quot;language-text&quot;&gt;send()&lt;/code&gt; 메소드를 호출하여 전송할 수 있다. 이때 유의할 점은, 레코드를 send 를 호출하는 그 즉시 전송하는 것이 아니라, 내부적인 과정을 일부 거친 뒤 전송하게 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;파티셔너(Partitioner)&lt;/code&gt; : 파티셔너는 &lt;strong&gt;어느 파티션으로 레코드를 전송할지 지정해주는 라우팅 역할을 담당한다.&lt;/strong&gt; (마치 로드밸런서의 역할이다.) 레코드의 메시지 키(message key) 를 해싱하여 도출된 해시 결과값을 기반으로 몇번 파티션에 전송할지를 내부적으로 결정해준다. 동일한 메시지 키를 가진 레코드는 항상 동일한 파티션으로 전송되는 특징이 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Accumulator&lt;/code&gt; : 파티셔너에 의해 어떤 파티션으로 전송될지 결정된다면, Accumulator 에서는 배치라는 단위로 레코드 여러개를 한 뭉텅이(배치)로 묶는 작업을 한다. 배치로 왜 묶는가라고 하면, 매번 모든 레코드 각각에 대해 send() 를 호출할 때 마다 레코드를 전송하게 되면 네트워크상의 병목과 리소스 낭비가 심해질 것이다. 따라서, 레코드 여러개를 배치로 묶고 전송하게 된다. 즉, Accumulator 를 활용하면 레코드가 많응 경우 최대한 레코드 여러개를 배치로 묶어서 전송하면 높은 데이터 처리량을 가질 수 있게된다. 다시 정리하자면, &lt;strong&gt;Accumulator 에 의해 배치라는 단위로 묶여진 레코드 뭉텅이가 일정량 쌓이고 나면, Sender 배치를 브로커와 통신해서 전송하게 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;파티셔너&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%ED%8B%B0%EC%85%94%EB%84%88&quot; aria-label=&quot;파티셔너 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파티셔너&lt;/h2&gt;
&lt;p&gt;프로듀서 API 를 사용하게 되면 2가지의 파티셔너를 제공받게 된다. 바로 &lt;code class=&quot;language-text&quot;&gt;UniformStickyPartitioner&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;RoundRobinPartitioner&lt;/code&gt; 이다. 이 중에 디폴트 파티셔너는 &lt;code class=&quot;language-text&quot;&gt;UniformStickyPartitioner&lt;/code&gt; 으로 설정되어 있다. 이 두 가지 파티셔너들은 각각 어떻게 동작할까? 이 파티셔너들은 레코드에 메시지 키가 있을 때와, 반대로 메시지 키가 없을 때 동작 방식이 조금씩 다른데, 한번 살펴보자.&lt;/p&gt;
&lt;h3 id=&quot;메시지-키가-있을-경우-동작&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%94%EC%8B%9C%EC%A7%80-%ED%82%A4%EA%B0%80-%EC%9E%88%EC%9D%84-%EA%B2%BD%EC%9A%B0-%EB%8F%99%EC%9E%91&quot; aria-label=&quot;메시지 키가 있을 경우 동작 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메시지 키가 있을 경우 동작&lt;/h3&gt;
&lt;p&gt;메시지 키가 있을 경우, &lt;code class=&quot;language-text&quot;&gt;UniformStickyPartitioner&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;RoundRobinPartitioner&lt;/code&gt; 모두 메시지 키를 해싱한 결과 값을 특정 파티션에 매핑하고, 해당 파티션에다 레코드를 전송한다. &lt;strong&gt;즉, 동일한 메시지 키를 갖는 레코드는 항상 동일한 파티션(파티션 번호) 에 전송된다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;단, 파티션 개수가 도중에 갑자기 변경될 경우, 기존에 매핑되었던 메시지 키와 파티션 번호간의 매핑 관계는 깨지게 된다. 즉, 파티션 개수가 변경되면 기존 레코드는 새롭게 해싱된 결과값을 기반으로 또 다른 파티션에 새롭게 매핑된다. (몰론 해시 결과값이 이전과 동일한 상황이라면, 해당 레코드는 동일한 파티션에 다시 매핑되는 케이스도 존재할 수 있다.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;따라서, 만약 메시지 키를 활용해야 하는 상황이라면, 파티션 개수를 충분히 많은 개수로 지정하는 것이 좋다.&lt;/strong&gt; 프로듀서가 보내는 데이터량, 컨슈머가 처리하는 데이터량을 고민을 해서 파티션 개수를 충분히 크게 지정하자. 왜냐하면, 파티션 개수에 따라서 컨슈머의 처리량이 달라지기 때문이다. (파티션 개수만큼 컨슈머 개수를 늘려서 데이터를 처리하기 때문이다.) 따라서, 파티션 개수를 변경하는 일이 발생하지 않도록 만들기 위해선 파티션 개수를 충분히 큰 개수로 지정하고 운영하면 된다.&lt;/p&gt;
&lt;p&gt;예를들어 컨슈머가 초당 10개의 데이터를 처리하고, 프로듀서는 100개씩 처리(전송)하는 경우라면, 컨슈머를 10개씩 띄우면 되겠구나라는걸 알고 10개의 파티션을 생성하면 될 것이다. 그런데 문제는, 프로듀서가 보내는 데이터량이 언제든 더 많아질 수도 있을 것이다. 따라서 파티션 개수를 10개보다도 더 많이 미리 널널하게 늘려 놓고 운영하면 좋을 것이다.&lt;/p&gt;
&lt;h3 id=&quot;메시지-키가-없을-경우-동작&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%94%EC%8B%9C%EC%A7%80-%ED%82%A4%EA%B0%80-%EC%97%86%EC%9D%84-%EA%B2%BD%EC%9A%B0-%EB%8F%99%EC%9E%91&quot; aria-label=&quot;메시지 키가 없을 경우 동작 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메시지 키가 없을 경우 동작&lt;/h3&gt;
&lt;p&gt;반대로 메시지 키가 없을 경우는 앞서 살펴본 두 파티셔너의 동작 방식이 비슷하면서도 조금 다르다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;공통점 : 파티션에 라운드 로빈(Round Robin) 방식으로 데이터를 최대한 골구로 동일하게 분배시킨다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;차이점 : 파티셔너가 레코드를 전송하는 묶음 단위가 다르다. RoundRobinPartitioner 는 레코드를 단일(1개) 단위로 전송하며, UniformStickyPartitioner 는 레코드를 배치 단위로 묶어서 전송한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;RoundRobinPartitioner&lt;/code&gt; : 단일(1개) 레코드(ProducerRecord) 가 유입되는대로 그 즉시 각 파티션에 라운드 로빈 방식으로 레코드를 전송한다. Accumulator 에서 묶이는 데이터 단위 정도가 적기 때문에 처리량(성능)이 낮다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;UniformStickyPartitioner&lt;/code&gt; : Accumulator 에서 레코드 여러개가 배치로 묶일 때 까지 대기했다가 라운드 로빈 방식으로 레코드를 전송한다. 배치 단위로 데이터를 전송하기 때문에 비교적 처리량이 뛰어나다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;UniformStickyPartitioner&lt;/code&gt; 는 그 특성상 &lt;code class=&quot;language-text&quot;&gt;RoundRobinPartitioner&lt;/code&gt; 에 비해 성능이 뛰어나다. 이 덕분에 &lt;code class=&quot;language-text&quot;&gt;UniformStickyPartitioner&lt;/code&gt; 는 현재 디폴트 파티셔너로 지정되어 있다.&lt;/p&gt;
&lt;h2 id=&quot;커스텀-파티셔너&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%8C%8C%ED%8B%B0%EC%85%94%EB%84%88&quot; aria-label=&quot;커스텀 파티셔너 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커스텀 파티셔너&lt;/h2&gt;
&lt;p&gt;앞선 파티셔너 외의 파티셔너를 직접 구현하고 싶다면 &lt;code class=&quot;language-text&quot;&gt;Partitioner&lt;/code&gt; 인터페이스를 구현하면 된다. Partitioner 인터페이스 구현체 내부적으로 메시지 키 또는 메시지 값에 따라 매핑되는 파티션 로직을 구현해볼 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;프로듀서-api-코드-구현하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A1%9C%EB%93%80%EC%84%9C-api-%EC%BD%94%EB%93%9C-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0&quot; aria-label=&quot;프로듀서 api 코드 구현하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프로듀서 API 코드 구현하기&lt;/h2&gt;
&lt;p&gt;카프카 프로듀서 API 코드는 어떻게 구현하는지 기본적인 스펙만 간단하게 알아보자.&lt;/p&gt;
&lt;h3 id=&quot;카프카-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B9%B4%ED%94%84%EC%B9%B4-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;카프카 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;카프카 설정&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Properties&lt;/span&gt; configs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
configs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BOOTSTRAP_SERVERS_CONFIG&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;kafka-test:9092&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
configs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;KEY_SERIALIZER_CLASS_CONFIG&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
configs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ProducerConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;VALUE_SERIALIZER_CLASS_CONFIG&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (3)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에서는 Bootstrap 서버를 지정하였으며, &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 에서는 메시지 키와 값에 대한 직렬화 옵션을 지정하였다. 여기서는 StringSerializer 으로 직렬화 옵션을 지정하였다.&lt;/p&gt;
&lt;h3 id=&quot;kafkaproducer-template&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#kafkaproducer-template&quot; aria-label=&quot;kafkaproducer template permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;KafkaProducer Template&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;KafkaProducer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; producer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KakfaProducer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;configs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;카프카 프로듀서 인스턴스를 정의해주었다. 카프카 프로듀서 인스턴스를 만들 때는, 앞서 정의한 직렬화 옵션(StringSerializer)에 알맞게 템플릿 형태(&amp;#x3C;String, String&gt;)를 지정해줘야 한다. 또한 앞서 생성한 카프카 Properties 를 파라미터로 지정해준다.&lt;/p&gt;
&lt;p&gt;위와 같이 프로듀서 인스턴스를 정의하면, &quot;kafka-test:9092&quot; 에 있는 카프카 클러스터와 연동함년서 메시지 키와 값을 String 으로 직렬화하는 프로듀서 인스턴스가 되는 것이다.&lt;/p&gt;
&lt;h3 id=&quot;producerrecord&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#producerrecord&quot; aria-label=&quot;producerrecord permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ProducerRecord&lt;/h3&gt;
&lt;h4&gt;ProducerRecord 기본 선언&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; messageValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;test-messagge-value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;ProducerRecord&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; record &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProducerRecord&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TOPIC_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; messageValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 
produdcer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;record&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞서 정의한 KafkaProducer 인스턴스의 &lt;code class=&quot;language-text&quot;&gt;send()&lt;/code&gt; 메소드를 호출하여 &lt;code class=&quot;language-text&quot;&gt;ProducerRecord&lt;/code&gt; 를 전송할 수 있다. &lt;code class=&quot;language-text&quot;&gt;ProducerRecord&lt;/code&gt; 는 토픽 이름과 메시지 값을 필수로 지정해야한다. 앞서 학습했듯이, ProducerRecord 는 Offset 이 없는 형태이며, 나중에 이 레코드가 브로커에 저장될 때, 즉 리더 파티션에 저장될 때 Offset 이 할당된다.&lt;/p&gt;
&lt;p&gt;또한, 앞서 살펴봤듯이 &lt;code class=&quot;language-text&quot;&gt;send()&lt;/code&gt; 를 호출한다고 해서 레코드가 바로 전송되지 않는다. Accumulator 가 배치로 레코드 여러개를 최대한 모은 뒤에 전송하게 된다.&lt;/p&gt;
&lt;h4&gt;파라미터 개수에 따른 ProducerRecord 선언 여러가지 방법&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ProducerRecord&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; record1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProducerRecord&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TOPIC_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;message key1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;message value1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;ProducerRecord&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; record2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProducerRecord&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TOPIC_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;message key1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;message value1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 메시지 키를 가진 ProducerRecord 선언 :  파타미터 3개를 넣으면 순서대로 토픽, 메시지 키, 메시지 값을 지정하게 되는 것이 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 파티션 번호를 지정한 ProducerRecord 선언 : 파라미터 4개를 넣으면 순서대로 토픽, 파티션 번호, 메시지 키, 메시지 값을 지정하게 되는 것이 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;custompartitioner-구현하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#custompartitioner-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0&quot; aria-label=&quot;custompartitioner 구현하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CustomPartitioner 구현하기&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CustomPartitioner&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Partitioner&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  
  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;partition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; topic&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; keyBytes&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; valueBytes&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cluster&lt;/span&gt; cluster&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keyBytes &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InvalidRecordException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Need message key&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;key1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PartitionInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; partitions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cluster&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;partitionsForTopic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;topic&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; numPartitions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; partitions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Utils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toPostitive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Utils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;murmur2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keyByte&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; numPartitions&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위처럼 &lt;code class=&quot;language-text&quot;&gt;Partition&lt;/code&gt; 인터페이스를 구현한 커스텀 파티셔너 구현체를 정의할 수 있다. 위 파티셔너의 경우 &quot;key1&quot; 이라는 메시지 레코드 키를 전달받은 경우 3번 파티션으로 전송되도록 구현하였다. 반면 이 외의 레코드에 대해선, 다시 해시값으로 변환해서 데이터가 전송되도록 하였다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;아파치 카프카 애플리케이션 프로그래밍 - 최원영&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/&quot;&gt;https://kafka.apache.org/documentation/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Circuit Breaker 패턴을 활용한 MSA 환경내 장애 차단]]></title><description><![CDATA[서킷 브레이커 패턴의 등장 및 개념 MSA 환경에서 개발을 하다보면, 다른 서비스 컴포넌트에서 제공하는 외부 API 를 호출해야 하는 경우가 흔하다. 특히나 전체적인 시스템 구성이 MSA…]]></description><link>https://haon.site/msa/circuit-breaker/</link><guid isPermaLink="false">https://haon.site/msa/circuit-breaker/</guid><pubDate>Wed, 18 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;서킷-브레이커-패턴의-등장-및-개념&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%ED%82%B7-%EB%B8%8C%EB%A0%88%EC%9D%B4%EC%BB%A4-%ED%8C%A8%ED%84%B4%EC%9D%98-%EB%93%B1%EC%9E%A5-%EB%B0%8F-%EA%B0%9C%EB%85%90&quot; aria-label=&quot;서킷 브레이커 패턴의 등장 및 개념 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서킷 브레이커 패턴의 등장 및 개념&lt;/h2&gt;
&lt;p&gt;MSA 환경에서 개발을 하다보면, 다른 서비스 컴포넌트에서 제공하는 외부 API 를 호출해야 하는 경우가 흔하다. 특히나 전체적인 시스템 구성이 MSA 로 되어 있다면, 다른 서비스를 호출하는 경우가 매우 빈번하다. 문제는 서버들이 장애가 발생할 수 있다는 점인데, 호출한 다른 서비스에 장애가 발생했다면 장애가 전파되어, 현재 외부 API 를 호출하는 본 서비스에까지 장애가 전파될 수 있다.&lt;/p&gt;
&lt;p&gt;특히나 MSA 환경은 클라이언트의 요청이 수 많은 서비스를 거쳐서 응답이 이루어질 수 있다는 점이다. 서비스 A 로 부터 시작된 장애가 서비스 B, C, D, E, ... 로 계속 전파되어 영향을 미칠 수 있다는 점이다. 또한, 장애가 시작될 경우 장애가 어떤 서비스에서부터 시작된 것인지 그 장애 시작점을 파악하기는 쉽지 않다. 따라서, MSA 환경내 각 서비스는 스스로 다른 컴포넌트의 장애가 발생했을 경우를 대비한 장애 우회 정책, 장애 격리 및 FailOver 등을 할 수 있어야 한다.&lt;/p&gt;
&lt;p&gt;따라서 장애가 발생한 서비스를 탐지하고, 요청을 재전송하지 않고 장애를 다른 방법으로 우회하도록 차단할 필요가 있다.&lt;/p&gt;
&lt;h2 id=&quot;서킷-브레이커-패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%ED%82%B7-%EB%B8%8C%EB%A0%88%EC%9D%B4%EC%BB%A4-%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;서킷 브레이커 패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서킷 브레이커 패턴&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 349px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d420adb4616277287c915a41164daa86/e9bf8/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 91.41104294478527%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAAAsTAAALEwEAmpwYAAACdElEQVR42p2UfVPaQBDG/f4fpH/Uaatt7TjVopZiRSmovCmGQMIll/CSBEKACuHp7kksUMGZ3sxNbnKX53af/W128B/D8zw4jgPXddWUUsK2bcRxjB0+8Pj4iPF4jPl8vlUo2R8Oh0pACIHJZLKypwRFq4VyqYhwMFAv49lMHXhp8gh8H6Mogk+RRtFw5VIlaEsHPT9A0O+v3LYpQk55MAgRBAFGo9GqoN9xUL44hvNQULPXuIVZK1JKUvnE/rBPtm2h2+3Bkwa0/HeYpSxkLQ9RuYTniuRK7Mh6CWHxEN38AazsHqLSIZq5Y5gtQYLy2XBJs94wUEwfYFI9gpv7jM6vA4zKh7BJeJ4Iuo0qft+nILIfUTp6o9btSgYBpTSbTjGl+eRfrD5y764w105RO9uFcb4H6GdwtRtMY+UJRWhouE/vw75Owb45gXObIgtSaBqm8siyLEVAMsy7ArTzTxCFb+qbVu4rnMbd35RnVNFKpQph2bDItyFVb1mARRmTRVUwI9YazSYM06RiStRqD8oS9pvPqir7hMGAkBkssOEUGdJ1waTKfG5M1VV7YYjpgmPmWQmy8bquo0/Y8Ee88czcC4L8LqJM+LmcjcKGb0uob7fbap200SZBvpj548wSDlc6ZRvEmyJkIRb+J8JNYi8JPvVxBJ14DKMRFcdA1/NfF1xvsx51CPf3dBZD3F/DIbw61Qzs2xNYeYK8uYTNJqFkcGqMhEUetywJUcxAXnxAPf0O+o/3CG6+oKMXEa9HuO3XxQXyfQ/9MIJTzcLK7EL83IN2+hbd3L7qlHhThK95ampV1Atp9XNolS+h0Vqa+jP4fwBJA14PwWOg8gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d420adb4616277287c915a41164daa86/e9bf8/image.png&quot;
        srcset=&quot;/static/d420adb4616277287c915a41164daa86/222b7/image.png 163w,
/static/d420adb4616277287c915a41164daa86/ff46a/image.png 325w,
/static/d420adb4616277287c915a41164daa86/e9bf8/image.png 349w&quot;
        sizes=&quot;(max-width: 349px) 100vw, 349px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위 문제처럼, MSA 환경내 각 서비스간에 장애 전파를 차단하기 위해 등장한 디자인패턴이 빠로 서킷 브레이커 패턴(Circuit Breaker Pattern) 이다. 서킷 브레이커 패턴은 이름과 같이 과부하가 걸린 전기를 자동으로 차단해서, 전기 사고를 방지하는 회로 차단기와 비슷하게 동작한다.&lt;/p&gt;
&lt;p&gt;서킷 브레이커는 다른 서비스에 대한 호출을 모니터링하며, 요청의 실패율이 일정 임계치(Threshold) 를 넘어가면, 장애가 발생한 서비스로의 요청을 차단하여 장애 전파를 막는 방식이다. 만약 서킷 브레이커가 열렸다면, 서비스에 요청하는 대신에 Fallback 을 하게된다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/00b252462340d5055b406832d22b8680/49217/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 31.901840490797547%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA5ElEQVR42k2QR46AQAwE+f8DERwQsOScc/CqvBq0hxYzHrvcjXUch1zX9ek8T6mqSrZtk/d95b5v2fdd2rbVN9PHXF3XMo6j9pialWWZLMsi67qq5nmWNE0lz3OJ41j6vtd33/clSZJPwMIwlCAIpCgKnem6TqymadQNLhBQHJZlqVDOuAPIIHW+zFGLokjP1FhuYfl5nk9YNyCzgIU4MgBmqOPU8zx1iTvSWZBpJhaapukvih9oI2cWGBgQ4rEQmG3bKsdxhN9nEQH6fyADAAASZxiGD4w77sSLfiJxXVf7WED9F/6VxIH3aJZNAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/00b252462340d5055b406832d22b8680/a6d36/image-1.png&quot;
        srcset=&quot;/static/00b252462340d5055b406832d22b8680/222b7/image-1.png 163w,
/static/00b252462340d5055b406832d22b8680/ff46a/image-1.png 325w,
/static/00b252462340d5055b406832d22b8680/a6d36/image-1.png 650w,
/static/00b252462340d5055b406832d22b8680/49217/image-1.png 701w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;서킷 브레이커는 위와 같이 크게 3가지 타입의 &lt;code class=&quot;language-text&quot;&gt;상태값(State Value)&lt;/code&gt; 를 가진다. &lt;code class=&quot;language-text&quot;&gt;Closed&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Open&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Half Open&lt;/code&gt; 이 3가지 타입이 존재하는데, 각 상태를 정리하면 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Closed&lt;/code&gt; : 요청 실패율이 정해놓은 임계치보다 낮은 상태로, 평소대로 모든 요청이 처리된다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Open&lt;/code&gt; : 요청 실패율이 정해놓은 임계치보다 높아진 상태로, 서킷 브레이커가 열린 경우, 요청을 보내지 않고 즉시 실패 처리한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Half Open&lt;/code&gt; : Open 이후 일정 시간이 지나면 Half Open 된다. 이 상태에서 요청이 성공하면 Closed 상태로 변경되고, 실패하면 Open 상태를 유지한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;서킷 브레이커가 동작하는 상황 예시를 들자면 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 일반적으로 외부 서버는 정상 실행중이므로, 서킷에 닫혀있고 요청이 정상적으로 전달된다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 이때, 외부 서버에 장애가 발생했다고 해보자.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 이후의 요청들은 장애가 발생한 외부 서버가 더 이상 전달되지 않고 차단되며, 빠르게 에러 실패 또는 에러를 응답을 반환한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 이후의 요청들은 외부 서버가 정상적으로 복구된다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; 이후의 외부 서버가 정상적으로 북구된다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(6)&lt;/code&gt; 회로가 Open 상태가 된지 특정 시간이 지나고, Half Open 상태로 변경된다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(7)&lt;/code&gt; 일부 요청들이 외부 서버로 전달되고, 응답에 성공하여 Closed 상태가 된다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(8)&lt;/code&gt; 모든 요청들이 정상적으로 복구된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Open 상태가 되면 특정 시간이 지나야 Half Open 상태가 된다. 만약 Half Open 상태가 되었는데 외부 서버가 복구되지 않았다면, 요청들은 실패해서 다시 Open 상태로 변경될 수도 있다. 여기서 중요한 것은 이러한 상태 변경이 자동으로 수행된다는 것이며, 상태 전이를 위한 시간들은 시스템 내부에서 관리되므로, 대부분 타임아웃과 관련된 모니터링 시스템을 제공해준다.&lt;/p&gt;
&lt;h2 id=&quot;서킷-브레이커-패턴의-장점과-필요성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%ED%82%B7-%EB%B8%8C%EB%A0%88%EC%9D%B4%EC%BB%A4-%ED%8C%A8%ED%84%B4%EC%9D%98-%EC%9E%A5%EC%A0%90%EA%B3%BC-%ED%95%84%EC%9A%94%EC%84%B1&quot; aria-label=&quot;서킷 브레이커 패턴의 장점과 필요성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서킷 브레이커 패턴의 장점과 필요성&lt;/h2&gt;
&lt;p&gt;서킷 브레이커 패턴을 도입하였을 때 얻는 장점은 다음과 같이 정리해 볼 수 있다.&lt;/p&gt;
&lt;h4&gt;장애 감지 및 격리&lt;/h4&gt;
&lt;p&gt;만지 장애가 발생한 서비스를 호출한다면 요청이 타임아웃만큼 대기하게 되고, 쓰레드와 메모리 및 CPU 등의 자원을 점유하게 된다. 이것은 결국 시스템 리소스를 부족하게 만들어 장애를 유발할 수 있다. 장애가 발생한 것은 다른 서비스인데, 장애가 전파되는 것이다. 서킷 브레이커 패턴은 장애가 발생한 서비스를 탐지하고, 더 이상 요청을 보내지 않도록 차단함으로써 장애를 격리시켜준다. 그래서 장애가 발생한 기능 외의 다른 기능들은 동작하게 하여, 시스템의 안정성을 높일 수 있다.&lt;/p&gt;
&lt;h4&gt;자동 시스템 복구&lt;/h4&gt;
&lt;p&gt;서킷 브레이커는 요청이 차단되면, 해당 서비스가 주기적으로 검사한다. 그리고 해당 서비스가 복구되었다면 차단이 해제되고, 정상적으로 요청을 보내게 된다. 이러한 부분들은 시스템이 자동으로 해주므로, 개발자들이 신경쓰지 않아도 된다. 대부분 타임아웃 등을 위한 모니터링 기능까지 제공하여, 서킷 브레이커가 모든 연동이 적용되면 데시보드를 통해 전체 시스템들의 연동 현황까지 모니터링 가능하다.&lt;/p&gt;
&lt;h4&gt;빠른 실패 및 고객 응답&lt;/h4&gt;
&lt;p&gt;만약 다른 서비스가 문제있음을 알 수 있다면, 타임아웃 동안 자원을 낭비할 필요가 없다. 그래서 빠르게 장애를 탐지하는 것이 중요한데, 서킷브레이커 패턴을 적용하면 가능한 빠르게 실패를 반환하고, 고객에게 응답을 전달할 수 있다.&lt;/p&gt;
&lt;h4&gt;장애 서비스로의 부하 감소&lt;/h4&gt;
&lt;p&gt;외부 서비스가 완전히 죽지는 않았는데, Slow Query 등의 이유로 사용 가능한 쓰레드가 더 이상 남아있지 않을 수도 있다. 이때, 계속 요청을 보내는 것은 외부 서비스의 상황을 악화시켜서 장애를 유발할 수 있다. 그러므로 해당 서비스가 안정을 찾도록 멈추는 것이 좋은데, 서킷 브레이커를 사용하면 해당 서비스는 더 이상의 요청이 유입되지 않아서 장애를 복구할 수 있는 기회를 얻을 수 있다.&lt;/p&gt;
&lt;h4&gt;장애 대안 커스터마이징&lt;/h4&gt;
&lt;p&gt;외부 서비스에서 장애가 발생했다면, 원하는 데이터를 얻지 못할 수 있다. 이때, 아무런 대응책이 없다면 해당 서비스 역시 장애가 발생한다. 서킷 브레이커를 적용하면, 장애 대안을 커스터마이징 할 수 있는데, 예를들어 다른 소스로부터 값을 얻어오거나, 서킷 브레이커가 캐싱해 둔 값으로 응답하는 등 다양한 방법을 적용할 수 있다. 그러면 외부에 장애가 발생해도 문제없이 서비스를 운영할 수도 있다.&lt;/p&gt;
&lt;p&gt;국내 대부분의 서비스들은 MVC 기반으로 되어있다. 스프링 MVC 는 멀티 쓰레드 기반으로 동작하므로, 장애가 있는 서비스를 호출하면 쓰레드를 점유에 의한 응답 지연이 발생하기 쉽다. 그래서 장애가 전파되기 쉬운데, 서킷 브레이커를 적용하면 빠르게 장애가 발생한 서버로의 요청을 차단하고 이를 해결할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;resilience4j-를-사용한-서킷-브레이커-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#resilience4j-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%84%9C%ED%82%B7-%EB%B8%8C%EB%A0%88%EC%9D%B4%EC%BB%A4-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;resilience4j 를 사용한 서킷 브레이커 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Resilience4j 를 사용한 서킷 브레이커 구현&lt;/h2&gt;
&lt;p&gt;서킷 브레이커 패턴을 개발자가 직접 구현하는 방식보단, 기존에 잘 만들어진 라이브러리를 사용하는 방법이 더 좋다. Resilience4j 는 함수형 프로그래밍으로 설계된 경량(light weight) 장애 허용(fault tolerance) 라이브러리로, 서킷브레이커 패턴을 위해 사용된다. Resilience4j 를 활용한 간단한 서킷 브레이커 구현을 실습해보도록 한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 서킷 브레이커 실습 코드는, &lt;a href=&quot;https://hudi.blog/circuit-breaker-pattern/&quot;&gt;참고1&lt;/a&gt; 을 보았을 때 아래 처럼 간단히 구현 가능한 방법도 있는 것으로 보이나, 외부 API 호출시 FeignClient 에 대한 서킷 브레이커 설정을 통해서도 외부 API 호출시 장애를 차단하는 방법이 있는 것으로 보인다. 이에 대한 실습 코드는 향후 잘 이해가 되었을 떄, 추가로 실습해보도록 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;gradle&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#gradle&quot; aria-label=&quot;gradle permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;gradle&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;dependencies &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;implementation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.springframework.boot:spring-boot-starter&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;implementation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.springframework.boot:spring-boot-starter-web&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;implementation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.springframework.boot:spring-boot-starter-aop&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;implementation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;io.github.resilience4j:resilience4j-spring-boot2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;의존성은 위와 같다. &lt;code class=&quot;language-text&quot;&gt;spring-boot-starter-app&lt;/code&gt; 의존성이 없으면, 뒤이어 사용할 &lt;code class=&quot;language-text&quot;&gt;@CircuitBreaker&lt;/code&gt; 어노테이션을 사용할 수 있으니 추가하도록 한다.&lt;/p&gt;
&lt;h3 id=&quot;applicationyml&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#applicationyml&quot; aria-label=&quot;applicationyml permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;application.yml&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;resilience4j&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;circuitbreaker&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
	configs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
			registerHealthIndicator&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
			slidingWindowSize&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
			minimumNumberOfCalls&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
			permittedNumberOfCallsInHalfOpenState&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
			automaticTransitionFromOpenToHalfOpenEnabled&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
			waitDurationInOpenState&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;s
			failureRateThreshold&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;
			eventConsumerBufferSize&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
	instances&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
		orderService&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
			baseConfig&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;resilience4j-spring-boot2 의존성을 사용하므로, 스프링부트 application.yaml 을 통해 선언적으로 서킷 브레이커에 대한 설정을 할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;컨트롤러&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC&quot; aria-label=&quot;컨트롤러 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨트롤러&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestContoller&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; val orderService&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	
	&lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/orders/{orderId}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	fun &lt;span class=&quot;token function&quot;&gt;getOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;token annotation punctuation&quot;&gt;@PathVariable&lt;/span&gt; orderId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; orderService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;orderId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;주문을 수행하는 컨트롤러다. 주문 API 요청시 고객 정보를 가져오기 위해 Service Layer 에서 UserClient API 를 호출하는 상황을 가정하도록 한다.&lt;/p&gt;
&lt;h3 id=&quot;서비스내-서킷-브레이커-적용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B9%84%EC%8A%A4%EB%82%B4-%EC%84%9C%ED%82%B7-%EB%B8%8C%EB%A0%88%EC%9D%B4%EC%BB%A4-%EC%A0%81%EC%9A%A9&quot; aria-label=&quot;서비스내 서킷 브레이커 적용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서비스내 서킷 브레이커 적용&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; val userClient&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	
	&lt;span class=&quot;token annotation punctuation&quot;&gt;@CircuitBreaker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;orderService&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fallbackMethod &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fallback&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	fun &lt;span class=&quot;token function&quot;&gt;getOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		val user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	
	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; fun &lt;span class=&quot;token function&quot;&gt;fallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Circuit Breaker is opened : $e&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;서비스 코드에 서킷 브레이커를 적용했다. &lt;code class=&quot;language-text&quot;&gt;@CircuitBreaker&lt;/code&gt; 어노테이션을 붙이면 된다. name 파라미터는 서킷 브레이커 설정시 넣어둔 이름과 동일하다.&lt;/p&gt;
&lt;p&gt;fallbackMethod 파라미터는 서킷이 열렸을 때 fallback 을 하기위해 실행할 메소드 이름을 입력한다. 단, 메소드 시그너치는 &lt;code class=&quot;language-text&quot;&gt;@CircuitBreaker&lt;/code&gt; 가 붙어있는 메소드와 메소드 시그너치가 동일해야하되, 추가로 Exception 파라미터를 받아야 한다. 다만, 위와 같이 원본 메소드의 파라미터를 생략하고 Exception 만 받아도 괜찮은 듯 하다.&lt;/p&gt;
&lt;h2 id=&quot;참고-및-인용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0-%EB%B0%8F-%EC%9D%B8%EC%9A%A9&quot; aria-label=&quot;참고 및 인용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 및 인용&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/circuit-breaker-pattern/&quot;&gt;https://hudi.blog/circuit-breaker-pattern/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/261&quot;&gt;https://mangkyu.tistory.com/261&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/290&quot;&gt;https://mangkyu.tistory.com/290&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[카프카 컴포넌트 핵심 기능 및 역할 정리]]></title><description><![CDATA[카프카 아키텍처 복습 지난 포스팅 에서 다루었던 카프카 아키텍처의 컴포넌트들에 대해 다시금 복습을 짧게 해본 뒤, 본격적으로 이번 포스팅의 주제로 넘어가보고자 한다.  카프카 클러스터(kakfa cluster…]]></description><link>https://haon.site/kafka/core-concept/</link><guid isPermaLink="false">https://haon.site/kafka/core-concept/</guid><pubDate>Sat, 29 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;카프카-아키텍처-복습&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B9%B4%ED%94%84%EC%B9%B4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EB%B3%B5%EC%8A%B5&quot; aria-label=&quot;카프카 아키텍처 복습 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;카프카 아키텍처 복습&lt;/h2&gt;
&lt;p&gt;지난 &lt;a href=&quot;https://haon.blog/kafka/overview/&quot;&gt;포스팅&lt;/a&gt; 에서 다루었던 카프카 아키텍처의 컴포넌트들에 대해 다시금 복습을 짧게 해본 뒤, 본격적으로 이번 포스팅의 주제로 넘어가보고자 한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/df3292f5b56eb8715849e83dcc20b292/1ac66/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 79.75460122699387%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAACyklEQVR42o2TW28aVxSF/d/7UEVKqjxUcZUHN63iRokcxQUn9SWNEwcMBnMdw8DM2ICZgblyGxhz/3oAO40louRIS+fMmdlr1t577Q3WrNlsRifwaN84KwQrdIYurcDGH3bvfT+fz5d7vV5n494LVi+6Y4+tzE88zz/iufSQZ6kHbF8szo94lvmZt5Ut8ddVxA8RuiODM/2AQatD6vwUKXtOIZ+iVMgS9LskGkeMx+P1ChcPX2OxWlOLqLqDH92m/u9TUjuP0fZ/ox3Zopd+w2l1n8l08uMpe1Ob0/ILutIJnmtSFOr0ispsNKBV+Ej0MiQIp+sJLdvG81rYtkO31/2fsPQXfTmO5bpcSHlUVcX3+3TKUaLa7heFvu8v4weDAcVikY1Y7IwF4okUiiThpPMo11kR9JpGZJdaco9KLET1LEwtHqKZOeCzsouRz2Hm85yfJYjHk6TTOcJ779hQFAVZLlEqq1Q1lZt6k4ZdWhKO4i8wPz1F+vsx1cNNBvHfCTKviWghfLNJYJrIQpUkFVDKGsfHH7+q4cJ7tSpB26c1d0XK2/hyAtuySMaiqHKBtufSLXwQDXvDVNStL9LtiJLMb+t5rynT4ZBWLrtMuWbLxLRXWLkT1HKOROSIbPKEinoh7g6JVcN02i2sbJaeUDgW9btPeNulUa/HQL2iomV5Wf2Ft/oT9pubhK5+XeIfY5Owsdj/wKvWaQsBo0771hvrbDMX9hfq3cDgoLRDw/PIawWSFxnOBZT6JddOk09Xewz9HkOR7t2ofptwYZtRg7DyJ0VdJl6KcJx+z4fUIQkljqTnObp6xWQyuQv6trG/1HM2Qe+r1HoyRqDRHF7SEDAC4YReEfumvhrj+XdmeUU2Rdd1rKazxHWljl4zcC0P47qBLe5MwxRG7n9/9O4G0HFsbMfCFTYxLXN5bomuLnbHdcRUWQRBsJbwP09KntirYVgNAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/df3292f5b56eb8715849e83dcc20b292/a6d36/image.png&quot;
        srcset=&quot;/static/df3292f5b56eb8715849e83dcc20b292/222b7/image.png 163w,
/static/df3292f5b56eb8715849e83dcc20b292/ff46a/image.png 325w,
/static/df3292f5b56eb8715849e83dcc20b292/a6d36/image.png 650w,
/static/df3292f5b56eb8715849e83dcc20b292/1ac66/image.png 651w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;카프카 클러스터(kakfa cluster) : 하나 이상의 카프카 브로커들의 집합이다. 카프카는 확장성과 내결함성을 위해 브로커들을 클러스터로 구성한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;브로커(broker)&lt;/strong&gt; : 브로커는 개별 카프카 서버이다. 브로커는 프로듀서로부터 메시지를 전달받아서 토픽에 저장하고, 컨슈머에게 전달하는 역할을 한다. 브로커는 여러개의 토픽을 가질 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;토픽(topic)&lt;/strong&gt; : 토픽은 데이터가 저장되는 단위라고 할 수 있다. 마치 데이터베이스에서 테이블(table) 과 거의 유사한 개념이다. 토픽은 이름으로 식별되며, 토픽에 한 번 추가된 데이터는 수정될 수 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;파티션(partition)&lt;/strong&gt; : 카프카의 확장성을 위해서 토픽은 1개 이상의 여러 파티션으로 나뉠 수 있다. 레코드에 설정된 키(key) 가 없다면 라운드 로빈(round robin) 방식으로 파티션에 나뉘어 저장되고, 같은 키(key) 를 가진 레코드는 같은 파티션에 저장된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;반대로, 레코드에 설정된 키(key) 값이 있다면, 해당 키 값을 해싱(hashing) 한 결과값을 기반으로 저장된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;파티션의 내부 구조는 Queue 구조와 유사하게 이루어져 있다. 즉, FIFO 의 특징을 갖는다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;컨슈머는 파티션에 적재된 데이터를 가져가저라도, 파티션에 적재된 레코드가 삭제되지 않는다. 즉, 컨슈머는 파티션에서 이미 가져온 동일한 데이터를 또 다시 가져오는 행위가 가능하다. (몰론, 어떤 특정 컨슈머가 어떤 데이터까지 읽었는지는 내부적으로 기록하고 있다. 이렇게 어떤 레코드까지 읽었는지 기록하는 행위를 &apos;커밋&apos; 이라고 한다. 컨슈머는 레코드는 읽어온 뒤 커밋을 하고, 앞서 커밋한 내용을 기반으로 어디서부터 새로운 레코드를 읽어오면 되는지 정보를 읽어온다.)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;오프셋(offset)&lt;/strong&gt; : 파티션에 저장된 레코드는 증가하는 정수 ID 값을 갖고, 이 정수 ID 값을 오프셋이라고 부른다. 오프셋은 0부터 시작하며, 레코드가 파티션에 저장될 떄 마다 순차적으로 값이 증가한다. 특정 파티션의 각 레코드는 고유한 오프셋을 갖지만, 서로 다른 파티션 사이에는 고유하지 않다. 파티션에서 데이터를 읽을 때 작은 것 부터 큰 순서대로 읽는다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;레코드(record)&lt;/strong&gt; : 파티션에 저장되는 데이터이다. Key, Value, TimeStamp, Compression Type, Optional Headers, Partition and Offset Id 로 구성된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;프로듀서(producer)&lt;/strong&gt; : 카프카에 요청을 날려서, 토픽에다 레코드를 추가하는 카프카 클라이언트이다. 카프카의 구성요소가 아니며, 카프카 외부에서 카프카에게 Request 를 전송하는 애플리케이션이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;컨슈머(consumer)&lt;/strong&gt; : 하나 이상의 파티션과 토픽으로부터 데이터를 읽어오는 카프카 클라이언트이다. 기본적으로 사용 가능한 가장 낮은 오프셋부터 높은 오프셋까지 순서대로 레코드를 읽어온다. 하나의 토픽 내부의 여러 파티션으로부터 레코드를 읽어올 때는 순서가 보장되지 않는다. 파티션 0, 1, 2 로 부터 레코드를 읽어올 때 파티션 0 의 레코드만 바라봤을 때는 순서가 보장되지만, 읽어온 전체 레코드를 바라봤을 때는 파티션 0 ~ 2 의 레코드가 순서와 상관없이 섞여있을 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;컨슈머 그룹(consumer group)&lt;/strong&gt; : 동일한 컨슈머 인스턴스를 여러개 생성하여 컨슈머 그룹을 생성할 수 있다. 컨슈머 그룹을 구성하는 여러 컨슈머는 동일한 토픽의 각자 다른 파티션을 도맡아 메시지를 컨슘할 수 있다. 예를들어 토픽 A 에 파티션이 0, 1, 2 가 생성되어 있고, 컨슈머 그룹 A 에 컨슈머 a, b, c 가 있다고 가정하자. 이 경우 컨슈머 a 는 파티션 0 을, 컨슈머 b 는 파티션 1 을, 컨슈머 c 는 파티션 2 를 컨슘한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;브로커broker&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%8C%EB%A1%9C%EC%BB%A4broker&quot; aria-label=&quot;브로커broker permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;브로커(Broker)&lt;/h2&gt;
&lt;h3 id=&quot;1-브로커란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EB%B8%8C%EB%A1%9C%EC%BB%A4%EB%9E%80&quot; aria-label=&quot;1 브로커란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 브로커란?&lt;/h3&gt;
&lt;p&gt;브로커는 개별 카프카 서버라고 설명했다. 브로커는 프로듀서로부터 메시지를 전달받아서 토픽에 저장하고, 컨슈머에게 전달하는 역할을 한다. 또한 브로커는 여러개의 토픽을 가질 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;브로커를 다시 표현하자면, 카프카 클라이언트(프로듀서, 컨슈머) 와 데이터를 주고받는 주체이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;데이터를 분산 저장하여 장애가 터지더라도 안건하게 사용할 수 있도록 도와주는 개별 카프카 서버이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하나의 서버에는 한 개의 카프카 브로커 프로세스가 실행된다. 단, 카프카 브로커 서버 1대만으로도 기본 기능이 모두 제공되긴 하지만, 데이터를 안전하게 보관하고 처리하기 위한 가용성(availability) 를 보장하고자 브로커 서버를 여러개로 Scale Out 한다. 그리고 여러대의 브로커 서버를 하나의 카프카 클러스터 단위로 묶어서 운영한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;카프카 클러스터로 묶인 브로커들은 프로듀서가 보낸 데이터를 안전하게 분산 저장하고, 복제(replication) 도 하며 고가용성(HA) 를 보장한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2-브로커와-주키퍼zookper&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EB%B8%8C%EB%A1%9C%EC%BB%A4%EC%99%80-%EC%A3%BC%ED%82%A4%ED%8D%BCzookper&quot; aria-label=&quot;2 브로커와 주키퍼zookper permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 브로커와 주키퍼(zookper)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 615px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e0cf800e4751b07e25840d92dede27af/f6b72/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.717791411042946%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABFElEQVR42o2RwYqCUBSGe/+ViJmrIhUJrU1ihK5F1MAH0IVtMjeWipGC/sM94DBTNsyBHxS93z3fOTNM1DAMKMsSt9uNcr1ekWXZ9ztL3/dTRzF7BbFqmga6rmO5XEJRFEiSBFEUsV6vIcsyVqsViqL4dWYSOFbXdYiiCEEQ4HQ6wbIs7Pd7hGFI8X2fLv0X8Pl8khJTrusal8sFcRwjSRJ6rqqKcr/f6d+PyuNMXNcFx3GkaBgGqc/ncywWIna7HTabDQRBAM/zsG37rcs3oOd5UFWVYEyThSkfj0ccDgeCsm+apsFxnM/An8pMlym1bUtdsK622y1M06RxjMqPx+PvLU9tPM9zpGmK8/lMM3xdwivwC/V6TH6v7BNNAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e0cf800e4751b07e25840d92dede27af/f6b72/image-2.png&quot;
        srcset=&quot;/static/e0cf800e4751b07e25840d92dede27af/222b7/image-2.png 163w,
/static/e0cf800e4751b07e25840d92dede27af/ff46a/image-2.png 325w,
/static/e0cf800e4751b07e25840d92dede27af/f6b72/image-2.png 615w&quot;
        sizes=&quot;(max-width: 615px) 100vw, 615px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;여러대의 브로커 서버들을 하나의 카프카 클러스터 단위로 묶어서 서비스를 운영한다고 했다. 그런데 이떄, 카프카 클러스터를 실행하기 위해서는 &lt;strong&gt;주키퍼(zookper)&lt;/strong&gt; 라는 컴포넌트가 카프카 클러스터에 필수로 연동되어 있어야 한다. 즉, 카프카 클러스터를 실행하기 위해선 주키퍼가 필요하다. (참고로, 카프카 Ver 3.0 부터는 주키퍼가 없어도 클러스러가 동작 가능해졌다.)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;주키퍼 안에 znode 라는 단위를 생성하고, 각 znode 에다 카프카 클러스터를 매핑(연동) 해주는 과정이 필요하다. 즉, 주키퍼의 서로 다른 znode 각각에다 1:1 로 카프카 클러스터를 지정하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;3-브로커의-세부-역할기능&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%EB%B8%8C%EB%A1%9C%EC%BB%A4%EC%9D%98-%EC%84%B8%EB%B6%80-%EC%97%AD%ED%95%A0%EA%B8%B0%EB%8A%A5&quot; aria-label=&quot;3 브로커의 세부 역할기능 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 브로커의 세부 역할(기능)&lt;/h3&gt;
&lt;h4&gt;3-1. 컨트롤러(Controller) 의 FailOver 기능&lt;/h4&gt;
&lt;p&gt;카프카 클러스터의 여러 브로커 중에 한 대가 &lt;strong&gt;컨트롤러(Controller)&lt;/strong&gt; 라는 역할을 수행한다. 컨트롤러는 다른 브로커들의 상태를 주기적으로 체크(Health Check) 하며, 문제가 발생한 브로커를 클러스터에서 빠르게 내쫓는다. (카프카는 지속적으로 데이터를 처리해야 하므로, 브로커의 상태가 비정상적이라면 빠르게 클러스터에 내쫓는다.)&lt;/p&gt;
&lt;p&gt;문제가 된 브로커를 내쫓는 즉시, 내쫓긴 해당 브로커에 존재했던 &lt;strong&gt;리더 파티션(leader partition)을 클러스터내 다른 브로커들에게 재분배&lt;/strong&gt;한다. 반대로 만약 컨트롤러 역할을 하는 브로커 본인에게 장애가 생겼다면, 본인은 내쫓기고 다른 브로커가 컨트롤러 역할을 대체한다.&lt;/p&gt;
&lt;h4&gt;3-2. 비휘발성 특징을 갖는 레코드를 삭제 가능한 주체&lt;/h4&gt;
&lt;p&gt;카프카는 다른 메시징 시스템과 다르게 컨슈머가 래코드를 가져가더라도 토픽 안의 래코드는 삭제되지 않는다. 즉, &lt;strong&gt;토픽내 파티션에 쌓인 래코드는 비휘발성 특징을 갖는다.&lt;/strong&gt; 또한, 컨슈머나 프로듀서가 이러한 레코드에 대해 삭제를 요청할 수도 없다. 만약 비휘발성 특징을 갖는 레코드를 제거해버리고 싶더라도, &lt;strong&gt;오직 브로커만이 레코드를 삭제할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;카프카에서 데이터 삭제는 파일 단위로 수행되는데, 이 하나의 파일 단위를 &lt;strong&gt;로그 세그먼트(log segment)&lt;/strong&gt; 라고 부른다. 이 세그먼트에는 다량의 데이터가 담겨있기 때문에, 일반적인 DB 처럼 특정 데이터를 선별해서 삭제할 수 없다.&lt;/p&gt;
&lt;p&gt;다시 정리하자면, &lt;strong&gt;카프카에서 데이터는 세그먼트 단위로 삭제가 발생하기 때문에 로그 단위(레코드 단위) 로 개별 삭제는 불가능하다.&lt;/strong&gt; 또한, 로그(레코드) 의 메시지 키, 메시지 값, 오프셋, 헤더 등 이미 적재된 데이터에 대해서 수정 또한 불가능하기 떄문에 데이터를 적재할 때(프로듀서) 또는 데이터를 사용할 때(컨슈머) 데이터를 검증하는 것이 좋다.&lt;/p&gt;
&lt;h4&gt;3-3. 컨슈머를 위한 오프셋 커밋&lt;/h4&gt;
&lt;p&gt;컨슈머 그룹이 특정 토픽의 특정 파티션으로부터 레코드를 가져가서 처리한 뒤에, &lt;strong&gt;해당 파티션의 어느 레코드까지 읽고 처리했는지 기록하고자 오프셋을 커밋(commit)&lt;/strong&gt; 한다. 이렇게 오프셋을 커밋하는 주체가 바로 브로커이다.&lt;/p&gt;
&lt;p&gt;커밋한 오프셋은 &lt;code class=&quot;language-text&quot;&gt;__consumer_offsets&lt;/code&gt; 이라는 토픽에 저장한다. 여기서 저장한 오프셋을 기반으로 컨슈머 그룹은 그 다음에 처리할 레코드 위치에 접근하여 레코드를 읽고 처리한다.&lt;/p&gt;
&lt;h4&gt;3-4. 코디네이터 역할 - 파티션 리밸런싱(rebalancing)&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;코디네이터(coordinator)&lt;/strong&gt; 는 컨슈머 그룹내 각 컨슈머의 상태를 **체크(Health Check)**하고, 문제가 된 컨슈머를 내쫓은 뒤, 파티션을 다른 정상적인 컨슈머와 적절히 매핑되도록 재분배하는 역할을 한다. 이 코디네이터 역할을 브로커가 담당한다.&lt;/p&gt;
&lt;p&gt;어떤 컨슈머가 문제가 터져서 컨슈머 그룹에서 내쫓기면, 매칭되지 않는 파티션을 정상 동작하는 다른 컨슈머로 재할당하여 흐름이 끊기지 않고 레코드가 처리되도록 돕는다. 이렇게 파티션을 컨슈머로 재할당하는 과정을 &lt;strong&gt;&apos;리밸런싱(rebalancing)&apos;&lt;/strong&gt;  이라고 부른다.&lt;/p&gt;
&lt;h3 id=&quot;4-브로커의-데이터-저장-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-%EB%B8%8C%EB%A1%9C%EC%BB%A4%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%80%EC%9E%A5-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;4 브로커의 데이터 저장 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 브로커의 데이터 저장 방식&lt;/h3&gt;
&lt;h4&gt;4-1. 데이터 저장 디렉토리&lt;/h4&gt;
&lt;p&gt;카프카를 실행할 떄 &lt;code class=&quot;language-text&quot;&gt;config/server.properties&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;log.dir&lt;/code&gt; 옵션에 정의한 디렉토리에 데이터를 저장한다. 토픽 이름과 파티션 번호의 조합으로 하위 디렉토리를 생성하며, 그 디렉토리 안에 데이터를 저장한다. 예를들면, 아래처럼 토픽 이름(hello.kafka) + 파티션 번호(0번) 조합으로 디렉토리가 생성되었으며, 해당 디렉토리 안에는 내부적으로 데이터가 저장되어 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ ls /tmp/kafka-logs
hello.kafka-0 &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;4-2. 각 디렉토리별 저장되는 파일 데이터 종류(로그와 세그먼트)&lt;/h4&gt;
&lt;p&gt;또한 아래 예시를 보면, hello.kafka 이라는 토픽의 0번 파티션에 존재하는 데이터들을 상세히 조회할 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;log&lt;/code&gt; 파일 : 메시지와 메타데이터를 저장한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;index&lt;/code&gt; 파일 : 메시지의 오프셋을 인덱싱한 정보를 담은 파일이다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;timeindex&lt;/code&gt; 파일 : 메시지에 포함된 timestamp 값을 기준으로 인덱싱한 정보가 담겨 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ ls /tmp/kafka-logs/hello.kafka-0
00000000000000000000.index 00000000000000000000.log
00000000000000000000.timeindex leader-epoch-checkpoint

00000000000000000010.index 00000000000000000010.log
00000000000000000010.timeindex leader-epoch-checkpoint

00000000000000000020.index 00000000000000000020.log
00000000000000000020.timeindex leader-epoch-checkpoint&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2fe8b9eee86918235527cbae34fd9a91/4d08a/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 19.631901840490798%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAyklEQVR42l2PuQqFQAxF5/8/QCxEcN8QsdLG3UK00UKstLQSRVAs7yMpX3E4mTuTDBHjOCLPc1RVhbIs0TQN12SCMqKua87Jbdty9n+e5xkiSRJYloU4jhEEAWRZZkuSxHYcB2EYwjRNKIoCwzCgqipc1+Wcauonp2kKkWUZfN9n6DERRRE0TWNTIzUQNNzzPDZ9YNs2dF3nO3JRFBDP8+A4DgzDwMGyLDjPE/d9M9u28Up932Pfd1zXhfd9MU0Tr9t1HbOuK77vww/fdf+1NTEQ1gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/2fe8b9eee86918235527cbae34fd9a91/a6d36/image-3.png&quot;
        srcset=&quot;/static/2fe8b9eee86918235527cbae34fd9a91/222b7/image-3.png 163w,
/static/2fe8b9eee86918235527cbae34fd9a91/ff46a/image-3.png 325w,
/static/2fe8b9eee86918235527cbae34fd9a91/a6d36/image-3.png 650w,
/static/2fe8b9eee86918235527cbae34fd9a91/4d08a/image-3.png 709w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;5-브로커의-데이터-복제replication-기능&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-%EB%B8%8C%EB%A1%9C%EC%BB%A4%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B3%B5%EC%A0%9Creplication-%EA%B8%B0%EB%8A%A5&quot; aria-label=&quot;5 브로커의 데이터 복제replication 기능 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. 브로커의 데이터 복제(Replication) 기능&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 541px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a663cb694b5e1377ca035f2d0d49c003/9d576/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.263803680981596%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABL0lEQVR42m2RS6uCUBSF+/+/IkxooOXANK3sAYmPFEK0ImjQxF5jG7Yua3P1XtIDm+Ne+/WdbQ+/5/P54Pt0aV36f79XO1mWQdM09Pt9DIdDWJYlmmEYGAwGopmmiSRJMJvNoKoqFEWR+H6/bxo3DbfbLRaLBUajkRSwmL7jOPI9mUwwnU7FqHPgeDyG67pYr9fthrvdDsfjEVEU4Xw+IwxDeJ6Hw+GAoigkRuL5fI48z8XnfTqdJLfVkCJN13WhXS6XWK1WMoDPImkQBDIkTVPYti2EBOlsyMD1ehW73W6yKxaTlv79fhdSPpF01MqyxOVykUGtn0ISksVx3Cx+s9kIJYfRuDsOoc585nJ/vu//Edadq6pqSHi/Xi+83288Ho/Gns+naIwxjxpv1taEP++JN0MiJj3FAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/a663cb694b5e1377ca035f2d0d49c003/9d576/image-4.png&quot;
        srcset=&quot;/static/a663cb694b5e1377ca035f2d0d49c003/222b7/image-4.png 163w,
/static/a663cb694b5e1377ca035f2d0d49c003/ff46a/image-4.png 325w,
/static/a663cb694b5e1377ca035f2d0d49c003/9d576/image-4.png 541w&quot;
        sizes=&quot;(max-width: 541px) 100vw, 541px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;데이터 복제(replication) 기능은 카프카를 장애 허용 시스템(Fault Tolerant System) 을 고려해, 가용성을 확보할 떄 좋은 기능이다. 카프카의 &lt;strong&gt;데이터 복제는 파티션 단위로 수행된다.&lt;/strong&gt; 토픽을 생성할 때 파티션의 복제 개수(replication factor) 도 같이 설정되는데, 직접 옵션을 선택하지 않으면 브로커에 설정된 옵션 값을 따라간다. 복제 개수의 최솟값은 1(복제 없음) 이고, 최댓값은 브로커 개수만큼 설정하여 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;복제된 파티션은 &lt;strong&gt;리더(leader) 파티션&lt;/strong&gt; 과 &lt;strong&gt;팔로워(follower) 파티션&lt;/strong&gt; 2가지 타입으로 구분한다. 프로듀서 또는 컨슈머와 직접 통신하는 파티션을 &lt;strong&gt;리더 파티션&lt;/strong&gt;, 나머지 복제 데이터를 가지고 있는 파티션을 &lt;strong&gt;팔로워 파티션&lt;/strong&gt;라고 부른다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;팔로워 파티션&lt;/strong&gt; 들은 리더 파티션의 오프셋을 확인하여, 현재 자신이 가지고 있는 오프셋과 차이가 나는 경우 리더 파티션으로 부터 데이터를 가져와서 자신의 파티션에 저장한다. (마치, MySQL 레플리케이션 GTID 방식과 비슷한 느낌이 든다. GTID 값을 기반으로 어디까지 복제를 진행했는지 식별하는데, 카프카 레플리케이션 기능도 비슷한 것 같다.)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;복제 개수 설정 : 참고로, 데이터 종류마다 다른 복제 개수를 설정할 수 있다. 상황에 따라서는, 각 토픽마다 복제 개수를 다르게 설정하여 운영 가능하는 것도 가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;브로커에 장애가 터졌을 때 FailOver : 브로커가 다운되면, 해당 브로커에 있는 리더 파티션은 사용 불가능하기 때문에 팔로워 파티션 중 하나가 리더 파티션으로 빠르게 승격한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;토픽topic-과-파티션partition&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%86%A0%ED%94%BDtopic-%EA%B3%BC-%ED%8C%8C%ED%8B%B0%EC%85%98partition&quot; aria-label=&quot;토픽topic 과 파티션partition permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;토픽(Topic) 과 파티션(Partition)&lt;/h2&gt;
&lt;h3 id=&quot;토픽topic&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%86%A0%ED%94%BDtopic&quot; aria-label=&quot;토픽topic permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;토픽(Topic)&lt;/h3&gt;
&lt;p&gt;토픽은 데이터가 저장되는 단위로, 데이터를 타입별로 구분하기 위해 사용하는 단위이다. 앞서 설명했듯이, DB 에서 테이블과 유사한 개념이다. 또한 DB 테이블에서 각 테이블이 이름으로 식별되듯이, 각 토픽은 이름으로 식별되며, 한번 토픽에 추가된 데이터는 수정이 불가능하다고 했었다. 그리고 &lt;strong&gt;토픽은 1개 이상의 여러개의 파티션을 보유할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;파티션partition&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%ED%8B%B0%EC%85%98partition&quot; aria-label=&quot;파티션partition permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파티션(Partition)&lt;/h3&gt;
&lt;p&gt;파티션에는 프로듀서가 보낸 데이터인 &apos;레코드(Record)&apos; 가 적재된다.&lt;/p&gt;
&lt;h4&gt;특징&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;FIFO &amp;#x26; 삭제되지 않는 레코드&lt;/strong&gt; : 파티션은 내부 구조가 Queue 와 유사하게 FIFO 특징인 자료구조로 구성되어 있다. 단, 일반적인 자료구조 Queue 와 다르게 카프카의 파티션은 컨슈머가 레코드를 가져간다고 해서 삭제되지 않고, 계속 남아 있는다. 즉, 파티션에 저장된 레코드는 컨슈머가 가져가는 것과 별개로 저장되고, 라이프사이클이 관리된다. 이런 특징 때문에 토픽 내의 레코드는 다양한 목적을 가진 여러 컨슈머 그룹들이 토픽의 데이터를 여러번 가져갈 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;앞서 설명했듯이, 파티션에 쌓인 레코드는 비휘발성 특징을 갖는다고 했었다. 오직 브로커만이 파티션내 레코드를 제거할 수 있다. 또한 브로커를 통해 레코드를 삭제할 때도, 삭제 단위가 세그먼트 단위로 발생하므로 개별 레코드 단위로 제거하는 것 또한 불가능하다고 했었다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;또한, 로그(레코드) 의 메시지 키, 메시지 값, 오프셋, 헤더 등 이미 적재된 데이터에 대해서 수정 또한 불가능하다고 했었다. 따라서 데이터를 적재할 때(프로듀서) 또는 데이터를 사용할 때(컨슈머) 데이터를 검증하는 것이 좋다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;프로듀싱한 레코드가 어느 파티션에 저장될지에 대한 기준&lt;/strong&gt; : 특정 토픽에 프로듀싱한 레코드는 여러 파티션 중에서 어떤 파티션에 저장될까? 만약 레코드 설정된 키(key) 가 특별히 없다면 &lt;strong&gt;라운드 로빈(round robin)&lt;/strong&gt; 방식으로 파티션에 나뉘어 저장된다. 반대로 키(key) 가 있다면 해당 키 값을 &lt;strong&gt;해싱(hashing)&lt;/strong&gt; 한 결과값을 기반으로 저장될 파티션을 결정하게 된다. 이때, 같은 키(key) 값을 가진 레코드는 같은 파티션에 저장된다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;신규-토픽-생성시-파티션이-내부적으로-배치되는-방식-및-기준&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A0%EA%B7%9C-%ED%86%A0%ED%94%BD-%EC%83%9D%EC%84%B1%EC%8B%9C-%ED%8C%8C%ED%8B%B0%EC%85%98%EC%9D%B4-%EB%82%B4%EB%B6%80%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%B0%B0%EC%B9%98%EB%90%98%EB%8A%94-%EB%B0%A9%EC%8B%9D-%EB%B0%8F-%EA%B8%B0%EC%A4%80&quot; aria-label=&quot;신규 토픽 생성시 파티션이 내부적으로 배치되는 방식 및 기준 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;신규 토픽 생성시 파티션이 내부적으로 배치되는 방식 및 기준&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/82eee302f909be12e78d7729ca00ccce/073e9/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.12883435582822%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABSklEQVR42j1RSQuCYBT0/98lM9uzxVYtzeiQIJUELadO0bVjVkokwcQ8sMNDvzd+s6nc73c8Hg8kSSJPnj+fD97vt7w/n0+8Xi/EcSyTZRnSNBWM+xzj3e/3C6XVaqHb7aJUKmE4HKJWq2G73WK1WqHX64F4pVLBeDwW7HQ6YT6fo9/vo16vC2bbNhqNBq7XKxQCHFVVMZ1O0W63sV6vhdBxHJimCV3X4Xkems0m9vs9fN8XAZKUy2W4rivCl8sFymAwwGg0QrFYlEt0mxPOZrO/e7oi+eFwwGKxQLVaFfFOpyNCdCsODcMQIB+62e12CMNQ1PM9yXhm5OVyKec8nWVZ4lAI+RF7YGSqknCz2SAIAtnTHTG64DsdMgkxTdNQKBT+XUpk9sSfwSV7mUwmOJ/POB6P0iHV2RXjs3xeiqJIMMalM2Kc2+2GH5m4umb0rTC4AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/82eee302f909be12e78d7729ca00ccce/a6d36/image-5.png&quot;
        srcset=&quot;/static/82eee302f909be12e78d7729ca00ccce/222b7/image-5.png 163w,
/static/82eee302f909be12e78d7729ca00ccce/ff46a/image-5.png 325w,
/static/82eee302f909be12e78d7729ca00ccce/a6d36/image-5.png 650w,
/static/82eee302f909be12e78d7729ca00ccce/073e9/image-5.png 719w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;신규 토픽을 생성한다면, 해당 토픽에는 신규 파티션들이 함께 여럿 생성될 것 이다. 만약 신규 토픽에 파티션을 5개 생성했을 경우를 가정해본다면, 위처럼 0번 브로커부터 시작하여 &lt;strong&gt;라운드 로빈(round robin)&lt;/strong&gt; 방식으로 &lt;strong&gt;리더 파티션(leader partition)&lt;/strong&gt; 들이 생성된다. &lt;strong&gt;즉, 파티션들은 0번 파티션부터 시작하여 라운드 로빈 방식으로 각 브로커에 생성된다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;라운드 로빈 방식으로 균등하게 생성됨으로써, 카프카 클라이언트는 여러 브로커에 골구로 균등하게 Request/Response 를 송.수신 할 수 있게된다. 이을 통해, &lt;strong&gt;레코드가 특정 서버(브로커) 에 몰리는 현상(hot spot) 을 방지&lt;/strong&gt; 할 수 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 그럼에도 불구하고, 만약 특정 브로커에 파티션이 몰리는 상황이 발생한다면 &lt;code class=&quot;language-text&quot;&gt;kafka-reassign-partitions.sh&lt;/code&gt; 명령어를 통해 파티션을 재분배 하여 해결할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;파티션-개수와-컨슈머-개수의-처리량&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%ED%8B%B0%EC%85%98-%EA%B0%9C%EC%88%98%EC%99%80-%EC%BB%A8%EC%8A%88%EB%A8%B8-%EA%B0%9C%EC%88%98%EC%9D%98-%EC%B2%98%EB%A6%AC%EB%9F%89&quot; aria-label=&quot;파티션 개수와 컨슈머 개수의 처리량 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파티션 개수와 컨슈머 개수의 처리량&lt;/h3&gt;
&lt;p&gt;파티션은 카프카의 병렬 처리의 핵심 컴포넌트로써, 그룹(컨슈머 그룹)으로 묶인 컨슈머들이 레코드를 동시간대에 병렬로 처리할 수 있도록 돕는다. 컨슈머의 처리량은 한정되어 있기에, 만약 많은 레코드를 병렬로 더 빠르게 처리하고 싶다면 컨슈머의 개수를 늘리는 것이 좋다. 그와 동시에, 컨슈머 개수만큼 정비례하여 파티션 개수도 늘리주면 처리량이 증가할 것이다. &lt;strong&gt;즉, 컨슈머와 파티션의 개수를 함께 Scale-Out 하여 레코드 처리 속도를 늘릴 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;파티션-개수를-줄이는-것은-불가능하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%ED%8B%B0%EC%85%98-%EA%B0%9C%EC%88%98%EB%A5%BC-%EC%A4%84%EC%9D%B4%EB%8A%94-%EA%B2%83%EC%9D%80-%EB%B6%88%EA%B0%80%EB%8A%A5%ED%95%98%EB%8B%A4&quot; aria-label=&quot;파티션 개수를 줄이는 것은 불가능하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파티션 개수를 줄이는 것은 불가능하다&lt;/h3&gt;
&lt;p&gt;단, 처리량 증가를 위해 파티션 개수를 늘릴 떄 한 가지 주의할 사항이 있다. 바로, 카프카에서 파티션 개수를 줄이는 기능은 지원하지 않는 점이다. 따라서 파티션을 늘리는 작업을 할 때는 그 개수를 다시 줄이는 작업이 불가능하므로 신중하게 파티션 개수를 정해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;만약, 파티션 개수를 꼭 줄이고 싶다면 토픽을 삭제해버리고 다시 재생성하여 파티션을 새롭게 생성하는 방법이 유일하다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;레코드record&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%EC%BD%94%EB%93%9Crecord&quot; aria-label=&quot;레코드record permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레코드(Record)&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 406px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/227bc9678f1eedd10c0dc369c0078af1/e33ef/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.87116564417178%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAABdElEQVR42q1TyarCQBD0o4N/4SUHDSYSd41oFMQlgiIoaEwCEU8uf+BRBQ9iPaphfA8eLgcDTc9UdyrVM5UEvvwk/m5utxsOhwN2ux22263Efr8XTGWFqzgej/Lu/X7/JVSb8/mMdDqNXC6HarWKSqWCbDaLTCbziFKpJHitVoOu6xgMBs8JL5cL8vk8JpMJNpsN1us1RqMRWq0Wms0mXNfFarVCHMdSLxQKGA6Hrwkty5KXSUoy5mKxKOr6/T7G47HgzIZhvCfkuGyKoghBEAhhu91GvV6X8Yj5vi919r4dmU3dbheLxQLz+RzT6RSO46BcLosyYrPZTOqmaX5GyKYwDLFcLkUJiaiSa2IM1j9SyDPsdDoPdVTDcanG8zzZE2edDnh5hrQNL4CW4ZgM2oM3zxvl2ApvNBqCUf1TwtPpJArZaNu2ZKpIJpPQNA2pVEo+qGrs7fV6zwmv16uMS9/RcyqIMXiOCuOaSunLf4Tf/Jd/AN8ijF9vMmdhAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/227bc9678f1eedd10c0dc369c0078af1/e33ef/image-6.png&quot;
        srcset=&quot;/static/227bc9678f1eedd10c0dc369c0078af1/222b7/image-6.png 163w,
/static/227bc9678f1eedd10c0dc369c0078af1/ff46a/image-6.png 325w,
/static/227bc9678f1eedd10c0dc369c0078af1/e33ef/image-6.png 406w&quot;
        sizes=&quot;(max-width: 406px) 100vw, 406px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;레코드는 &lt;strong&gt;타임스탬프, 헤더, 메시지 키, 메시지 값(value), 오프셋으로 구성되어 있다.&lt;/strong&gt; 프로듀서가 생성한 레코드가 브로커로 전송되면 오프셋과 타임스탬프가 지정되어 저장된다. 브로커에 한번 적재된 레코드는 수정할 수 없고, &lt;strong&gt;설정된 로그(레코드) 리텐션(retention) 기간 또는 용량 한도 설정 값에 따라서만 삭제된다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 오직 브로커만이 레코드를 삭제할 수 있는 주체로 이해했는데, 여기서 언급된 &apos;retention 기간 또는 용량 설정값에 의해서만 삭제된다&apos; 라는 표현이 있다. 이 설정에 의해 삭제하도록 만드는 주체가 브로커인지, 아니면 별건인지 추가 서칭이 필요하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;타임스탬프(timestamp)&lt;/strong&gt; : 레코드의 타임스탬프는 스트림 프로세시에서 활용하기 위한 시간을 저장하는 용로도 사용된다. 별다른 설정이 없다면, &lt;strong&gt;기본 값으로 ProducerRecord 생성 시각(CreateTime)&lt;/strong&gt; 이 들어간다. 또는 이 필드에 &lt;strong&gt;브로커 적재 시간(LogAppenTime)&lt;/strong&gt; 으로도 적재되도록 별도 설정할 수 있다. 이 옵션은 &lt;strong&gt;토픽 단위로 설정 가능하다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;오프셋(offset)&lt;/strong&gt; : 레코드의 오프셋은 프로듀서가 생성한 레코드에는 존재하지 않는다. 프로듀서가 전송한 레코드가 브로커에 적재될 때 오프셋이 지정된다. 오프셋은 0부터 시작되며, 순차적으로 1씩 증가한다. 컨슈머는 오프셋을 기반으로 처리가 완료된 데이터와, 앞으로 처리해야 할 데이터를 구분한다. 각 메시지는 파티션 별로 고유한 오프셋을 가지므로, 컨슈머에서 중복 처리를 방지하기 위한 목적으로도 사용한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;헤더(header)&lt;/strong&gt; : 헤더에는 key-value 데이터를 추가할 수 있으며, 레코드의 스키마 버전이나 포맷과 같이 데이터 프로세싱에 간단히 참고할만한 정보를 담아서 사용할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;메시지 키(message key)&lt;/strong&gt; : 메시지 키는 처리하고자 하는 메시지 값을 분류하기 위한 용도로 사용하며, 이를 &lt;strong&gt;파티셔닝(partitioning)&lt;/strong&gt; 이라고 한다. 파티셔닝에 사용하는 메시지 키는 파티셔너(partitionor) 에 따라 토픽의 파티션 번호가 정해진다. 메시지 키는 필수가 아니며, 지정하지 않으면 null 로 설정된다. 메시지 키가 null 인 레코드는 특정 토픽의 파티션에 라운드 로빈으로 전달된다. 반대로, null 이 아닌 메시지 키는 &lt;strong&gt;해싱(hashing)&lt;/strong&gt; 한 결과값에 의해서 특정 파티션에 매핑디어 전달된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;메시지 값(message value)&lt;/strong&gt; : 레코드의 메시지 값은 실질적으로 처리할 데이터가 담기는 공간이다. 메시지 값의 포맷(타입)은 제네릭으로, 사용자에 의해 구체 타입이 지정된다. Byte[], Float, String 등 다양한 타입을 지정 가능하다. 브로커에 저장된 레코드의 메시지 값은 어떤 포맷으로 직렬화되어 저장되었는지 알 수 없기 때문에, 컨슈머는 미리 역직렬화 포맷을 알고 있어야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고-및-인용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0-%EB%B0%8F-%EC%9D%B8%EC%9A%A9&quot; aria-label=&quot;참고 및 인용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 및 인용&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;아파치 카프카 애플리케이션 프로그래밍 - 최원영&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/&quot;&gt;https://kafka.apache.org/documentation/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA['일의 격' 책 인용 및 정리]]></title><description><![CDATA[회사를 다니다가, 지난번에 LINE…]]></description><link>https://haon.site/book/the-level-of-work/</link><guid isPermaLink="false">https://haon.site/book/the-level-of-work/</guid><pubDate>Sun, 09 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 306px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5d9d1f28464362841db92f56b39dab25/98b92/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 127.60736196319019%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAaCAYAAAC3g3x9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAEsElEQVR42qVV21ObVRDnH9EHxyen9vZQKZZSsAghFMolV0fNd0m4X57qg2Ordbw8dJzRwamd0tZ2kNiCoNgWW8epPqgjDC2l3AttQ4DcgJAUyIUkP3dPEqAKvvjNbHbP7p7f+e3u+b5kJBIJsPzfJ42TwYuBsVGc+uosPr10ER9fvCDkowut+OD8+Q05TZL2fdjaKmyWTyj3vbNf4pe+PgEsAO29vXheq8Eeox679JV4SVeJA2YdihQD8iUDNIoReRYD9hp10KoGZL+pFzmcu8dkwHNFhThz5comYOfPt7HfbESuTUWOKiNLllDeoOLU6Rq8c7IG775fK7SmVsWJk9WwnqhCpiSJXN6zj0BbvrVvAl67fUuwO0wJh2QLMi0WvF4lw9ysoqRORlGNjPJ6BVmSBQXkLyVfJtmcy3t2G3T4wv7NJuDVWz9tAPKmPKsETZUk7IOpjYXVEnJVSaxZDisWaKplwVoAtm8DeEiR8YqFWUiorJfFJl7nqRaYGpNrZpY+6NUUw5d3AtTWWqGh8kqpPF2jCm2NQswUKlGBsUkhNgqKtoiG4to6K/aadPh8O8Dj9VZUEFg5sSsjkKRNoMS4jHw6YlmW8rGubKCDmq00FP32gLm2ZMPNzYrYbGwkJtQnLk9DPTSkys6Wk33ltlTQbdj1z5KvEeDu1FBKaiUqjxLrJegaZNF47lsO9VFPjHj63NejtuQB+VXKzoDcr3JiqCdmZXWSKL2kVhbN50EcI7uyIcmygmOUm52+Nv8u2UDDUKkMRTA7TkwZoDw1bS77CLFkQBYGzKFrJACNOzDMs8liEBW0oZQAC2zMUhLXKEtOgh4jv4kmXky3IVvZvNjPDKWD3pR99OodrbZC30QsWajZr9kUwZpfw1yyC2tUEdc3UV6jFZUkRXU28T632Ns3Adtv3sALJcU4+JYJBTIB0weB9X6zAQfeMODI2wbk0sdBq5ooZiSfUegi1Yh8xYwXS4tx5vLXScBEPI6BkRF81taGSz9042pvD9p+7EZbTyfOdXbgXNd3sN/8HvbrXcLX1tOFy90daL9OvhuUT7EWext+7e9HIhZDRjgcRigUEujuuTkM3bsLx+NHWF5awurKivBNjI7CNTcLP/mcDgfWVlfxZHoaj6emaD2DGAFFIhGsra0lAdmI09d2auoh7g70Y3RkGEP3BzEz48Dg4D38ducO+vv+wuTkBMbHx4R/ZHgYjwh0fGwMIcIIEyATe4YhP2y75ufx5x+/48HQfSEjww8wSMz5kAkCYHvWOYNVYpp+0jgbgPx/EIutw+fxYH7OCZ/XA79/CUv+RSz4vFhc8MFHmn0elwtutxteyo1TuYlEnABTDPlH0F0NILjsg3d+Gl7XFJ4GvAgsuYX2zE7CNTOGgN+N5UUXwmtB0rOU+5DiC1hb8SMajSZ7yICRSDQZINAF9xMSB8KhFbic45S8jKfLXgT9npRvQuTywS7npCDBsWh0PT2UkKg/EY9hPRpGNBKiYBix9aiwWc/ThGedDmpJ0re+HhE5kfCqiMfJz6Q2esjyXw/3ykM9o3/eHXMi6Slz7WzwxILBIAIB6iVJYItwKSxbfRu5pHkvxxn0bwwb/eJ+fAS9AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/5d9d1f28464362841db92f56b39dab25/98b92/image-1.png&quot;
        srcset=&quot;/static/5d9d1f28464362841db92f56b39dab25/222b7/image-1.png 163w,
/static/5d9d1f28464362841db92f56b39dab25/98b92/image-1.png 306w&quot;
        sizes=&quot;(max-width: 306px) 100vw, 306px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;회사를 다니다가, 지난번에 LINE+ 신규 입사자.임원간의 회식 자리를 가진 적이 있다. 조직장님을 통해 책 &lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000001938229?utm_source=google&amp;#x26;utm_medium=cpc&amp;#x26;utm_campaign=googleSearch&amp;#x26;gt_network=g&amp;#x26;gt_keyword=&amp;#x26;gt_target_id=dsa-1974044872798&amp;#x26;gt_campaign_id=9979905549&amp;#x26;gt_adgroup_id=132556570510&amp;#x26;gad_source=1&quot;&gt;&apos;일의 격&apos;&lt;/a&gt; 을 추천받았는데, 나에게 큰 동기부여와 도움이 된 책인 것 같다. 주말이기도 하고, 서적을 읽으면서 마음이 들었던 부분 및 문구들을 인용하며 생각을 정리해보고자 한다.&lt;/p&gt;
&lt;p&gt;아직 1장, 2장 외에 3장은 못 읽었지만, 그냥 읽는대로 편하게 블로그에 발행해보려고 한다. 완벽한 글을 올리고 싶은 마음이 아닌, 그냥 내 생각이 정리된 글을 편하게 올리고 싶은 것이 목적이니.&lt;/p&gt;
&lt;h2 id=&quot;-1장-성장--일의-성과를-극대화-하는-기술&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-1%EC%9E%A5-%EC%84%B1%EC%9E%A5--%EC%9D%BC%EC%9D%98-%EC%84%B1%EA%B3%BC%EB%A5%BC-%EA%B7%B9%EB%8C%80%ED%99%94-%ED%95%98%EB%8A%94-%EA%B8%B0%EC%88%A0&quot; aria-label=&quot; 1장 성장  일의 성과를 극대화 하는 기술 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;📚 1장. 성장 | 일의 성과를 극대화 하는 기술&lt;/h2&gt;
&lt;h3 id=&quot;성공의-가장-큰-적은-실패가-아닌-지루함&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B1%EA%B3%B5%EC%9D%98-%EA%B0%80%EC%9E%A5-%ED%81%B0-%EC%A0%81%EC%9D%80-%EC%8B%A4%ED%8C%A8%EA%B0%80-%EC%95%84%EB%8B%8C-%EC%A7%80%EB%A3%A8%ED%95%A8&quot; aria-label=&quot;성공의 가장 큰 적은 실패가 아닌 지루함 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;성공의 가장 큰 적은 실패가 아닌 지루함&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;아마추어는 기분 좋을 떄만 훈련한다. 보통 선수는 매일 훈련하지만 지루할 떄면 대충 한다. 그러나 뛰어난 선수는 상관없이 훈련한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&apos;신경 끄기의 기술&apos; 이라는 책에서도 이런 내용이 있다. &quot;목표는 멋지지만 목표로 가는 길에는 똥 덩어리가 가득하다. 지루한 길이다. 성공을 결정하는 질문은 &apos;나는 무엇을 하고 싶은가?&apos; 가 아니라, &apos;그 과정에서 오는 고통을 견딜 수 있는가?&apos; 이다. 특히 직장 생활을 시작한 초급 사원에게 제일 필요한 것은 허드엣일을 견디는 것이다&quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;나 자신을 돌이켜보고 내 주위를 봐도 이 지루함을 멈추는 그떄부터 그 사람은 더 이상 성장하지 않는다. 그것이 멈추어질 때 프로들은 현역에서 은퇴한다. 강수진은 그떄 떠나는 게 후배들, 관객들을 위한 예의이자 존중의 표시라고 했다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그러면 이 지루함을 도대체 어떻게 이길까? 홀륭한 선수라고 열정이 무한대가 아니다. 그들도 열정이 식을 떄가 있다. 그러나 그들을 그 지루함을 지속할 동기를 스스로 찾는다고 한다. &lt;strong&gt;동일한 일을 그대로 반복하는 것이 아니라 조금씩 목표를 올려 도전함으로써 즐거움을 만든다.&lt;/strong&gt; 그리고 이 과정을 습관화하여 자동으로 행동하게 만든다고 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;그냥-찾아가라&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%83%A5-%EC%B0%BE%EC%95%84%EA%B0%80%EB%9D%BC&quot; aria-label=&quot;그냥 찾아가라 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그냥 찾아가라&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;어떤 운동을 배울 떄 나 혼자 연습을 열심해 해서 좀 괜찮아진 후 코치를 만나야겠다고 생각했다. 그리고 이것저것하고 시간을 끌었다. 한참 후에 코치를 만나니, 그동안 혼자 이것저것 했던 게 대부분 시간 낭비였음을 발견했다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;우리가 진단, 코칭, 피드백 또는 도움을 받는 이유는 무엇일까? &lt;strong&gt;당연히 문제를 정확히 찾아 빠르게 해결하고 실력의 진보를 위한 것이다.&lt;/strong&gt; 그런데 왜 미루는 것일까? 여러 이유가 있겠지만, 그 중 하나는 그것을 &lt;strong&gt;&apos;평가&apos; 로 여기기 때문이기도 하다.&lt;/strong&gt; 학창 시절 끝없는 비교와 평가로 살아왔던 우리는 모든 것을 평가로 인식하는 경향이 있다. 또한 실제 진단을 평가로 쓰는 곳도 많다. 그러다 보니, &lt;strong&gt;잘한 다음에 하려고, 또는 잘 보이고자 하는 마음에 자꾸 미룬다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;가장 효과적이며 효율적인 길은 가장 빠른 시간 내에, 그냥 있는 그대로 모습을 드러내어 피드백을 받거나 진단, 도움을 받는 것이다. 미루지 말고 그냥 헤라. 잘한 뒤에 상사나 멘토나 코티나 도움받을 사람을 찾아가는 것이 아니다. &lt;strong&gt;잘하기 위해 찾아가는 것이다.&lt;/strong&gt; 미리 잘 하려고 애쓰지 마라. &lt;strong&gt;내가 좋은 평가를 받기 위해 고수를 찾아가는 것이 아니라, 도움받기 위해 찾아가는 것이다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 나에게 지금 가장 필요한 조언이 아닐까? 나에게 완벽하는 것을 바라는게 아닐 것이다. 잘하기 위해서, 피드백과 도움을 받기 위해 선배들을 찾아가자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;연봉을-더-받으려면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%B0%EB%B4%89%EC%9D%84-%EB%8D%94-%EB%B0%9B%EC%9C%BC%EB%A0%A4%EB%A9%B4&quot; aria-label=&quot;연봉을 더 받으려면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;연봉을 더 받으려면?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;연봉이라는 것은 어떻게 산출될까? 경험상으로 대략 공식을 만들어 보면 &lt;strong&gt;&apos;개인의 연봉 = f(자신이 만드는 가치, 희소성)&apos;&lt;/strong&gt; 이라고 할 수 있다. 여러가지 변수가 있겠지만 나는 이 두 가지 변수가 가장 크다는 생각이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&apos;자신이 만드는(또는 기여하는) 가치&apos;&lt;/strong&gt; 란 자신이 일함으로써 회사가 얼마나 이익을 창출할 수 있는가이다. 이는 개인의 능력만으로 결정되는 것은 아니고, 회사의 수익 시스템과 결합되어 결정된다. 예를들어 한 회사가 한 사람을 뽑아 1억의 수익을 얻는다고 한다면 그 사람의 연봉은 1억 이상을 줄 수 없다. 그런데 실제로는 연봉 외에 그 사람을 위해 들어가는 간접비(사무실 임대료, 각종 비용 등) 가 50% 에서 100% 까지 더 들어간다. 만일 간접비가 100% 라면 그 사람에게 최대로 줄 수 이는 연봉은 5천만원 정도이다. 그러면 회사는 아무 이익을 남기지 않고 사업을 할 수 있나? 아니다. 그러므로 마진을 떄면 더 낮아진다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;연봉을 많이 받으려면, 아래 2가지 법칙을 준수하라.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;법칙1. 가능한 자신을 통해 만들 수 있는 가치가 큰 기업으로 가야한다.&lt;/strong&gt; 똑같은 능력의 사람으로 5천만원 바에 못 만드는 회사도 있고 10억을 만드는 회사도 있다. 당연히, 적은 가치를 만드는 회사가 높은 연봉을 줄 수 없다. 나랑 똑같은 학교, 학과를 졸업한 내 친구는 저 만큼 받는데 나는 이 모양이라고 불평한다면 답을 심플하다. &apos;갈 수 있다면 그곳으로 가라&apos; 단지, 기억할 것은 지금 큰 가치를 만드는 회사가 미래에도 계속 그러리라는 보장은 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;법칙2. 대체하기 어려운 희소성을 확보하는 것이다.&lt;/strong&gt; 그런데, 이때 희소성은 &apos;고정적&apos; 이 아니다. 내가 작은 회사에 다닐 떄는 대체 가능한 사람이 주위에 없었지만, 큰 회사에 가니 대체 가능한 사람들이 주위에 많을 수 있다. 이 경우 오히려 작은 회사에 있는 것이 높은 연봉을 받을 수 있는 가능성이 더 높다. 그러므로, 무조건 큰 기업에 가는 것이 나은 선택이 아니다. 벤처에서 펄펄 날다가 대기업에 와서 그저 그렇게 지내고 연봉도 이후 거의 상승 안되는 직원들도 많이 봤다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;결론적으로, 두 가지 조합의 관점에서 생각해보면 4가지 경우로 나뉜다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 생산가치도 높고, 나의 희소성도 높다 : 지금 있는 회사가 좋다. 여기에서 승부를 건다. 굳이, 욕심을 낸다면 동일한 희소성을 유지하면서 더 높은 생산가치를 낼 수 있는 곳으로 옮길 수 있지만, 기업문화 차이 등을 고려할 떄 위험하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 생산가치는 높은데 나의 희소성이 낮다 : 신의 직장이다. 최대한 지금 있는 회사에서 잘리리 않으려 노력한다. 공명심에 회사 그만두면 밖에서는 지옥을 경험할 것이다. 단, 희소성을 높이돌혹 자기계발을 해야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 생산가치가 낮은데 나의 희소성은 높디ㅏ : 지금 있는 회사에게 낮은 연봉을 받는다면 높은 연봉을 요청한다. 안 올려주면 같은 회소성을 유지하면서 더 높은 생산가치를 낼 수 있는 곳을 찾아 옮길 것을 고려한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 생산가치도 낮고 나의 희소성도 낮다 : 큰일이다. 연봉은 신경 쓰지 말고 일단 실력을 키운다. 미 취업자라면 일단 받아주는 곳에 가서 잔말 말고 2~3년 정도는 열심히 일하며 실력을 키운다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;하버드생보다-더-뛰어난-성과를-올리는-방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%98%EB%B2%84%EB%93%9C%EC%83%9D%EB%B3%B4%EB%8B%A4-%EB%8D%94-%EB%9B%B0%EC%96%B4%EB%82%9C-%EC%84%B1%EA%B3%BC%EB%A5%BC-%EC%98%AC%EB%A6%AC%EB%8A%94-%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;하버드생보다 더 뛰어난 성과를 올리는 방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;하버드생보다 더 뛰어난 성과를 올리는 방법&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;한 분야의 오랜 학습은 이것저것 잘하는 천재를 이긴다. 소위 공부 잘하는 사람들이 서울대, 허버드를 자랑하고 최고의 성적을 자랑하고 있을 떄, &lt;strong&gt;조금 덜 똑똑해도 한 분야를 끈질기게 오래 한 사람은 자신의 분야에 획을 그을 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;예전에 일본인 노벨상 수상자들에 관한 글을 읽은 적이 있다. 여기서 흥미로운 점이 있는데, 그들 중 어떤 분들은 평생 외국 유학도 안해보고, 심지어 박사학위도 없이 일반 회사에 다니는 분들도 있다는 것이다. 천재도 아니고, 심지어 공부를 그리 잘하지 못했던 분들도 있었다. 그런데 공통점은 &apos;한 분야&apos; 에 미쳤다는 것이다. &lt;strong&gt;돌아가도 좋다. 서툴러도 좋다. 하나는 완성하는 일, 그것이 중요하다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;스스로 머리가 부족하다고 여길수록 한 분야를 파라. 미친 듯이 오랜 기간 파라. 그러면 이것저것 관심 많고 인기 분야만 쫓아다니는 수재, 천재, 고학력자를 이길 수 있다. 세상은 알량한 머리와 학벌을 자랑하는 이들에 의해 바뀌는게 아니다. 우직하고 호기심 있는 이들의 꾸준하고 피땀어린 노력에 의해 바뀐다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;리더가-에너지를-너무-많이쓰는-대상이-안되는게-좋다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%EB%8D%94%EA%B0%80-%EC%97%90%EB%84%88%EC%A7%80%EB%A5%BC-%EB%84%88%EB%AC%B4-%EB%A7%8E%EC%9D%B4%EC%93%B0%EB%8A%94-%EB%8C%80%EC%83%81%EC%9D%B4-%EC%95%88%EB%90%98%EB%8A%94%EA%B2%8C-%EC%A2%8B%EB%8B%A4&quot; aria-label=&quot;리더가 에너지를 너무 많이쓰는 대상이 안되는게 좋다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리더가 에너지를 너무 많이쓰는 대상이 안되는게 좋다.&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;잘 맞는 인재는 리더가 에너지를 많이 쓰지 않게 한다. 능동적으로 움직인다. 어떤 지시를 하면 리더의 마감일보다 조금 빠르게 결과를 제공하고, 성격이 급한 리더와 함께 할 때는 적절하게 중간 진행을 커뮤니케이션한다. 자신의 일을 깔끔하게 처리하고 맡긴 조직을 잘 끌고 가서 리더가 신경 쓸 일이 많지 않게 한다. 가벼운 코칭으로도 실행을 빠르게 해낸다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;반면, 잘 맞지 않는 구성원은 리더의 에너지를 많이 쓰게 한다. 리더와의 마감 약속을 자꾸 어긴다. 자신의 일을 깔끔하게 처리하지 못해서 자꾸 잔소리를 하게 한다. 커뮤니케이션을 적절히 하지 못햇거 성질 급한 리더가 자꾸 물어보게 한다. 자꾸 마음에 걸리고 신경 쓰인다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;당신이 구성원이라면? 리더가 에너지를 많이 쓰지 않게 하는 것이 좋다. 이 말은 리더가 당신에 대해 무관심하게 만들라는 것이 아니라 믿을만한 모습을 보여주라는 것이다. 특히 꼼꼼하고 성질 급한 상사에 대해서는 선재적인 것이 좋다. 수동적으로 있으면 일을 적게 받는다고 생각하는 분들이 있는데 큰 오해다. 또한 마감보다 항상 조금 앞서 제공하라. 너무 빨리하면 일이 많아질 위험이 있으나 약간 정도만 빠르게 해라. 리더를 위협하지 마라. 셜령 이후 커리어를 고민하고 있을지라도, 있을 때는 영원히 있을 것 처럼 일하라. 리더가 개인 에너지를 덜 빼앗긴다면 당신은 괜찮은 직원으로 여겨질 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그것보다 더 좋은 것은? 리더의 에너지를 안 쓰게 만드는 정도를 넘어 반대로 리더에게 에너리를 주는 사람이 있다. 그를 보면 힘이 난다. 그러면 당신은 정말 사랑받을 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 요즘 말로 표현하면 알잘딱갈센이 아닐까? 믿을만한 신입 사원이 되기 위해 노력해보자. 선배들이 에너지를 쓰지 않도록 신뢰도 있는 후베가 되어보자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;평범해도-비범해지는-법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8F%89%EB%B2%94%ED%95%B4%EB%8F%84-%EB%B9%84%EB%B2%94%ED%95%B4%EC%A7%80%EB%8A%94-%EB%B2%95&quot; aria-label=&quot;평범해도 비범해지는 법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;평범해도 비범해지는 법&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;어떤 사람은 비범한 일도 평범하게 만드는데, 어떤 사람은 평범한 일도 비범하게 만든다. 일 자체가 평범하거나 비범하다고 여겨서는 안 된다. 일하는 태도가 평범과 비범을 나눈다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&quot;쥐꼬리만한 돈밖에 받지 않는데 평범한 일을 비범하게 하는 것은 결국 착취당하는 거 아닌가요? 돈 받은 만큼만 일하고 근무시간에는 최대한 놀아야지요. 그건 구세대들 때나 통해는 이야기에요&quot; 이런 말을 하는 분들이 있다. 바보 같은 질문이다. &lt;strong&gt;평범한 일을 비범하게 만드는 것은 남을 위한 것이 아니라 자신의 가치를 높인다.&lt;/strong&gt; 또한 그런 사람은 절대 그 일만 계속하지 않는다. 더 큰 일을 하게 되며, 그렇게 일하는 것이 몸에 익어 더 큰 을 맡거나 자기 사업을 해도 역시 비범하게 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;고깃집에서 알바를 오랜 기간 했다는 직원이 있었다. 그때 팁을 꽤 많이 받았단다. 비결을 물어보니 손님이 오면 어떤 분이 어떤 반찬을 좋아하는지 유심히 관찰했다고 한다. 그분이 다시 오면 그 반찬을 많이 내어 드리고 빠르게 채워드렸다고 한다. 고객이 놀랄 수밖에 없다. 알바는 고기를 더 줄 순 없지만, 반찬은 더 줄 수 있는 권한이 있다. 역시 동일한 알바를 해도 다르게 하는 사람이 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 평소에 일하고 있는 작업들도 더 비범하게 만들어보자. 마감기한이 아닌 더 빠르게 작업을 끝낸다던지, 다른 사람들이 생각치 못했던 서비스 장애 및 검수 사항등을 더 고민하고 파악해볼 수 있을 것이다. 또한 내가 노력해 작업한 내용들의 가치를 소중하다고 생각하고, 인정해주자. 비범하게 만든 일을 괜히 평범하게 만들어버릴 수도 있는것은 나 자신이 될 수도 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;바쁜-사람은-항상-바쁘다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%94%EC%81%9C-%EC%82%AC%EB%9E%8C%EC%9D%80-%ED%95%AD%EC%83%81-%EB%B0%94%EC%81%98%EB%8B%A4&quot; aria-label=&quot;바쁜 사람은 항상 바쁘다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;바쁜 사람은 항상 바쁘다&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;가끔 &quot;저는 너무 바빠요. 좀 여유롭게 일할 수 없을까요?&quot; 라고 상담하는 분들을 만났다. 그런데 흥미롭게도 이런 분들의 상당수는 여유를 부릴 수 있는 환경에서조차 스스로를 바쁘게 굴린다는 것을 발견했다. 이에 대게 바쁜 사람은 여유로운 일을 맡아도 바쁘고, 여유로운 사람은 정신없는 일을 맡아도 여유롭다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;바쁜 사람과 여유로운 사람 유형으 차이는 무엇일까? 바쁜 사람은 고객 수준이나 프로젝트의 유형과 무관하게 목표를 항상 100% 에 잡았고 품질에 대한 스스로의 완벽성과 만족을 중시하며 일했다. 황상 계획을 빡빡하게 잡았다. 고객의 평가와 무관하게 자신이 만족해야 했기에 매사 힘들 수 밖에 없었다. 반대로 여유로운 사람 유형은 고객의 수준과 프로젝트의 유형에 따라 프로젝트를 진행했다. 고객의 수준이 80 이면 85 정도를 목표로 했던 것이다. 가끔 수준이 높은 고객 프로젝트는 힘들게 진행했지만 대개의 프로젝트는 여유를 가질 수 있었던 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;바쁜 사람으로 일하는 것이 필요하지만 평생 그렇게 일하는 것은 삶에 좋지 않다. 삶은 균형과 여유 공간이 필요하다. 과도하게 일하다가 건강을 해치기도 한다. 강철 체력도 있지만 그렇게 일하다 진짜 골병드는 사람이 있다. 매사 바쁘고 매사 최선을 다하는 삶이 꼭 바람직하다고 말하기 어렵다. &lt;strong&gt;매사 최선을 다하지 말라. 최선을 다할 가치가 있는 일에만 최선을 다하자!&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;여유롭게 일하는 비결은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;모든 것은 다 동일하게 중요하지 않다는 사실을 인정한다.&lt;/strong&gt; 중요한 것에 에너지를 쏟고, 중요하지 않은 일을 초스피드로 하거나 대충 하거나 타인에게 맡기거나(떠맡기는게 아니라, 대가를 주거나 역할로 맡김) 아예 하지 않는다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;내가 할 일은 내가 빠르게 하고 남의 일을 대신 고민하지 않는다.&lt;/strong&gt; 나는 내가 할 일과 산하 구성원이 할 일을 명확히 한다. 정치적인 구성원들은 자기가 할 일을 위나 옆이나 아래에 미루곤 한다. 이에 나는 산하 임원에게 &quot;그건 당신이 고민하고 답할 문제인데 왜 제게 떠넘기죠?&quot; 라는 말로 책임을 명확히 준다. 단, 그가 그것을 이루지 못할 경우를 대비한 계획을 준비한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;바쁘다는 것 자체를 인생을 보람있게 사는 것으로 여기는 분들도 있다. 새벽에 출근해 밤중에 나오면서 삶의 보람을 느끼곤 하신다. 그러다가 퇴직하고는 멘붕에 빠진다. 사실 위로 올라갈수록 더 여유를 가질 수 있는데 대개 워커홀릭은 스스로를 바쁘게 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 바쁜것은 나 스스로가 바쁘게 만든 것이 아닐까? 우선순위를 구분하고, 모든 일이 중요하지 않다는 것을 인지하고 일을 하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;때로-뺀돌이가-되어라&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%95%8C%EB%A1%9C-%EB%BA%80%EB%8F%8C%EC%9D%B4%EA%B0%80-%EB%90%98%EC%96%B4%EB%9D%BC&quot; aria-label=&quot;때로 뺀돌이가 되어라 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;때로 뺀돌이가 되어라&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;바쁘지 않으면서도 성과를 내는 비결은 이미 다 나와있다. &apos;파레도의 법칙&apos; 을 실행하면 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;우리 일을 살펴보면 20의 핵심적인 일과 80의 비 핵심적인 일이 있다. 앞의 20의 특징은 잘하면 비선형적인 성과를 내는 일이다. 핵심 프로젝트를 성공 시키거나 고객을 발굴하거나 새로운 변화를 만드는 일, 새로운 고객 가치를 만드는 일, 재테크 등일 것이다. 반면 뒤의 80은 못하면 욕을 먹지만 잘해야 본전인 일이 많다. 대개 많은 사람 손이 가고 시간을 많아 잡아먹는 운영상 일들이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;몰론 위의 20이나 80의 일, 둘 다 대충 하면 실패환다. &lt;strong&gt;성실하고 바쁜 사람들은 대개 위의 20이나 80을 구분하지 않고 무조건 최선을 다해 열심히 한다.&lt;/strong&gt; 다 완벽하려 한다. 이러면 성과도 나고 인정도 받고 승진도 하지만 항상 정신이 없고 여유가 없다. 너무 바빠서 새로운 생각을 하거나 새로운 시도를 하거나 삶을 돌아볼 여유가 부족해진다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그러면 어떻게 일할 것인가?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;위의 20은 열심히 한다. 에너지를 쏟는다. 시간을 더 투자한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;위의 80은 무작정 열심히 하지 말고 &apos;어떻게 편하게 할까?&quot; 에 초점을 둔다. 소위 뺀돌이가 되는 것 이다. 허술하게 하라는 뜻은 아니다. 거절하거나 시스템화하거나 자동화하거나 아웃소싱하거나 협업을 하거나, 여하튼 편할 방법을 찾아야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;80 중 아예 할 필요가 없는 것은 하지 않는다. &lt;strong&gt;할 필요가 없는 일을 판단하는 기준은 &quot;그 일을 안 했을 때 무슨 큰 일이 일어나는가?&quot; 를 자문하면 된다.&lt;/strong&gt; 큰 일이 안 일어나는 일은 하지 않으면 된다. 할 필요가 없는 일을 효율적으로 하는 것도 바보짓이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;피하는-쪽이-아니라-향하는-쪽으로-생각하라&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%BC%ED%95%98%EB%8A%94-%EC%AA%BD%EC%9D%B4-%EC%95%84%EB%8B%88%EB%9D%BC-%ED%96%A5%ED%95%98%EB%8A%94-%EC%AA%BD%EC%9C%BC%EB%A1%9C-%EC%83%9D%EA%B0%81%ED%95%98%EB%9D%BC&quot; aria-label=&quot;피하는 쪽이 아니라 향하는 쪽으로 생각하라 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;피하는 쪽이 아니라 향하는 쪽으로 생각하라&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&quot;이러한 원하지 않는 모습을 어떻게 해결할 수 있을까요?&quot;&lt;/strong&gt; 라는 질문을 많이 받는다. 즉, &lt;strong&gt;&quot;피하고 싶은 미래를 어떻게 피하고 해결할 수 있을지&quot;&lt;/strong&gt; 에 대한 질문이다. 예를들면 &quot;저는 역량이 부족한 것 같아요&quot;, &quot;상사가 힘들게 하고 마음에 안 들어요&quot; 등이 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하지만 사람들은 &lt;strong&gt;&quot;피하고 싶은 미래가 아니라 정말 이루고자 하는 미래, 원하는 모습, 마음을 하나로 집중시킬 목표는 무엇인가요?&quot;&lt;/strong&gt; 에 대한 대답은 하지 못한다. 생각 외로 원하는 모습은 잘 묘사하지 못한다. 피하고자 하는 상황, 문제는 잘 아는데 진짜 원하는 모습을 그리는데 익숙하지 않은 것이다. 돈의 결핍을 피하고자 하지만 돈을 번 후 그걸로 무엇을 할지는 생각이 없다. 역량의 부족은 피가호자 하나 역량을 쌓아 뭘 할지는 생각이 별로 없다는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&quot;나는 왜 돈이 부족할까?&quot;, &quot;나는 왜 실력이 부족할까?&quot;, &quot;나는 왜 나쁜 습관에 빠져있을까?&quot; 등에 집중하다보면 항상 돈, 실력이 부족하고 나쁜 습관에 빠져있는 채 살게 된다는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;진짜 원하는 다른 것에 집중하고 시간을 쓰다보면 버리려던 나쁜 습관이 자연스럽게 없어질 수 있다. 그러므로 중요한 것은, &lt;strong&gt;원하지 않는 것을 없애려는데 시간을 쓰기 보다는 원하는 것에 에너지를 쓰는 것이 더 낫다.&lt;/strong&gt; &quot;내가 직장에서 어떤 모습을 만들까? 어떤 사람이 될까? 돈을 많이 벌어서 뭐 할까?&quot; 에 집중하게 이를 위해 무엇을 할까? 를 실행하는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;피하려고 생각하기보다는 향해서 나아가려고 생각하라.&lt;/strong&gt; 나아가려하면 창의적인 생각들이 떠로은다. 자신의 부족함을 극복하기 위해 애쓰려는 노력을 원하는 형상이나 이미지를 만들고 이를 성취하는데 쓰라.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;초보-코치가-아니라-코치다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B4%88%EB%B3%B4-%EC%BD%94%EC%B9%98%EA%B0%80-%EC%95%84%EB%8B%88%EB%9D%BC-%EC%BD%94%EC%B9%98%EB%8B%A4&quot; aria-label=&quot;초보 코치가 아니라 코치다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;초보 코치가 아니라 코치다&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;스스로 과도한 겸손에 빠지면 &quot;초보&quot; 라는 말 뒤에 숨어서 가능성을 제한해버릴 수 있다. 겸손이 지차닌 것 은 부족함과 유사하다. 그런데 흥미로운 점은, 대개 당신이 선망하는 그 유명한 분들은 당신보다 더 어린 나이와 부족한 지식으로 남들을 돕고 가르치기 시작했다는 것을 잊지 마시라.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;코치면 코치지 무슨 초보 코치인가? 초보 코치가 아니라 코치이고, 신입사원이 아니라 사원이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&apos;신입&apos; 이나 &apos;초보&apos; 라는 이름하에 숨을 이유가 없다. 그것은 겸손이 아니다. 프로의 세계에 들어가면 그때부터 프로인 것이고 프로답게 행동해야 한다. 연륜을 존경할 필요는 있지만, 오히려 연륜이 부족할 때 더 신선한 시각과 넘치는 에너지로 더 잘할 수 있다는 것을 기억할 필요가 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;집중력과-의지력의-명과-암&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%91%EC%A4%91%EB%A0%A5%EA%B3%BC-%EC%9D%98%EC%A7%80%EB%A0%A5%EC%9D%98-%EB%AA%85%EA%B3%BC-%EC%95%94&quot; aria-label=&quot;집중력과 의지력의 명과 암 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;집중력과 의지력의 명과 암&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;열정, 집중력, 의지력은 등은 용량이 한정되어 있다.&lt;/strong&gt; 이에, 어떤 것이 에너지를 과하게 쓰는 사람은 항상 그 이면이 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;너무 한쪽에 모든 에너지를 써버리거나 매사에 과도한 열정과 의지력을 써버리는 것도 위험하다. 조금 남겨두어야 개인의 삶이나 진짜 원하는 꿈을 위해서도 쓸 수 있다. 일주일에 하루 정도는 눈앞의 일에서 벗어나 소소함을 누리거나 조금 먼 시각, 장기적인 시각에서 자신의 일과 삶을 돌이켜 볼 필요도 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;매사에 빈둥거리는 것도 문제이지만 매사에 효과, 효율만 강조하게 조급해하고 불안해하는 것이 오히려 건강한 성장에 도움이 안된다. 의외로 쉬거나 주제없이 대화하는 것을 불안해하고 죄챇감을 느끼는 분들이 많다. &lt;strong&gt;가끔식은 그 동안 집중했던 것에서 떠나 에너지를 축적하고 일상의 소소함, 자연이나 큰 관점에서 에너지를 써보자.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;탁월한-사람과-경쟁하면-실력이-늘까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%83%81%EC%9B%94%ED%95%9C-%EC%82%AC%EB%9E%8C%EA%B3%BC-%EA%B2%BD%EC%9F%81%ED%95%98%EB%A9%B4-%EC%8B%A4%EB%A0%A5%EC%9D%B4-%EB%8A%98%EA%B9%8C&quot; aria-label=&quot;탁월한 사람과 경쟁하면 실력이 늘까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;탁월한 사람과 경쟁하면 실력이 늘까?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;건전한 경쟁, 약간의 차이가 있는 사람들과의 경쟁은 우리의 실력을 향상시킨다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그러나 슈퍼스타와의 경쟁은 오히려 자신의 실력을 망가뜨린다. 기량 발휘를 못하고 심리적으로 주눅 들고 쪼그라든다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그러므로 슈퍼스타가 없는 곳에서 마음 편하게 놀아라. 즉, 용의 꼬리보다 뱀의 머리가 돼라.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;더 훌륭한 전략은 슈퍼스타와 경쟁하려 하지 말고 협력화는 것이다. 그리고 실력이 쌓이면 슈퍼스타를 떠나서 자기만의 분야를 만들어라.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 내 생각을 다시 정리하면, 우선 경쟁을 통한 성장을 하고 싶다면 비슷한 실력 수준의 사람들과의 경쟁을 통한 성장을 하는 것이 좋다는 것으로 정리되었다. 이것보다 더 좋은 방안은 뛰어난 실력자와 경쟁이 아닌 협업을 통해 성장하는 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;글로-쓰면-다룰-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%80%EB%A1%9C-%EC%93%B0%EB%A9%B4-%EB%8B%A4%EB%A3%B0-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;글로 쓰면 다룰 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;글로 쓰면 다룰 수 있다&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;글로 명료화하는 순간, 복잡한 생각을 쉽게 다룰 수 있게 된다. 글로 명료화하지 않으면 다루기 어렵다. 생각이란 실체가 없어서 머리 밖으로 뿔뿔이 산발되어 흩어지기 쉽다. 게다가 감정까지 복합되면 아무리 똑똑한 사람도 이를 다룬다는 것은 거의 불가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그러므로 일할 때나 보고서, 논문 쓸 때만 글로 쓰지말고 평소 자기 삶에서도 써보길 권고한다. 좋은 생각이면 좋은 생각대로, 부정적인 생각이면 부정적인 생각대로 써보면 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;쓰지 않는 또 하나의 이유는 너무 잘 쓰려 해서이다. 특히, 블로그나 페북 등의 SNS 를 꾸준히 하는 비결은 몇 줄이라로 가볍게 쓰는 것이다. 처음엔 그냥 내 감정이나 생각을 2~3줄로 &apos;명명&apos; 하거나 &apos;명로화&apos; 하는 정도로 충분하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;너무 &apos;길게&apos; 그것도 &apos;자주&apos; 그리고 &apos;잘&apos; 쓰려하면 오래가지 못한다. 주말에만 쓰는 것도 방법이다. 주위 사람들의 반응은 &apos;동기&apos; 가 되기도 하고 &apos;독&apos; 이 되기도 한다. 그냥 적절한 동기부여 정도로만 받아들여야지 너무 의식하면 피곤해진다. 자신을 위해서 쓰는 것이지 타인에게 영향주려고 쓰면 피곤해진다. 처음엔 2~3줄로 충분하다. 아무렇게나 막 쓰면 되고, 그러다가 좀 쓰게 되면 글쓰기 책 한 권 정도 보면 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;유명해지지 않는 한 당신의 글에 주위 사람들은 아무 관심이 없다. 그냥 고민없이 한번에 쉼 없이 쓰는 것을 추천한다. 그리고 혹 수정할 필요가 있으면 이후 몇 차례 수정한다. 자꾸 읽다 보면 활자 중독이 되어 책을 쉽게 읽게 되는 것 처럼 쓰는 것도 습관화되다 보면 쉽게 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 내가 가장 인상깊게 읽었던 부분인 것 같다. 내가 평소에 글을 지주 쓰는 이유 및 철학과 완전히 일치한다. 글을 쓰면 생각이 정교화(elaboration) 되고, 메타인지가 활성화 되는 효과를 자주 느낄 수 있었다. 글쓰기와 관련해 느낀 점 및 개선점을 추가로 적어보자면 아래와 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;업무 중에도 CS, QA, 회의 및 기타 안건을 해결할 때 생각이 흩어진 느낌을 받았다면 바로 글로 상황을 정리해보자.&lt;/li&gt;
&lt;li&gt;길게, 자주, 잘 쓰려는 것이 부담이 될 수 있다. 블로그에 요즘 글 발행을 거의 못하고 있는데, 부담이 된다면 짧게라도 정리되지 않은 내용을 올린 후 자주 수정 및 업데이트를 해보자. 타인을 의식하지 않고, 순수히 나만을 위한 글을 써보겠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;타인의-성공비결이-내게-얼마나-도움이-될까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%83%80%EC%9D%B8%EC%9D%98-%EC%84%B1%EA%B3%B5%EB%B9%84%EA%B2%B0%EC%9D%B4-%EB%82%B4%EA%B2%8C-%EC%96%BC%EB%A7%88%EB%82%98-%EB%8F%84%EC%9B%80%EC%9D%B4-%EB%90%A0%EA%B9%8C&quot; aria-label=&quot;타인의 성공비결이 내게 얼마나 도움이 될까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;타인의 성공비결이 내게 얼마나 도움이 될까?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;타인의 성공 및 실패 경험은 나의 성공 및 성장률에 얼마나 큰 도움이 될까? 성공률에 큰 영향력을 끼치는 요소는 타인의 성공 경험이 아닌, 타인의 실패 경험이라는 점이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;타인의 실패 경험은 내 성공 및 성장에 큰 영향력을 끼친다. 흥미롭게도 타인의 성공 경험은 자신의 성공에 큰 영향을 미치지 않았으나, 타인의 실패 경험은 자신의 성공에 매우 큰 영향을 미쳤다. 왜일까? 대개 사람은 타인의 성공을 부러움으로 가져가지만, 자신의 레슨과 피드백으로는 가져가지 않기 때문이다. 그러나, 타인의 실패는 명확한 피드백이 되고 타산지석이 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;흥미롭게도, 자신의 실패를 통한 배움은 없었다. 인간은 오히려 유사한 실패를 반복한다. 그런데 자신의 성공 경험은 더 큰 성공을 가져왔다. 왜일까? 대개 사람은 자신의 실패에 대해서는 주위나 자신을 탓할 뿐 냉정한 피드백을 가져가지 못하기 때문이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 내가 성공하려면 타인의 성공/실패 경험 어떤 것을 집중해야 할까? 타인의 실패 경험에 집중해보자. 실패 경험을 들음으로써 명확한 피드백과 타산지석이 될 것이다. 아울러, 나 자신의 실패를 통한 배움은 없다고 하는데, 나 자신에게 실패했을 경우에 대한 냉정한 피드백이 있으면 되는 것 아니곘는가? 나 자신에 대한 실패 경험이 생겼을 경우, 괴롭고 고통스럽겠지만 냉정한 피드백을 해보자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;주위에-나보다-잘하는-사람이-많다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A3%BC%EC%9C%84%EC%97%90-%EB%82%98%EB%B3%B4%EB%8B%A4-%EC%9E%98%ED%95%98%EB%8A%94-%EC%82%AC%EB%9E%8C%EC%9D%B4-%EB%A7%8E%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;주위에 나보다 잘하는 사람이 많다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;주위에 나보다 잘하는 사람이 많다면&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;잘하는 상대의 타입에 따라 두 가지 중 하나를 권고한다. 대개 나이가 어린 사람에게는 꾸준함을 잃지 않고 일단 현재 영역에서 어느 정도 수준까지 도달할 것, 또 하나는 현재의 영역을 하고 싶거나 다른 잘 하는 영역과 융합해 볼 것을 이야기한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이미 방법론과 룰이 세팅되어 있는 영역에서는 경험과 실력이 더 많은 사람이 항상 또는 거의 이기게 되어있다. 그런 영역은 1등부터 꼴등까지가 명확하다. 자신이 앞서 있다면 몰라도 그렇지 않다면 기존의 게임의 룰을 따르기보다는 자신의 영역을 만드는 것이 나을 것이다. 자신의 영역은 어떻게 만들까? 완전히 새로운 것을 찾을 수도 있지만, 자신의 적당히 하는 것들을 적절히 융합해서 나만의 콘텐츠 및 개성을 찾을 수 있다. 그리고 보통 사람에겐 전자보다 후자가 더 현실적이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;배움의-기술&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%EC%9B%80%EC%9D%98-%EA%B8%B0%EC%88%A0&quot; aria-label=&quot;배움의 기술 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배움의 기술&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;배움이 가장 효과를 거두려면 운동이나 악기를 배우는 방식과 유사하다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;개략적인 큰 그림을 동영상이든 세미나를 통해 가능한 그 분야를 제일 잘 하는 분이나 잘 가르치는 분에게 배운다. 가능한 &apos;독학으로는&apos; 공부하지 않는다. 수영을 처음하는 사람이 수영을 배우기 위해 수영장이 아닌 수영 책을 섭렵하는 것은 그리 바람직하지 않다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;초기에는 실행 과정을 등록하여 그저 시키는 대로, 삶이나 일의 현장에서 실행하면서 꾸준히 피드백과 코칭을 받는다. 프로를 목표로 하지 않는 경우라도 최소 1~3년 정도는 꾸준히 해야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그러면서 어느정도 스스로 분별할 수 있는 수준이 되며 책이나 다른 사람들의 경험, 세미나를 선택하여 스스로 공부하고, 자신에게 접목한다. 열심히 읽고 배우고 다양하게 실험해본다. 그리고 초기에 배웠던 특정 스승을 떠난다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;기존 방식과는 다른 자신만의 차별화 방법을 만든다. 멘토의 말은 선별하여 듣는다. 꾸준히 책을 읽으며 감각을 유지하고 발전시킨다. 소비는 그만하고 생산한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이제 남들을 가르치면서 더 발전한다. 글도 열심히 쓰고 영향력을 확대한다. 타 영역도 배우면서 타 영역의 인사이트를 융합시킨다. 꾸준히 자신의 차별화를 강화시킨다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;좋지 않는 배움의 방식은,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;혼자 무작정 실행한다. 혼자 무작정 책만 보고 배우거나 주위 비슷한 수준의 친구를 보고 배우거나 자기 느낌대로만 실행하면 초기에는 빠르게 성장하는 것 같지만, 어느 시점 이후로 성장이 정체된다. 거리 싸움꾼이 일반인들은 이기겠지만, 제대로 배운 프로선수는 이기지 못한다. &quot;Street Smart&quot; 에 그친다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;유명한 분들만 찾아다니고 수동적인 공부만 하면서 지식만 잔뜩 쌓는 것도 안좋은 배움 방식이다. 방법만 찾고 막상 실험과 행동을 별로 안한다. 소비만 하고 생산은 별로 안한다. 남들의 방법만 쫓아다니고 지적 만족에 그치며, 막상 실험과 행동을 통해 자신만의 방법을 만들지 않는다. 예를들어, 남들이 말하는 &apos;돈 버는 법&apos; 백발 배우고 쫓아다녀야 돈 못 번다. &lt;strong&gt;배우고 실험해서 자신만의 &apos;법&apos; 을 만들어야 한다.&lt;/strong&gt; 이런 분들은 대개 머릿속에 온작 이론이 뭉쳐저있어 행동을 더 못한다. &apos;Book Smart&apos; 에 그친다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 말만 번지르르, 책만 읽는 수동적인 배움 태도는 성장하지 못한다. 적극적으로 자신만의 노하우 및 스킬을 찾아나가는 배움의 태도가 최고일 것 같다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;효과적으로-배우는-방법은-비효율적으로-배우는-것이다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%9A%A8%EA%B3%BC%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%9D%80-%EB%B9%84%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EA%B2%83%EC%9D%B4%EB%8B%A4&quot; aria-label=&quot;효과적으로 배우는 방법은 비효율적으로 배우는 것이다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;효과적으로 배우는 방법은 비효율적으로 배우는 것이다&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;수동적인 독서, 강의 듣기는 편하지만 한계가 분명하다. 기껏해야, 이해하거나 요약하는 정도의 머리밖에 쓰지 않는다. 떠먹여 주는 강연이나 설교는 이해나 요약조차 필요 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;실제 삶의 변화가 이루어지려면 뇌에 새겨져야 한다. 뇌에 새겨지려면, 스스로 몸과 머리를 쓰고 기록하고 실습하고 연습하고 자기가 이를 말로 이야기해봐야 한다. 그것도 반복해서 해야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&apos;귀찮음&apos; 과 &apos;지루함&apos; 을 이겨야 한다. 또한 &apos;돈과 시간의 소비&apos; 가 필요하다. 진짜 &apos;효과적&apos; 으로 배우는 방법은 &apos;비효율적&apos; 으로 배우는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;처음부터-제대로-가-민첩함-의-발목을-잡는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B2%98%EC%9D%8C%EB%B6%80%ED%84%B0-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EA%B0%80-%EB%AF%BC%EC%B2%A9%ED%95%A8-%EC%9D%98-%EB%B0%9C%EB%AA%A9%EC%9D%84-%EC%9E%A1%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;처음부터 제대로 가 민첩함 의 발목을 잡는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&apos;처음부터 제대로&apos; 가 &apos;민첩함&apos; 의 발목을 잡는다&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;어떤 분들은 &apos;처음부터 제대로&apos; 할 것이 아니라면 아예 실행하지 않는다. &apos;실수&apos; 하거나 &apos;실패&apos; 하려 하지 않는다. 특히, 지금까지 좋은 경력과 브랜드를 쌓아 왔을수록, 성공과 인정의 욕구가 강할수록 더더욱 그러하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;일단 작게 실험해보고 피드백을 받으며 발전시키는 것이 훨씬 효과적이고 효율적이다. 더더욱 변화가 빠르게 예측이 어려운 이 세상에서는 말이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&apos;부담 없는 작은 출발&apos; 그리고 &apos;반복&apos;, 이 과정 중의 &apos;피드백&apos; 의 &apos;지속적인 반영&apos; 이 완벽한 준비 부담으로 아예 출발조차 못하거나 무겁게 출발했다가 부담이 되어 지속하지 못하는 것보다 훨씬 낫다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 어쩌면 인생은 &apos;애자일&apos; 방법론을 따르면 살아야 하지 않을까? 부담없이 작은 출발을 하고, 피드백을 통해 점차 개선하는 모습은 마치 애자일 방법론을 떠오르게 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;낮은-수준의-생각-전략&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%82%AE%EC%9D%80-%EC%88%98%EC%A4%80%EC%9D%98-%EC%83%9D%EA%B0%81-%EC%A0%84%EB%9E%B5&quot; aria-label=&quot;낮은 수준의 생각 전략 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;낮은 수준의 생각 전략&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;중요한 일을 회피하는 자신을 합리화하고 안심을 주기 위해 쓸데없는 일을 열심히 하는 것을 &apos;낮은 수준의 생각 전략&apos; 이라고 명명한다. 중요하지만 하기 싫은 일이 있을 때, 높은 수준의 생각을 써야 하는 것이 귀찮고 힘들 떄, 단순한 일을 하고 낮은 수준의 생각을 함으로써 스트레스를 줄이고 자기 합리화를 하는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;자신이 바쁘다면 스스로에게 물어볼 필요가 있다. 내가 진짜 중요한 일에 바쁜가? 아니면 그것을 회피하기 위한 쉬운 일을 하면서 이게 그 중요한 일이 도움 될 거야라고 스스로를 합리화하고 있는 것은 아닌가?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;전략적-무능&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%EB%9E%B5%EC%A0%81-%EB%AC%B4%EB%8A%A5&quot; aria-label=&quot;전략적 무능 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전략적 무능&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&apos;전략적 무능&apos; 이라는 표현이란, 우리가 모든 것이 유능하려고 애쓸 필요가 없다는 것이다. 모든 것을 제대로 하고, 모든 것을 잘 하려고 하면 바쁘고 에너지가 한없이 든다. 그러므로 떄로 어떤 것들에는 &apos;무능&apos; 해질 필요가 있다는 것이다. 가장 가치있고 중요한 일에 초점을 맞추고 나머지는 전략적으로 대충 해도 된다는 뜻이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;일을 할 때도 내가 집중적으로 봐야 할 부분은 굉장히 깊고 꼼꼼하게 들어가지만, 그렇지 않은 부분은 대충 보는 것이 좋다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;몇 가지 영여겡서 &apos;무능&apos; 을 선택하면 의외로 시간이 많이 남는다. 그 시간에 자기가 원하고 필요한 것을 하면 된다. 세미나 또는 교육 과정을 참석하기도 하고 새로운 것을 배우기도 하며, 책도 읽고 글도 쓰고 페북도 하고 휴일에 침대에서 뒹굴거리기도 하는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;매우 바쁘게 살고 이를 좀 변화하고 싶다면, 한 번쯤은 거절하고 쳐낼 것, 좀 무능할 영역을 찾아보시라. 그거 안 한다고 죽지도 않고 큰 일 나지도 않는다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;그리-안-똑똑해도-엄청-똑똑하게-보이는-비결은&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%A6%AC-%EC%95%88-%EB%98%91%EB%98%91%ED%95%B4%EB%8F%84-%EC%97%84%EC%B2%AD-%EB%98%91%EB%98%91%ED%95%98%EA%B2%8C-%EB%B3%B4%EC%9D%B4%EB%8A%94-%EB%B9%84%EA%B2%B0%EC%9D%80&quot; aria-label=&quot;그리 안 똑똑해도 엄청 똑똑하게 보이는 비결은 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그리 안 똑똑해도 엄청 똑똑하게 보이는 비결은?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;일터에서 &lt;strong&gt;&apos;묻는 질문에 간략히 핵심만 대답하기&apos;&lt;/strong&gt; 만 잘해도 정말 똑똑해 보인다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;예를들어, &apos;그 방안의 장점과 단점을 말해주세요&apos; 라고 물었다면 &apos;그 방안의 장점은 ~ 이고, 단점은 ~ 입니다&apos; 이렇게 답함녀 되는데 이렇게 답하는 사람은 의외로 많지 않다. &apos;그 방안은 어떻고요. 그 방은 어떻게 만들어졌고요&apos; 질문과 관계없는 주변 가지들을 자꾸 말한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;질문에 대해서는 &lt;strong&gt;&apos;결론만 짧고 명확히 먼저 말한다&apos;&lt;/strong&gt; 그리고 시간이 남거나 상대가 이유를 요청하면, 근거가 되는 이유 3가지를 첫째, 둘째, 셋째 이렇게 말한다. 이렇게만 하면 엄청나게 똑똑한 사람으로 보인다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;그때-그걸-했어야-했는데-를-지금-하라&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%95%8C-%EA%B7%B8%EA%B1%B8-%ED%96%88%EC%96%B4%EC%95%BC-%ED%96%88%EB%8A%94%EB%8D%B0-%EB%A5%BC-%EC%A7%80%EA%B8%88-%ED%95%98%EB%9D%BC&quot; aria-label=&quot;그때 그걸 했어야 했는데 를 지금 하라 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&apos;그때 그걸 했어야 했는데&apos; 를 지금 하라&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;과거는 지나갔다. 후회나 평론은 아무 변화를 가져오지 못한다. 과거에 못했다면 교훈을 얻어 지금이라도 하면 된다. 과거는 바꾸지 못하지만 현재와 미래는 바꿀 수 있다. &apos;이랬어야 했는데&apos; 는 과거다. 과거는 과거다. 이랬어야 했는 걸 지금 행동하면 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;불가능한 일이 아니라면 지금 하면된다. 창피할지라도, 이랬다저랬다 한다고 욕을 먹더라도, 그냥 하면된다. 돌이키고 자백하고 사과하고 하면 된다. 그것이 진짜 똑똑한 사람이 하는 방식이다. 그런데 왜 이렇게 하기 어려울까? &apos;용기&apos; 가 부족하기 때문이다. 바꾸면 자존심도 상하고 욕도 먹게 되어있다. 과거의 결정이 잘못되었다고 스스로 인정하는 것이기 때문이다. 그걸 뛰어넘는게 &apos;용기&apos; 이다. 욕먹을 용기, 과거 잘못된 판단을 시인할 용기, 자존심을 꺾을 용기, 미움받을 용기, ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;진짜 똑똑한 살함은 &apos;용기 있는 사람&apos; 이다. 진짜 똑똑한 사람은 사과를 하고 변화를 만든다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;제대로-망치를-두드려라&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%A7%9D%EC%B9%98%EB%A5%BC-%EB%91%90%EB%93%9C%EB%A0%A4%EB%9D%BC&quot; aria-label=&quot;제대로 망치를 두드려라 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제대로 망치를 두드려라&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;열심히 땀을 뻘뻘 흘리며 일하는게 중요한 것이 아니다. 열심히 일하는데 아무 곳이나 망치를 두드리면 오히려 상황을 악화시킨다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;프로, 전문직이나 기업은 &apos;열심&apos; 이나 &apos;노력&apos; 으로 박수를 받는 것이 아니다. &apos;유능함&apos; 으로 박수를 받아야 한다. 끝없는 훈련과 개발, 경험으로 역량을 쌓아 문제의 본질을 찾아 해결해야 박수를 받는 것이다. 이것저것 아무거나 건드려서 시간을 빼앗과 비용과 에너지를 증가시키는 의사, 변호사, 경영자를 지지할 사람들은 거의 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;제대로 망치를 두드려야 한다. 우리 또한 제대로 망치를 두드리는 사람이 되도록 훈련하고 실력을 쌓아야 한다. 만일, 우리 자신이 부족하다면 그런 전문가를 찾아야 한다. 누가 제대로 망치를 두드리는 사람인가를 아는 능력 또한 제대로 망치를 두드리는 능력만큼이나 중요하다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;이미-지고-들어가는-말들&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%AF%B8-%EC%A7%80%EA%B3%A0-%EB%93%A4%EC%96%B4%EA%B0%80%EB%8A%94-%EB%A7%90%EB%93%A4&quot; aria-label=&quot;이미 지고 들어가는 말들 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이미 지고 들어가는 말들&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;가끔 예의 바른 직원이 자신의 의견을 제시하면서 이런 표현을 쓴다. &quot;외람될지도 모르는 말씀이오나...&quot; 그러나, 이러한 표현을 쓰는 순간 그 다음 그가 하는 말이 별거 아닐수도 있었는데, 이미 듣는이에게 긴장과 불쾌감을 주게 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;불필요한 말은 안하는게 낫다. &quot;제가 오늘 준비가 안되었지만요&quot;, &quot;제가 오늘 화장이 잘 안 먹어서요&quot;, &quot;제가 떨려서요&quot; 다 불필요한 말이다. 몰론 이런 말을 하는 심리적 이유가 있다. 진짜 겸손의 표현일 수도 있지만, &lt;strong&gt;실패할 경우를 대비하여 변경거리를 미리 만드는 것일 수도 있다.&lt;/strong&gt; 이렇게 해야 심리적 안정이 될 수 있다. 신경 쓰지 않으면 본능적으로 이런 말을 하게 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;그러나 불행히도 그런 말을 하는 순간 살함들은 그 프레임 속에서 당신을 보기 시작한다. 화장을 관찰하고 긴장.떨림을 관찰한다.&lt;/strong&gt; 내가 왜 나보다 못난 녀석에게 이야기를 들어야하지? 라고 여기게 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그냥 하시라. 대부분은 당신의 화장이 잘 먹었는지, 긴장되었는지 모두 관심없다. 발표를 하고 강의를 하고 건의를 하고 부탁을 하고 실례를 하려면 그냥 자신있게 해라.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;속독이냐-정독이냐&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%86%8D%EB%8F%85%EC%9D%B4%EB%83%90-%EC%A0%95%EB%8F%85%EC%9D%B4%EB%83%90&quot; aria-label=&quot;속독이냐 정독이냐 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;속독이냐? 정독이냐?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;의지력이 강하지 않는 보통 사람들은 기본적으로 &apos;속독&apos; 하고, 마음에 들면 이후 &apos;정독&apos; 하는게 효과적이다. 의지력이 강하거나 책에 쉽게 몰입하는 분들은 처음부터 정독해도 좋으나, 일반인들은 그렇게 접근했다가는 단 한 권도 책 끝까지 못 읽는다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그러므로 일단, 대충 쓱쓱 읽으면서 주요 부분한 밑줄 친다. 날을 정해서 가능한 몇 시간 내 단숨에 끝까지 읽는다. 그리고 별로면 거기서 끝내고, 정말 좋으면 다시 읽어라.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;결론은, 당신이 집중력이 강하고 끈기가 있는 비범한 사람이라면 처음부터 정독하라. 그러나 의지박약에 약한 기억력의 소유자라면 대충 속독하여 일단 끝까지 읽어라. 그리고 정말 좋은 책이라 생각하면 다시 읽어라. 정독할 능력이 안된다면 굳이 처음부터 정독하려 애쓸 필요없다. 또한 다양한 많은 책을 읽으려면 일단 속독하라.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 나는 기억력이 좋지않고, 흥미가 안 붙으면 머리속에 정리도 안되고 기억도 안남는다. 이 떄문에 책 안의 내용이 친숙해지고 흥미로워 질 떄까지 n회 속독을 한다. 앞으로도 계속 고집 피우지말고 속독 스타일로 공부, 독서를 해볼까 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;피드백을-회피하지-말자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%BC%EB%93%9C%EB%B0%B1%EC%9D%84-%ED%9A%8C%ED%94%BC%ED%95%98%EC%A7%80-%EB%A7%90%EC%9E%90&quot; aria-label=&quot;피드백을 회피하지 말자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;피드백을 회피하지 말자&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;피드백이 제대로 효과를 발휘라혀면 아래 4가지가 충족되어야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;자신이 피드백을 받으려는 마음이 있어야 한다 : 그러나 불행히도 피드백을 받길 좋아하는 사람은 사실상 없다. 인간 본성상 누구에게 쓴 말을 듣고 싶은 사람은 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;피드백을 주는 사람에 대한 신뢰가 있어야 한다 : 이 또한 쉽지 않다. 대부분은 리더에 대해 신뢰가 부족하다. 신뢰하지 않는 리더나 코치가 주는 피드백은 꼰대의 잔소리나 간섭, 질책으로 밖에 안 들린다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;피드백을 주는 사람이 제대로 된 구체적인 피드백을 줄 정도로 실력이 있어야 한다&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;피드백을 주는 사람이 그 피드백을 잘 전달해야 한다&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&apos;피드백&apos; 이 없으면 절대 발전이 없다. 천재가 아닌 이상 혼자의 방식대로 수영하고 바둑을 둬서는 제대로 배우고 피드백 받은 사람들을 이길 수 없다. 마찬가지로 일을 하거나 사업을 할 때, 훌륭한 코치를 찾아 제대로 된 피드백을 받아 성장하는 것이 효과적이고 효율적인 길임이 분명하다. 그러하니 누구든지 좋은 코치를 찾아야 하고, &apos;피드백&apos; 받는 것을 회피하거나 두려워하지 말자.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;즐긴다-는-말의-허상&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A6%90%EA%B8%B4%EB%8B%A4-%EB%8A%94-%EB%A7%90%EC%9D%98-%ED%97%88%EC%83%81&quot; aria-label=&quot;즐긴다 는 말의 허상 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&apos;즐긴다&apos; 는 말의 허상&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;즐기면서 최고가 될 수 있는 사람은 아무도 없다. 결과는 즐길 수 있겠지만 그 과정은 즐길 수 있는 성질의 것이 아니다. (몰론, 순간순간 성취감에 즐거움을 느낄 수 있겠지만) 고통을 극복하고 최고의 위치에 오른 후 되돌아보니 과거의 고통이 미화되고 성취감과 승리감의 기억에 즐겼다고 오해하는 것일 가능성이 높다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그러므로 &apos;즐기면서 해라&apos; 라는 말은 취미생활 정도를 목표로 하는 이들에게나 할 수 있는 조언이지, 최고를 지향하는 이들에게 할 수 있는 조언은 아니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;약점이-강점이-된다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%BD%EC%A0%90%EC%9D%B4-%EA%B0%95%EC%A0%90%EC%9D%B4-%EB%90%9C%EB%8B%A4&quot; aria-label=&quot;약점이 강점이 된다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;약점이 강점이 된다&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;자신의 치명적 약점을 생각해 보면, 의외로 그 약점 덕분에 다른 부분이 발달되었거나 성장할 수 있음을 발견하게 된다. 치명적인 약점을 극복하는 과정에서 오히려 강점이 된 것이 있을 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;yes-는-yes-이고-no-는-no-이다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#yes-%EB%8A%94-yes-%EC%9D%B4%EA%B3%A0-no-%EB%8A%94-no-%EC%9D%B4%EB%8B%A4&quot; aria-label=&quot;yes 는 yes 이고 no 는 no 이다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Yes 는 Yes 이고 No 는 No 이다&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;삶에서 어려운 것 중 하나는 Yes 와 No 를 분명히 하지 못하는 것이다. Yes 도 No 도 아닌 채 어중간하게 유지하는 경우들이 종종 있다. 이 이유는 정말 마음속에서 분명한 선택을 못해서인 경우도 있지만, 마음속에 선택을 했음에도 불구하고 상대를 실망시키거나 상대에게 싫은 말을 하지 않기 위해서 질질 끄는 경우가 많다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그런데, 이렇게 미루는 것은 결과적으로 서로에게 더 큰 피래를 주는 경우가 많은 듯 하다. 상대에게 희망고문이 되는 경우가 많다. 빠르게 &apos;No&apos; 라고 하면, 깨끗하게 포기하고 다른 대안을 찾아 나서면 되는데 이도 저도 아니면 끝없이 기다리고 자원을 써야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그러므로 중간은 없다. Yes 는 Yes 라고 하고, No 를 No 라고 하는 것이 결과적으로 서로에게 낫다. Yes 를 Yes 라, No 를 No 라 명확히 못하는 사람은 &apos;마음이 여린&apos; 사람이 아니다. &apos;자기중심적인&apos; 사람일 뿐이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 나도 종종 이럴때가 있다. 결정에 있어 빠르게 판단하고, 의사를 확실하게 밝혀서 상대에 대해 배려를 해주자. 시간을 너무 오래 끌어서 자기중심적인 사람이 되지 않도록 노력하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;가장-나쁜-핑계--나보다-잘하는-사람이-얼마나-많은데요&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%80%EC%9E%A5-%EB%82%98%EC%81%9C-%ED%95%91%EA%B3%84--%EB%82%98%EB%B3%B4%EB%8B%A4-%EC%9E%98%ED%95%98%EB%8A%94-%EC%82%AC%EB%9E%8C%EC%9D%B4-%EC%96%BC%EB%A7%88%EB%82%98-%EB%A7%8E%EC%9D%80%EB%8D%B0%EC%9A%94&quot; aria-label=&quot;가장 나쁜 핑계  나보다 잘하는 사람이 얼마나 많은데요 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;가장 나쁜 핑계 : 나보다 잘하는 사람이 얼마나 많은데요&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&apos;나보다 잘하는 사람이 얼마나 많은데요&apos; 이 생각은 자신을 평가절하하고 새로운 시도를 스스로 제한하는 가장 나쁜 핑계 중 하나이다. 가장 노래 잘하는 가수가 가장 유명하고 부를 얻는게 아니다. 가장 노래 잘하는 사람은 보이스 코칭을 하거나 미사리에서 어렵게 어렵게 돈을 벌고 있다. 노래는 별로인데 특별한 매력을 가진 이들이 돈도 벌고 인기도 얻는다. 지신만의 독특함을 찾는게 더 중요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;45세-때부터였다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#45%EC%84%B8-%EB%95%8C%EB%B6%80%ED%84%B0%EC%98%80%EB%8B%A4&quot; aria-label=&quot;45세 때부터였다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;45세 때부터였다&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&apos;논리적이고 체계적으로 생각하는 가장 좋은 방법&apos; 은 무엇일까? 이 방법 중 하나는 바로 자신의 생각을 &apos;글로 쓰는 것&apos; 또는 &apos;그림으로 그리는 것&apos;이다. 자신의 머릿속의 생각을 글로 쓰거나 그림으로 표현해 보면 생각을 더욱 정교화 할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&apos;힘든 일이 있거나 감정이 복잡할 떄 해결할 수 있는 방법&apos; 은 무엇일까? 이 또한 &apos;쓰는 것&apos; 이 가장 좋은 방법이다. 헝클어진 생각들은 다루기 힘들다. 이를 글로 정리하면 이제 머릿속의 생각이 지면으로 옮겨진다. 머릿속은 내가 다루기 어렵지만, 언어화된 글은 내가 다룰 수 있다. 글을 써보면 논리적 오류도 찾을 수 있고 생각의 변화를 가져갈 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;-2장-성공--조직을-성공으로-이끄는-리더쉽의-발견&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-2%EC%9E%A5-%EC%84%B1%EA%B3%B5--%EC%A1%B0%EC%A7%81%EC%9D%84-%EC%84%B1%EA%B3%B5%EC%9C%BC%EB%A1%9C-%EC%9D%B4%EB%81%84%EB%8A%94-%EB%A6%AC%EB%8D%94%EC%89%BD%EC%9D%98-%EB%B0%9C%EA%B2%AC&quot; aria-label=&quot; 2장 성공  조직을 성공으로 이끄는 리더쉽의 발견 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;📚 2장. 성공 | 조직을 성공으로 이끄는 리더쉽의 발견&lt;/h2&gt;
&lt;h3 id=&quot;왜-훌륭한-목표와-전략이-실행에서-실패할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-%ED%9B%8C%EB%A5%AD%ED%95%9C-%EB%AA%A9%ED%91%9C%EC%99%80-%EC%A0%84%EB%9E%B5%EC%9D%B4-%EC%8B%A4%ED%96%89%EC%97%90%EC%84%9C-%EC%8B%A4%ED%8C%A8%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;왜 훌륭한 목표와 전략이 실행에서 실패할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 훌륭한 목표와 전략이 실행에서 실패할까?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;한 연구소는 50만명 이상의 리더와 팀을 대상으로 조사하여 목표와 전략이 실행단계에서 실패하는 4가지 근본 원인이 있음을 발견했다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;85% 의 응답자는 조직의 목표를 몰랐다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;85% 의 응답자는 조직의 목표를 이루기 위해 자신이나 자신의 조직이 할 일을 명확히 몰랐다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;85% 의 응답자는 회사가 가장 중요해 하는 목표와 관련해서 성공하는지 실패하는지 몰랐다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;79% 는 목표 진행에 있어 자신의 명백한 책임을 몰랐다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;최고의-실행이-안먹히는-이유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B5%9C%EA%B3%A0%EC%9D%98-%EC%8B%A4%ED%96%89%EC%9D%B4-%EC%95%88%EB%A8%B9%ED%9E%88%EB%8A%94-%EC%9D%B4%EC%9C%A0&quot; aria-label=&quot;최고의 실행이 안먹히는 이유 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;최고의 실행이 안먹히는 이유&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;우리가 책에서 본 최고의 실행을 도입할 때 주의할 것 중 하나는 Context 를 놓치면 안 된다는 것이다. 그런데 책이나 강연에는 이 Context 가 없다. 저자들이나 강연자들 또한 다른 회사들의 사정을 알 리가 없다. 무작정 듣고 읽고 뱅룬다고 다른 회사의 성과가 복제되는 것은 아니다. 많은 리더들이 이 Context 를 읽지 못한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;예를들어, 대부분의 실리콘밸리 기업이 자율을 추구할 수 있는 이유는 구성원들의 역량이 있기 때문이다. 완전한 자율과 위임은 구성원들의 역량이 있고 목표가 명확할 때 가능하다. 이 가정을 보지 않고 겉에 드러난 &apos;자율&apos; 만을 이식하려고 하면 어려움이 생기는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그러므로 중요한 것은, 자신과 자신의 조직에 대한 이해가 선행되어야 한다. 그렇지 않은 상ㄱ태에서는 아무리 멋진 실행도 맞지 않는 옷이 되는 것이다. 그렇다고 포기하고 예전의 방식을 고수하라는 것은 아니다. 당연히 훌륭한 철학과 실행을 도입하고 변화를 만들어내야 하지만, 자신의 회사가 가지고 있는 역량과 문화에 맞게 조율해야 한다는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;우리를-성공하게-한-비결이-우리를-실패하게-할-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%B0%EB%A6%AC%EB%A5%BC-%EC%84%B1%EA%B3%B5%ED%95%98%EA%B2%8C-%ED%95%9C-%EB%B9%84%EA%B2%B0%EC%9D%B4-%EC%9A%B0%EB%A6%AC%EB%A5%BC-%EC%8B%A4%ED%8C%A8%ED%95%98%EA%B2%8C-%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;우리를 성공하게 한 비결이 우리를 실패하게 할 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;우리를 성공하게 한 비결이 우리를 실패하게 할 수 있다&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;자신을 성공으로 이끈 개인 성향 또는 조직의 원칙, 문화가 실패로 이끌 수도 있다. 논리나 데이터를 중시하는 분은 그것이 반드시 최상의 결과를 가져오지 않을 수 있다는 것을 받아들일 필요가 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;복잡한 상황, 문제를 대하는 가장 좋은 전략은? 바로 &apos;유연성&apos; 이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;힘들게-하는-상사를-어떻게-대해야-하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%9E%98%EB%93%A4%EA%B2%8C-%ED%95%98%EB%8A%94-%EC%83%81%EC%82%AC%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8C%80%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;힘들게 하는 상사를 어떻게 대해야 하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;힘들게 하는 상사를 어떻게 대해야 하는가?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;상사와 잘 지내기 위해서는 근본 원리와 기술이 필요하다. 근본 원리는 &apos;신뢰&apos; 이다. 신뢰가 형성되지 않으면 상사와 함께 일하기 어렵다. 신뢰를 얻기 위해서는 1) 일에서 크고 작은 성공들을 쌓고 2) 성실과 충성을 통해 믿을만한 모습을 보이고 3) 인간적 친밀감을 쌓아 나가는 것 외에 길이 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&apos;그는 어떻게 그 모든 일을 해내는가&apos; 책을 보면 &apos;상사를 대하기 위한 좋은 기술&apos; 들이 아래와 같이 접근법을 제시하고 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;상사의 스타일을 파악하고 맞춘다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;간섭하는 상사에게는 작은 프로젝트를 통해 신뢰를 얻어라. 상사가 묻기전에 먼저 보고하라. 이런 유형은 안심이 될수록 간섭은 줄어든다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;상사의 의사소통 방식, 업무 스타일에 맞춘다. 메일을 좋아하면 메일로, 대면 보고를 좋아하면 대면으로 하라.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;상사가 어떤 유형이든 당신이 매번 과제를 성공한다면 환영을 받는다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;프로젝트를 성공한다는 것과 그걸 알린다는 것은 다르다. 요청하지 않아도 시의적절하게 성과를 알려야 한다. 인사고과 시즌은 당신이 겸손할 때가 아니다. 가만히 있는데 알아서 점수를 잘 주는 상사는 거의 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;프로젝트가 실패할 것 같으면 빨리 알려라. 지연되다가 갑자기 안좋은 소식을 듣고 반길 상사는 아무도 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;문제 보고 시에는 창의적 해결책도 같이 보고하라.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;상사와의 신뢰 구축은 오래 걸리지만 무너지는 것은 한순간이다. 절대 동료들과 상사 뒷담화를 하지마라. 다 귀에 들어간다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;상사에게 반대 의견을 제시해도 최종 결론은 상사의 것이며 따를 것임을 분명히 한다. 근거 자료와 대안을 준비하라. 절대 감정적으로 맞서거나 상사의 능력을 비하하지 마라.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;새로운-일을-꺼리고-저항하는-이유는-싫어서가-아니라-몰라서이다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%88%EB%A1%9C%EC%9A%B4-%EC%9D%BC%EC%9D%84-%EA%BA%BC%EB%A6%AC%EA%B3%A0-%EC%A0%80%ED%95%AD%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0%EB%8A%94-%EC%8B%AB%EC%96%B4%EC%84%9C%EA%B0%80-%EC%95%84%EB%8B%88%EB%9D%BC-%EB%AA%B0%EB%9D%BC%EC%84%9C%EC%9D%B4%EB%8B%A4&quot; aria-label=&quot;새로운 일을 꺼리고 저항하는 이유는 싫어서가 아니라 몰라서이다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;새로운 일을 꺼리고 저항하는 이유는 싫어서가 아니라 몰라서이다&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;사람들이 저항하는 이유는 &apos;익숙함을 벗어나는 것에 대한 두려움&apos; 이다. 새로운 것을 잘 못화면 뒤쳐질 것 같은 두려움 때문에 저항하는 것이다. 혁신적인 10~20% 는 누가 말 안해도 스스로 배우지만, 중간에 있는 다수는 염려하고 저항한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;만약 새로운 것을 팀과 도전하고 싶다면, 방법은 2가지가 있다. 하나는 끊임없이 이야기한다. 만날 때고 하고, 메일로도 하고, 전체로 모여도 하고, 그룹으로 모여도 한다. 일 년 내내 이야기한다. 이려면, 대부분의 구성원들이 새로운 변화의 용어를 스스로 자연스럽게 말하게 된다. 두 번째는 배울 수 있도록 도와준다. 말만 백날하고 토론만 백날 해서는 단 하나도 변화하지 않는다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;learn-it-all-은-know-it-all-을-이긴다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#learn-it-all-%EC%9D%80-know-it-all-%EC%9D%84-%EC%9D%B4%EA%B8%B4%EB%8B%A4&quot; aria-label=&quot;learn it all 은 know it all 을 이긴다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Learn it all 은 Know it all 을 이긴다&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&quot;Learn it all 은 Know it all 을 이긴다&quot; 이 말은 &lt;strong&gt;아는체 하지 말고 겸손하게 모른다는 것을 인정하고, 매일이 새로운 날이 되도록 배우고 탐구하고 실험하라는 것이다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;회사를 다닌 기간이 중요한 것이 아니다. 3년 경력만으로도 10년 경력보다 잘 하는 사람들을 종종 보았다. 중요한 것은 얼마나 배우고 성장했는가이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;모르는 것을 인정하고 끊임없이 배움을 추구하는 사람은, 자신은 이미 모든 것을 알고 있다고 착각하는 사람을 이긴다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;우리가-진실이라고-받아들이는-가정이-때로-우리를-한계-짓는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%B0%EB%A6%AC%EA%B0%80-%EC%A7%84%EC%8B%A4%EC%9D%B4%EB%9D%BC%EA%B3%A0-%EB%B0%9B%EC%95%84%EB%93%A4%EC%9D%B4%EB%8A%94-%EA%B0%80%EC%A0%95%EC%9D%B4-%EB%95%8C%EB%A1%9C-%EC%9A%B0%EB%A6%AC%EB%A5%BC-%ED%95%9C%EA%B3%84-%EC%A7%93%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;우리가 진실이라고 받아들이는 가정이 때로 우리를 한계 짓는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;우리가 진실이라고 받아들이는 가정이 때로 우리를 한계 짓는다&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;한 영역에서 오래 일한 분들은 그 업무를 매우 잘 이해하고 전문성이 있음에 틀림없다. 그런데 흥미롭게도 이들 대부분은 더 큰 애벌레가 되는 &apos;변화&apos; 는 훌륭하게 해내지만, 애벌레가 나비가 되는 &apos;변신&apos; 을 생각하는 데 있어서는 매우 힘들어한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;왜 그럴까?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;첫쨰, 기존의 &apos;가정&apos; 을 고수하고 있다. 어느 업무나 &apos;이래야 한다&apos; 는 가정이 있다. &apos;서커스는 동물이 있어야 하고, 프라이드 감자는 기름에 튀경야 하고, 음악은 CD 로 들어야 한다&apos; 는 식의 널리 받아들여진 가정이다. 일도 마찬가지 일 것이다. 어떤 것을 가정하고 오랫동안 일 하다보면, 어느새 그 가정을 절대 깨어질 수 없는 &apos;법칙&apos; 이 된다. 이에 그 가정을 넘어서는 생각을 하지 못한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;둘째, &apos;비효율의 숙달화&apos; 이다. 불편하고 비효율적인 일도 자꾸 하다보면 숙달되어 편해진다. 편해지면 그게 바꾸어야 할 것이라는 것을 못 느낀다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;해결 방법은 무엇일까?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;경험자들은 자신의 눈에 &apos;맹점&apos; 이 있을 수 있다는 것을 인정해야 하고, 자신이 고수하고 있는 가정이 무엇일지 생각해 본다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;고객, 외부에서 온 직원, 신입 직원, 외부인들의 관점을 무시하지 말고 들을 필요가 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;자신과 다른 업종의 혁신을 참조하고, 새로운 관점을 얻기 위해 책을 읽고 공부해라.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;좋은-회사란-무엇인가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A2%8B%EC%9D%80-%ED%9A%8C%EC%82%AC%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot; aria-label=&quot;좋은 회사란 무엇인가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;좋은 회사란 무엇인가?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;아마존에서 10년 이상 근무한 한국 청년이 아마존에 대해 이상적인 좋은 평가를 하지 않았다. 그의 평은 이러했다. &apos;성장할 수 있지만 버터기 힘든 회사, 괴롭지만 배울 점이 있는 곳, 행복하지 않은 곳, 다시 돌아가고 싶지는 않은 곳&apos; 한마디로 말하면 &apos;훈련소&apos; 같은 느낌이다. 만약, 성장을 중요시하는 직원이었다면 이 회사에 대해 반대로 매우 긍정적인 평가를 했을 가능성이 높다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;무엇이 반드시 성공하는 회사 문화라고, 더 좋은 문화라고 단정할 수 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;직원은 자신의 철학과 가치에 일치하는 기업을 찾고 선택하고 노력해야 한다. 개인생활과 자유가 중요한 가치인 직원은 그런 철학을 가진 회사에, 훈련과 성장이 중요한 가치인 직원은 그런 철학을 가진 회사에 가는 것이 좋다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;좋은 회사란 남들이 좋다고 하는 회사가 아니라 자신의 가치와 철학에 맞는 회사이다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;모르는-걸-모른다고-말할-용기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A8%EB%A5%B4%EB%8A%94-%EA%B1%B8-%EB%AA%A8%EB%A5%B8%EB%8B%A4%EA%B3%A0-%EB%A7%90%ED%95%A0-%EC%9A%A9%EA%B8%B0&quot; aria-label=&quot;모르는 걸 모른다고 말할 용기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;모르는 걸 모른다고 말할 용기&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;마이그크로소프에서는 실패하면 그 직원은 천재가 아니라고 간주하며 낮은 평가를 주거나 해고하는 문화가 있었다. 그래서 이들은 잘 묻지도, 정보를 공유하지도 않았다. 다들 아는체 했다. 실패하거나 모르면 평가절하되기 때문이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그리고 목표를 낮게 설정했다. 높게 했다가 멍청이라고 비난을 받기 때문이다. 실패를 두려워하고 작은 성공에 목매고 모르는 것을 두려워했다. 부서 간에 서로 정보를 감추었다. 당연히 회사는 점점 쇠락했다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;새롭게 CEO 로 부임한 시티아 나델라는 이렇게 말했다. &quot;저는 이 기술을 몰라요. 설명을 해주세요&quot; 그러고는 이런 말을 했다. &lt;strong&gt;&quot;모른다는 것과 실패 했다는 것은 멍청하다는 의미가 아닙니다. 성장을 의미하는 것입니다.&lt;/strong&gt; 우리에겐 천재가 필요한게 아니라 서로 협력하는 팀이 필요합니다.&quot; 그러자 그동안 천재 흉내를 내었던 임직원들은 실패를 두려워하지 않고 높은 목표를 설정하고 정보를 오픈했다. 모르는 것은 솔직하게 서로 묻고 답했다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;모르는 것은 모른다고 하는 데에는 상당한 용기가 필요하다. 그러니 용기를 내어 솔직하게 말하자. 그리고 배우고 귀 기율여야 한다. 이것은 자신의 무능을 드러내는 것이 아니라 배움과 성장을 만드는 기회이다. &apos;모르는 것을 모른다고 하는 것&apos; 이것이 성장의 출발점이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 책을 읽으면서 나에게 가장 교훈이 된 것 같다. 난 아직도 모르는 것을 모른다고 잘 말하지 못하고 있다. 나에겐 아직 용기가 없다. 용기를 내어서, 모른다는 것을 솔직하게 말해야 겠다. 모른다는 것은 성장할 수 있는 기회라는 것을 항상 새기고 있자. 모르는 것을 바로 모른다고 용기있게 말하는데는 아직 습관화되기 전까지는 시간이 걸리겠지만, 그 전까지는 천재인척 모르는 것이 없으며 성장하지 않으려는 태도를 보인다면, 그때 내 스스로를 부끄러워 하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;상대가-진짜-똑똑한지-허풍인지-구별하는-방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%81%EB%8C%80%EA%B0%80-%EC%A7%84%EC%A7%9C-%EB%98%91%EB%98%91%ED%95%9C%EC%A7%80-%ED%97%88%ED%92%8D%EC%9D%B8%EC%A7%80-%EA%B5%AC%EB%B3%84%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;상대가 진짜 똑똑한지 허풍인지 구별하는 방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;상대가 진짜 똑똑한지 허풍인지 구별하는 방법&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;아마존의 제프 베조스는 자신이 같이 일하고자 하는 &apos;똑똑한 사람&apos; 의 기준을 다음과 같이 일한다. &apos;가장 똑똑한 사람들은 끊임없이 자신의 이해를 수정한다. 그들은 이미 해결했던 문제들에 대해서도 다시 고려해본다. 그들은 기존 사고에 대항하는 새로운 관점, 정보, 생각, 모순, 도전 등에 대해 열려있다. 자신의 예전 생각이 잘못되었다면 언제든 바꾼다&quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;새로운 정보가 들어왔을 때 자신의 기존 의견 및 의사결정을 바꾸는 것을 &apos;자신이 틀렸고 패배했고 어리석었다&apos; 라는 마인드로 생각해버리지 말자.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;몰론, 반드시 지켜야 할 가치와 원칙은 흔들리지 않고 고집스럽게 지켜나가는 것이 중요하다. 또한, 자신의 의견을 바꾸라는 것은 아무 생각도 줏대도 없이 남의 말에 혹해서 정신없이 이랬다저랬다 하라는 의미는 아니다. 자신의 의견은 가지고 있되 가정과 사실과 환경이 바뀌면 과감히 변경하라는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Spring Batch 개요]]></title><description><![CDATA[회사에서 Spring Batch 를 사용하는데, 아직 이해도가 낮아서 한번 공부해 볼 필요가 있다고 느꼈다. 이번 포스팅에선 스프링 배치를 왜 사용하고, 내부 아키텍처 동작 및 구성방식이 어떻게 이루어지는지에 대해 학습해보도록 한다. Batch…]]></description><link>https://haon.site/spring-batch/batch-basic/</link><guid isPermaLink="false">https://haon.site/spring-batch/batch-basic/</guid><pubDate>Tue, 09 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/67fe11dba45a82b2b8205ef838dd8b57/7be33/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.717791411042946%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABa0lEQVR42q1RyUoDQRCdT/If/AeP4sm7+AOexA9Q8OpFEdQIIkEJkkQxMQcxiGhC9m2WbDPTs3TP9uzumBjFozM8XtdUddWrN0qSJPivR/RSoiSE4XQw9TQ4zMTU1zF2B5h46jdc9Wf8B4ZOF2HMoNDQhenpvHuM2ugFPo/lNMQciTzNX3lOZt9jwRKzDS1/CC+woYiuXbOKg8IWNlMr2MttQCMteZmFHuRA3+CDCIKIIYrDBbOIyhoW+SB0Aj8gUOIkwptWwO7dOrbTq9jJrOG+cYH0+yEemmcoda5RbF8hWz9BvnGKD72IbO0YmeoRHpuXqBglPPdu5No0dKCI7gbp4Kl1i/PyPnK1FJc/Rs+soG9VpTKX2RwWXtUcRm5PXtaJ8F3nnht8VfK1MlcoJKt2nRuvodzP88K2lO+wqWRROIeIbToGYUt5OpI8sOqzlYVXAfci5L5wh/mfCqQnc4hcsGD6K7dcQ6Xvn9xhWzc0Q8l3AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/67fe11dba45a82b2b8205ef838dd8b57/a6d36/image.png&quot;
        srcset=&quot;/static/67fe11dba45a82b2b8205ef838dd8b57/222b7/image.png 163w,
/static/67fe11dba45a82b2b8205ef838dd8b57/ff46a/image.png 325w,
/static/67fe11dba45a82b2b8205ef838dd8b57/a6d36/image.png 650w,
/static/67fe11dba45a82b2b8205ef838dd8b57/e548f/image.png 975w,
/static/67fe11dba45a82b2b8205ef838dd8b57/3c492/image.png 1300w,
/static/67fe11dba45a82b2b8205ef838dd8b57/7be33/image.png 1558w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;회사에서 Spring Batch 를 사용하는데, 아직 이해도가 낮아서 한번 공부해 볼 필요가 있다고 느꼈다. 이번 포스팅에선 스프링 배치를 왜 사용하고, 내부 아키텍처 동작 및 구성방식이 어떻게 이루어지는지에 대해 학습해보도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;batch&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#batch&quot; aria-label=&quot;batch permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Batch&lt;/h2&gt;
&lt;p&gt;스프링 배치에 대해 학습하기전에, Batch 라는 그 자체의 개념에 대해 제대로 이해하고 넘어가보자. 종종 애플리케이션에서 필수적인 작업으로 배치 프로세싱이 요구된다. 그런데 여기서, 배치란 무슨 뜻일까? 배치는 &lt;code class=&quot;language-text&quot;&gt;집단, 무리, 함께 묶다&lt;/code&gt; 라는 뜻을 가진다. 다시 정리하면, 배치는 하나의 큰 무리.집단 단위를 의미하며, 대부분 그 규모가 큰 것들을 뜻한다. 그래서 Batch Job 이라고 하면 개발자가 정의한 작업을 한번에 일괄 처리하는 애플리케이션 Job 이라고 정의할 수 있다.&lt;/p&gt;
&lt;p&gt;예를들어, 한 기업에서 할인 이벤트를 매일매일 진행한다고 해보자. 회원가입된 유저 수는 100만건이며, 매일마다 유저들에게 새로운 할인 이벤트 소식을 전달해야한다. 그러면 이 할인 이벤트 소식을 100만건의 유저에게 어떻게 하루마다 매일 보낼 수 있을까?&lt;/p&gt;
&lt;p&gt;이 작업은 기존의 일반적인 웹 애플리케이션을 개발.처리하던 방식과는 다른 처리 방식을 가져야한다. &lt;strong&gt;유저와의 상호작용이 없으며, 대용량 데이터, 복잡한 프로세스, 반복적이고 주기적인 프로세스를 가진다는 특징&lt;/strong&gt;이 있기 때문이다. 이런 상황말고도, 월말 정산을 처리한다거나 따로 적재된 데이터베이스를 통합하는 작업, 은행 점검 상황등이 있을 것이다. 이런 상황에서 처리하면 되는 작업이 바로 배치이다. 배치 Job 은 사용자가 개입하지 않는 환경에서 특정 완료 지점까지 실행된다.&lt;/p&gt;
&lt;h3 id=&quot;batch-처리시-고려사항&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#batch-%EC%B2%98%EB%A6%AC%EC%8B%9C-%EA%B3%A0%EB%A0%A4%EC%82%AC%ED%95%AD&quot; aria-label=&quot;batch 처리시 고려사항 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Batch 처리시 고려사항&lt;/h3&gt;
&lt;p&gt;Batch 가 없는 일반적인 애플리케이션 개발 상황에서는 고려사항이 꽤나 많다. 트래픽 및 리소스 사용률 급증, 보안 검증, 데이터 Validation 처리 등등 그 모든 것들을 유저를 고려한 소프트웨어 개발이 필요하다.&lt;/p&gt;
&lt;p&gt;그와 달리 배치는 위 사항들을 고려할 필요가 없다. &lt;strong&gt;즉, 배치를 사용.처리하는 상황에서는 유저를 고려한 설계.개발 처리가 필요 없다는 것이다.&lt;/strong&gt; 배치를 사용할 때는 확실한 로그와 피드백용 알림만을 사용해서 정확하게 에러를 인지하고, 복구.처리하는 것에만 집중하면 된다. 몰론 보안이나 데이터 유효성을 처리하기야 해야 겠지만, 트래픽 사용량이나 사용자 중심의 에러 처리를 고려하지 않아도 된다는 뜻이다.&lt;/p&gt;
&lt;h2 id=&quot;spring-batch&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#spring-batch&quot; aria-label=&quot;spring batch permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Spring Batch&lt;/h2&gt;
&lt;p&gt;Spring Batch 는 Enterprise 시스템의 일상적인 운영에 필요한 배치 애플리케이션을 쉽게 개발할 수 있도록 구현된 라이브러리이다. 제공하는 기능으로는 크게 &lt;strong&gt;로깅/추적, 트랜잭션 관리, 작업 처리 통계, 작업 플로우 지정(작업을 재시작하거나 스킵), 리소스 관리 등 대량으 레코드를 처리&lt;/strong&gt; 하는데 필수적인 기능을 재사용 가능한 형태로 제공한다.&lt;/p&gt;
&lt;p&gt;여기서 주목할 점은, &lt;code class=&quot;language-text&quot;&gt;재사용&lt;/code&gt; 가능하다는 점이다. 1달에 1번씩 필요한 배치 프로세스를 오늘 개발한 뒤에, 다음 달에 다시 자동으로 배치 프로세스를 돌릴 수 있다는 뜻이다. 또한 스프링 배치는 Java 기반 Configuration 이 가능하며, XML 없이도 POJO 기반 개발 접근 방식으로 개발 가능하다.&lt;/p&gt;
&lt;h3 id=&quot;vs-quartz-schduler&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#vs-quartz-schduler&quot; aria-label=&quot;vs quartz schduler permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;vs Quartz, Schduler&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f1b187b190346a39d48a953e8d8400c9/e8814/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 31.901840490797547%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAABLElEQVR42o1QbUvCYBT1/3/sS1GUUBRogeVQwdRkGzrMaaJjWWu+pTPfNq10umc7Pd6C+iQduM99OZdzLk8IHEEAgsd8Xv80hL/1794uhLaP4zKkXxxIzQHqpkXExHbwuVyRAeNGjDGs1xv4vo/F+weWKxdznmfOnMcCK96ToMddDyQDBVmC1hsjKmqwhiMkcjLSooJ8UYX2ZEK+f0AiK0Gt6ziO3BAXEdIolGsQMiIFCbZsF7ePA0CN4CQuodaZQGs+IyOXEI7GcR5L4a5YxuFFDPtnVwhf8tl1ClnO5xUVutGi3uz2vwUb7Tcc5XTsnQpICkkaMs+jy5RKHUa7B7lUhdHqYmrPUWnoGM9s9K0RhqMpOq8WmW+FSdDdeMhVTTT4ZVzqXz8f7Nj5Ah+guBP6hVm9AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/f1b187b190346a39d48a953e8d8400c9/a6d36/image-1.png&quot;
        srcset=&quot;/static/f1b187b190346a39d48a953e8d8400c9/222b7/image-1.png 163w,
/static/f1b187b190346a39d48a953e8d8400c9/ff46a/image-1.png 325w,
/static/f1b187b190346a39d48a953e8d8400c9/a6d36/image-1.png 650w,
/static/f1b187b190346a39d48a953e8d8400c9/e548f/image-1.png 975w,
/static/f1b187b190346a39d48a953e8d8400c9/3c492/image-1.png 1300w,
/static/f1b187b190346a39d48a953e8d8400c9/e8814/image-1.png 1392w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;나도 혼동된 개념이지만, Quartz 나 스케줄러와 같은 개념들과 Spring Batch 의 차이점이 햇갈릴 수 있다. Quartz 는 Job Scheduling 라이브러리로, 비교적 단순하거나 소규모 데이터를 스케줄링하며 처리하는데 적합하다. 즉, Quartz 의 경우는 작은 단위의 데이터를 요구하는 스케줄러만으로 작업이 가능한 범위를 처리 가능하다.&lt;/p&gt;
&lt;p&gt;반면 Spring Batch 는 스케줄러를 대체하는 개념이 아니라, &lt;strong&gt;스케줄러에 의해서 동작될 수 있는 개념&lt;/strong&gt;으로 이해하는 것이 좋다. 스케줄러가 Trigger 되어서 제작한 Spring Batch 를 동작시키도록 한다. 또한 Spring Batch 는 대용량 데이터 처리에 적합하며, 기록 관리.대시보드등의 복잡한 기능까지 함께 제공받고 처리하는데 용이하다.&lt;/p&gt;
&lt;h2 id=&quot;spring-batch-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#spring-batch-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;spring batch 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Spring Batch 아키텍처&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/701e7197277bbbc967f912d897aaa6d9/1e093/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.49079754601227%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA30lEQVR42q2SQUvCcBjG97k8iNgO0s1LrD6G7CD4Hbp07OotTBZSlwiFdMumILhDNEISXJRLDdGJ7P/rz1hHYTNfeOGF9+H3PvC8CgcuZefm2wKvzmZs4L9csXitEXoNmFzD0o1FIgkwFo2r4OgEgzKzpxI/tk7oVGCoy2NmLA0TAEUMfLuAdhaej1k0C6wfC9CVbaowbaUA/jkcXUpYkdA+Y2NpbLsa9E7BPgG/nd6hcM+lwwzze5WP2zyfd3mC1hF0cvD1sIfDeV8GUEN4N4iJETVyjkJZvacJ5X9v8ws0xF6KSrLDdQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/701e7197277bbbc967f912d897aaa6d9/a6d36/image-2.png&quot;
        srcset=&quot;/static/701e7197277bbbc967f912d897aaa6d9/222b7/image-2.png 163w,
/static/701e7197277bbbc967f912d897aaa6d9/ff46a/image-2.png 325w,
/static/701e7197277bbbc967f912d897aaa6d9/a6d36/image-2.png 650w,
/static/701e7197277bbbc967f912d897aaa6d9/e548f/image-2.png 975w,
/static/701e7197277bbbc967f912d897aaa6d9/3c492/image-2.png 1300w,
/static/701e7197277bbbc967f912d897aaa6d9/1e093/image-2.png 1376w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;스프링 배치는 Layered 아키텍처를 취하며, 크게 3개의 Layer 로 구성된다. &lt;code class=&quot;language-text&quot;&gt;Application&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Core&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Infrastructure&lt;/code&gt; 3개의 Layer 컴포넌트가 있다. &lt;code class=&quot;language-text&quot;&gt;Application&lt;/code&gt; 은 스프링 배치를 사용하는 개발자가 만드는 모든 배치 Job 과 커스텀 코드를 포함한다. &lt;code class=&quot;language-text&quot;&gt;Core&lt;/code&gt; 는 Job 을 실행하고 제어하는데 필요한 핵심 Runtime 클래스를 포함한다. 각각에 대해 더 자세히 알아보자.&lt;/p&gt;
&lt;h3 id=&quot;application&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#application&quot; aria-label=&quot;application permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Application&lt;/h3&gt;
&lt;p&gt;가장 바깥쪽을 보면 Application Layer 가 존재한다. 개발자가 구성한 배치 처리에 사용되는 코드나 컴포넌트가 이 계층에 포함된다. 즉, 스프링 배치 프레임워크를 통해 개발자가 만든 모든 배치 Job 과 커스텀 코드가 이 Layer 에 포함된다. &lt;strong&gt;비즈니스 로직이나 서비스, 또는 Job 구조화&lt;/strong&gt;와 관련된 내용이 Application 계층에 포함된다고 할 수 있다. 개발자는 이 Application Layer 에서 배치 프레임워크를 활용한 비즈니스 로직 구현에만 집중하고, 배치 관련 기반 기술들은 프레임워크가 내부적으로 담당하고 동작하도록 위임한다.&lt;/p&gt;
&lt;p&gt;개발자가 배치를 위해 짜놓은 대부분의 코드가 Core 계층과 함께 동작하는 Application 계층에 있지만, 위 그림은 종종 &lt;code class=&quot;language-text&quot;&gt;Reader&lt;/code&gt; 나 &lt;code class=&quot;language-text&quot;&gt;Writer&lt;/code&gt; 를 커스텀하여 Core 또는 Infrastructure 의 일부를 만들기도 하기 때문에, Core 와 Infrastructure 를 감싸도록 표현했다.&lt;/p&gt;
&lt;h3 id=&quot;core&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#core&quot; aria-label=&quot;core permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Core&lt;/h3&gt;
&lt;p&gt;배치 Core 에는 배치 작업을 시작하고 제어하는데 필요한 &lt;strong&gt;핵심 클래스&lt;/strong&gt;가 포함되어 있다. Job 을 실행, 모니터링, 관리하는 API 가 이 Layer 안에 담겨있다. &lt;strong&gt;Job, Step, Flow, JobLauncher, JobParameters 구현체&lt;/strong&gt;가 포함된다.&lt;/p&gt;
&lt;h3 id=&quot;infrastructure&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#infrastructure&quot; aria-label=&quot;infrastructure permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Infrastructure&lt;/h3&gt;
&lt;p&gt;어떤 처리를 수행하려면 파일, 데이터베이스 등으로부터 읽고 쓸 수 있어야 할 것이다. 또한, Job 수행에 실패한 이후 재시도 처리할 때 어떤 일을 수행하며 재시도할지를 다룰 수 있어야 할 것이다. 이 기능을 가능케 하는 계층이 Infrastructure 계층이다. 애플리케이션 개발자가 코어 프레임워크 자체 두 계층에서 사용할 수 있는 기능들을 제공한다. 정리하자면, Job 실행의 원활한 흐름과 처리를 위한 틀을 제공하는 Layer 이다.&lt;/p&gt;
&lt;p&gt;예를들어 개발자가 사용할 수 있는 &lt;code class=&quot;language-text&quot;&gt;ItemReader&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;ItemWriter&lt;/code&gt; 와 같은 &lt;code class=&quot;language-text&quot;&gt;Reader&lt;/code&gt; 나 &lt;code class=&quot;language-text&quot;&gt;Writer&lt;/code&gt; 가 포함되어 있으며, 코어에서 사용할 수 있는 기본 Reader 나 Writer 와 &lt;code class=&quot;language-text&quot;&gt;RetryTemplate&lt;/code&gt; 과 같은 서비스가 포함된다.&lt;/p&gt;
&lt;h2 id=&quot;batch-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#batch-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;batch 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Batch 아키텍처&lt;/h2&gt;
&lt;p&gt;Spring Batch 의 배치 작업에서 사용되는 핵심 개념들에 대해 살펴보도록 한다. 각각의 개념 및 용어를 살펴보기전에, 우선 Batch 아키텍처를 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0d93f19c82091812ccaaab219df9dfc8/b1001/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.94478527607362%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB5klEQVR42p2STU8TURSG55eYuHOhP8CFcefOpUH2bozRRA1RozExCGJFMHGhgWJBC1oitERbNQOhlhIxIpCKfLS2tAHbmtKZTqczbelMH6eDqda48iRv7slzPnLPuVfgP61er1NVN6ipy5h6xDqXqGpxhD8TGvpXYSvf9zVNR1o5yU7wIOuBQ2RCB9BiZxEKpTKKXsEwTDuxslejUNKRLZWrezarGUaTqeUKkiQRi8WQky7UrXvI8X6KiR60rBfhVOcQ7d0uQosR8tnvuMUF2nrcnHaM8cQ/x256G3Fhhba7o7TfGeGi8zXTs0Gi0Rjq5hmSs0dIvj9Keu4weuIKwqBP5NWHCDm5CKZBPLOLZ+YjI/4QiUzeZllJsXK+MOgVCa8l2YxGSaVSlHbcKPEu1GQvxfhttMwkgmvISS6Xs0czzf2xl5c+M+Xz2r5hjdswWZZxDgw095pOZ5HWzpH7dJxvwRPkF4+hbd1CGPd4kPL5loZfV1eZFsUWphQKvHg+1mSNd9K3HyKvX+BHpANl4zzlzDBCh+sNN9xiU9efvuPSo5dcfjzBNVegJXZ1+C03R0UkVWve9G8THJMh+n1h+mzN0zsRpOuZn253AMf4DH1T879iYe5bemBJsX6G3dDab73+WxbgJ8+Ldq0nEntfAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/0d93f19c82091812ccaaab219df9dfc8/a6d36/image-4.png&quot;
        srcset=&quot;/static/0d93f19c82091812ccaaab219df9dfc8/222b7/image-4.png 163w,
/static/0d93f19c82091812ccaaab219df9dfc8/ff46a/image-4.png 325w,
/static/0d93f19c82091812ccaaab219df9dfc8/a6d36/image-4.png 650w,
/static/0d93f19c82091812ccaaab219df9dfc8/e548f/image-4.png 975w,
/static/0d93f19c82091812ccaaab219df9dfc8/3c492/image-4.png 1300w,
/static/0d93f19c82091812ccaaab219df9dfc8/b1001/image-4.png 1380w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위 다이어그램은 스프링 배치의 핵심 개념을 도메인 언어로 나타내고 있다. &lt;code class=&quot;language-text&quot;&gt;Job&lt;/code&gt; 은 하나 이상의 &lt;code class=&quot;language-text&quot;&gt;Step&lt;/code&gt; 을 가질 수 있고, 각각의 Step 은 &lt;code class=&quot;language-text&quot;&gt;ItemReader&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ItemProcessor&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ItemWriter&lt;/code&gt; 를 단 하나씩만을 가지며, 현재 프로세스에서 필요한 메타정보들은 &lt;code class=&quot;language-text&quot;&gt;JobRepository&lt;/code&gt; 에 저장된다. 위 아키텍처를 더 자세히 뜯어보면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8706c10e02d188a2dfcd2677cbc55e14/0c3d0/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.80368098159509%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB+ElEQVR42nVTi46cMAzc///L6tQ7Fgh5Qd5hOg63vUVVI1khjhl77MkDb+s87yYrhAC1bYgp/TdGVsod81bxeDk6L+lDpTV+H+mECx3KZkyLh/YF9jjp7+Ne4nIj6Ot/AuRy/gC+gl7L7RXGGBjr8DHn8b2uGv4of2MKQU7c163CwoytX99CYTUF05ZxhAa7F8w6s4r+D2At9KfIKtsPoFwY/mRsoEVo12iRoHFQGS1pV98q6UjiWMV/QmmPabXYQ74DriZDa4ONAV+qwHk/giP7dn63JRJ8VgHO8c5XggKby1iUGT1/tH5lk10GcXCYgbuAmIO0XUchSGI1ibuNJz7nQACPp6lQZPFkgq+nw8YEj+mp8etjYpBh2R7eB1iasg0pVewcwu+l4ogFfs/4VJUAB6tnO2xhlYm9jRAce7CHxgWsm4OLnRkKtPEDUHqYMkFC4Y+NFReCJyz0f86ebdkxc2izCdQfQVmM3km5kIrYRZkUfSdIvygzQPGc80Vf+TZsWiPFfgxwvSdWTNrLTq3W+1A0gwtlIQmG8hcFxWYnjlcmK0k2xmgqwTupqI2hKJsGy0H5Jhs6HM1Td54tkNchbZDzTjMU+06/DIgKwcEhiYwch7jwFeX6JuyXuN9tyKReehNLdCjH3m2GZwJ3eQznEHj9fth/AD/S9bT8YcxuAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/8706c10e02d188a2dfcd2677cbc55e14/a6d36/image-5.png&quot;
        srcset=&quot;/static/8706c10e02d188a2dfcd2677cbc55e14/222b7/image-5.png 163w,
/static/8706c10e02d188a2dfcd2677cbc55e14/ff46a/image-5.png 325w,
/static/8706c10e02d188a2dfcd2677cbc55e14/a6d36/image-5.png 650w,
/static/8706c10e02d188a2dfcd2677cbc55e14/e548f/image-5.png 975w,
/static/8706c10e02d188a2dfcd2677cbc55e14/3c492/image-5.png 1300w,
/static/8706c10e02d188a2dfcd2677cbc55e14/0c3d0/image-5.png 1414w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Batch 아키텍처에 등장하는 각 컴포넌트의 개념에 대해 학습해보자.&lt;/p&gt;
&lt;h3 id=&quot;job&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#job&quot; aria-label=&quot;job permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Job&lt;/h3&gt;
&lt;p&gt;Job 이란 &lt;strong&gt;배치 처리 과정들을 하나의 단위로 만들어 놓은 객체&lt;/strong&gt;이다. 또한 배치 처리 과정에 있어서 전체 계층 최상단에 위치하고 있다. 또한 Job 은 여러개의 Step 단계별로 나누어진 Step 들을 묶어놓은 논리적인 하나의 배치 처리 단위라고도 정의할 수 있다. 즉, Job 은 그 안에서 여러개의 Step 단계별로 나뉜다.&lt;/p&gt;
&lt;h3 id=&quot;jobinstance&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jobinstance&quot; aria-label=&quot;jobinstance permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JobInstance&lt;/h3&gt;
&lt;p&gt;JobInstance 는 &lt;strong&gt;Job 의 실행 단위&lt;/strong&gt;를 나타낸다. Job 을 실행시키게 되면 하나의 JobInstance 가 생성된다. 예를들어 10월 1일 실행, 10월 2일 실행을 하게되면 각각의 JobInstance 가 생성되며, 10월 1일 실행한 JobInstance 가 실패하여 다시 실행을 시키더라도 이 JobInstance 는 10월 1일에 대한 데이터만 처리하게 된다.&lt;/p&gt;
&lt;h3 id=&quot;jobparameters&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jobparameters&quot; aria-label=&quot;jobparameters permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JobParameters&lt;/h3&gt;
&lt;p&gt;앞서 JobInstance 는 Job 의 실행 단위라고 했다. 그렇다면 각각의 JobInstance 는 어떻게 구별할까? 이는 바로 JobParameters 객체로 구분하게 된다. 즉, JobParameters 는 &lt;strong&gt;JobInstance 의 구분.식별자&lt;/strong&gt;로, 보통 날짜 형식으로 식별자를 만들 수 있다.&lt;/p&gt;
&lt;p&gt;JobParameters 는 JobInstance 의 식별자 역할 외에도 개발자 JobInstance 에 전달되는 매개변수 역할도 하고있다. 또한 JobParameters 는 String, Double, Long, Date 와 같은 4가지의 형식만을 지원하고 있다.&lt;/p&gt;
&lt;h3 id=&quot;jobexecution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jobexecution&quot; aria-label=&quot;jobexecution permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JobExecution&lt;/h3&gt;
&lt;p&gt;JobExecution 은 &lt;strong&gt;JobInstance 에 대한 실행 시도&lt;/strong&gt;에 대한 객체이다. 1월 1일에 실행한 JobInstance 가 실패하여 재실행을 하여도, 동일한 JobInstance 를 실행시키지만 이 각각의 실행에 대한 JobExecution 은 개별로 생성된다. JobExecution 은 이러한 JobInstance 실행에 대한 상태, 시작시간, 종료시간, 생성시간 등의 정보를 담고있다.&lt;/p&gt;
&lt;h3 id=&quot;step&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step&quot; aria-label=&quot;step permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step&lt;/h3&gt;
&lt;p&gt;Step 은 Job 의 배치 처리를 정의하고 순차적인 단계를 캡슐화한다. 즉, &lt;strong&gt;Step 은 Job 내부적으로 순차적인 세부 단계를 정의하는 각각의 논리적인 단위이다.&lt;/strong&gt; Job 은 최소한 1개 이상의 Step 을 가져야 하며 Job 의 실제 일괄 처리를 제어하는 모든 정보가 들어있다.&lt;/p&gt;
&lt;h3 id=&quot;stepexecution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stepexecution&quot; aria-label=&quot;stepexecution permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;StepExecution&lt;/h3&gt;
&lt;p&gt;StepExecution 은 JobExecution 과 동일하게 &lt;strong&gt;Step 실행 시도에 대한 객체&lt;/strong&gt;를 나타낸다. JobExecution 이 Job 을 위한 객체였다면, StepExecution 은 Step 을 위한 객체인 것이다. 하지만 Job 이 여러개의 Step 으로 구성되어 있을 경우, 이전 단계의 Step 이 실패하게 되면 다음 단계가 실행되지 않음으로 인하여, 실패 이후의 StepExeution 은 생성되지 않는다.&lt;/p&gt;
&lt;p&gt;StepExecution 또한 JobExecution 과 동일하게 실제 시작이 될 때만 생성된다. StepExectuion 에는 JobExecution 에 저장되는 정보외에 Read 수, Write 수, Commit 수, Skip 수 등의 정보들도 저장이 된다.&lt;/p&gt;
&lt;h3 id=&quot;executioncontext&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#executioncontext&quot; aria-label=&quot;executioncontext permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ExecutionContext&lt;/h3&gt;
&lt;p&gt;ExecutionContext 란 Job 에서 데이터를 공유할 수 있는 데이터 저장소이다. Spring Batch 에서 제공하는 ExecutionContext 는 &lt;strong&gt;JobExecutionContext, StepExecutionContext 2가지 종류&lt;/strong&gt;가 있으나, 이 2가지는 지정되는 범위가 다르다. JobExecutionContext 의 경우 Commit 시점에 저장되는 반면, StepExecutionContext 는 실행 사이에 저장이 된다. ExecutionContext 를 통해 Step 간에 데이터 공유가 가능하며, Job 실패시 ExecutionContext 를 통한 마지막 실행 값을 재구성 할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;jobrepository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jobrepository&quot; aria-label=&quot;jobrepository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JobRepository&lt;/h3&gt;
&lt;p&gt;JobRepository 는 앞서 말한 &lt;strong&gt;모든 배치 처리 정보를 담고있는 일종의 저장소&lt;/strong&gt;이다. Job 이 실행되면 JobRepository 에 JobExecution 과 StepExecution 을 생성하게 되며, JobRepository 에서 Execution 정보들을 저장하고 조회하며 사용하게 된다.&lt;/p&gt;
&lt;h3 id=&quot;joblauncher&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#joblauncher&quot; aria-label=&quot;joblauncher permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JobLauncher&lt;/h3&gt;
&lt;p&gt;Job 과 JobParameters 를 사용하여 Job 을 실행하는 객체이다.&lt;/p&gt;
&lt;h3 id=&quot;itemreader&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#itemreader&quot; aria-label=&quot;itemreader permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ItemReader&lt;/h3&gt;
&lt;p&gt;ItemReader 는 Step 에서 Item 을 읽어오는 인터페이스이다. ItemReader 에 대한 다양한 인터페이스가 존재하며, 다양한 방법으로 Item 을 읽어올 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;itemwriter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#itemwriter&quot; aria-label=&quot;itemwriter permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ItemWriter&lt;/h3&gt;
&lt;p&gt;ItemWriter 는 처리된 데이터를 Write 할때 사용한다. Writer 는 처리 결과물에 따라 Insert 가 될 수도, Update 가 될 수도, 또는 Queue 를 사용한다면 Send 가 될 수도 있다. Writer 또한 Read 와 동일하게 다양한 인터페이스가 존재한다. Writer 는 기본적으로 Item 을 Chunk 로 묶어서 처리한다.&lt;/p&gt;
&lt;h3 id=&quot;itemprocessor&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#itemprocessor&quot; aria-label=&quot;itemprocessor permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ItemProcessor&lt;/h3&gt;
&lt;p&gt;ItemProcessor 는 Reader 에서 읽어온 Item 을 데이터를 처리하는 역할을 한다. Processor 는 배치를 처리하는데 필수 요소는 아니며, Reader, Writer, Processor 처리를 분리하여 각각의 역할을 명확하게 구분하고 있다.&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;다음 포스팅에선 Spring Batch 를 직접 코드로 작성하며, 더 높은 이해도를 갖고 Batch 를 다루어볼까 한다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://gngsn.tistory.com/177#google_vignette&quot;&gt;https://gngsn.tistory.com/177#google_vignette&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loosie.tistory.com/838&quot;&gt;https://loosie.tistory.com/838&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://khj93.tistory.com/entry/Spring-Batch%EB%9E%80-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B3%A0-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0#google_vignette&quot;&gt;https://khj93.tistory.com/entry/Spring-Batch%EB%9E%80-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B3%A0-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0#google_vignette&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Spring Cloud 란 무엇이고, 왜 사용하는걸까?]]></title><description><![CDATA[MSA 환경에서 Spring Cloud 의 필요성 MSA 는 단일 애플리케이션을 작은 서비스 단위로 분할하여 개발, 배포, 확장을 용이하게 하는 아키텍처이다.  이러한 MSA…]]></description><link>https://haon.site/spring/cloud-basic/</link><guid isPermaLink="false">https://haon.site/spring/cloud-basic/</guid><pubDate>Thu, 04 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dc65a7d20640885f7e52ca64d27d9244/cab8c/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.78527607361963%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA9ElEQVR42q2SQUuEUBSF5xe36KdEbaKVgtAqAlvYJpCISJfRDDMQ6OSijaPoOOrzvS99YuPUtLL3OHDu5XDuhXNn/PObjQupJKrFZEPVfikHo5arpmdKHWDoddq9/pdhL6ybipu3cx6DW12XZUnTCIQQ1HWtjYqiIEkSbdbVnebohlWz4+z5BHtxyevnE8uNx3y+wLZtfN/H8zxc19WwLAvDMDBNE8dxvrc+MNyJLRcvpzy8X3O/MrlbXiEqyfpjTZZl5HlOGIakaUocx0RRRBAEmh817JuSvNqwrVMdzuSUFfspAx9P/hnQOKg/z6YPSE06my8bCbmxLKkWPgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/dc65a7d20640885f7e52ca64d27d9244/a6d36/image-3.png&quot;
        srcset=&quot;/static/dc65a7d20640885f7e52ca64d27d9244/222b7/image-3.png 163w,
/static/dc65a7d20640885f7e52ca64d27d9244/ff46a/image-3.png 325w,
/static/dc65a7d20640885f7e52ca64d27d9244/a6d36/image-3.png 650w,
/static/dc65a7d20640885f7e52ca64d27d9244/cab8c/image-3.png 744w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;msa-환경에서-spring-cloud-의-필요성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#msa-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-spring-cloud-%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1&quot; aria-label=&quot;msa 환경에서 spring cloud 의 필요성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MSA 환경에서 Spring Cloud 의 필요성&lt;/h2&gt;
&lt;p&gt;MSA 는 단일 애플리케이션을 작은 서비스 단위로 분할하여 개발, 배포, 확장을 용이하게 하는 아키텍처이다.  이러한 MSA 환경에서 각 마이크로 서비스들은 독립적으로 운영되며, 서로 유기적으로 협력하여 전체 시스템을 구성한다. Spring Cloud 는 바로 이러한 MSA 환경에서 각 서비스들이 유연하게 통신.동작할 수 있도록 기능을 제공하는 라이브러리이다.&lt;/p&gt;
&lt;h2 id=&quot;spring-cloud-란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#spring-cloud-%EB%9E%80&quot; aria-label=&quot;spring cloud 란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Spring Cloud 란?&lt;/h2&gt;
&lt;p&gt;Spring Cloud 는 MSA 를 구현하는데 필요한 다양한 도구와 라이브러리를 제공하는 프레임워크이다. 즉, MSA 환경 구성시 개발, 빌드, 배포, 운영에 필요한 내용들을 쉽게 구성할 수 있도록 도와주는 라이브러리이다. 이를 통해 MSA 환경에서 각 서비스간의 통신, 설정관리, 부하 분산, 서킷 브레이커등 여러 문제를 해결할 수 있다. 더 자세히 어떤 기능을 제공하는지 살펴보자.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;spring-cloud-주요-기능&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#spring-cloud-%EC%A3%BC%EC%9A%94-%EA%B8%B0%EB%8A%A5&quot; aria-label=&quot;spring cloud 주요 기능 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Spring Cloud 주요 기능&lt;/h2&gt;
&lt;p&gt;Spring Cloud 에서 제공하는 기능은 매우 다양하다. 그 중에 가장 핵심 기능에 대해서만 우선 다루고, 아래에서 상세 기능들 각각을 더 자세히 다루어볼까 한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9370d4c5070cd8297ec0ea6963379996/c5bb3/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.80368098159509%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC0klEQVR42j2TaWsbVxiF9YP7X/KtlJZCWoqhC4ZAmobmS0OXOMFSbUv2SOPROpp91SySRhrN0yOVVnC4l9G9zz3v1qu6E9XpRHls8fOcqChJy5py37CptpS7hlTf8rIi0/dkU17WtKiINgXZdse2g7rrqIDeoW05CFbuClb2Ctte8/xsYknL5ZJKIN/3sddrPNcjCIKLoihitVoRxTHn30nA0xk4iVLMJObb4Ze8sq5Jtg3DsYW1cFg6PrtDR3OAvbQ76yg3LVRSIYU679R71jrgnjp6RphghDE/Pf3IL+Zbws2eIC/RPbwgoSwOHPYC7gSTttqXTXcBOskGO4opqpqlXC+Upt4kyeibM4yFizFfM57b5LUgIoZBRpHvmE0XOI570drxFL5LkJXcPU6If3vH/fu3PIzucA6tHPoRN0ODT8MnBjowUri2EylHJfYqoNrscAWajCd8+P0jH28+KccTwjRn6oQEsymWaTBVfp3DSQ4V1rNC/vr2c64fvyeSo9u/HzAMi8fhhLm5FNimriuu77/iL+tX2n2HYVrMvZhcFY7bDl8ROVp7YwHNKOEH44o309fk25ZI4ahoREFOmdfkQcW7+RUvBp/xZvaSWTokSSrsOCM/dsTKqStdgGa6YbR0MWXfkmZOQFYdaPRi4Gdqm4bQT1l4M/qzP7mVVr5NmFXK94qpWsu0pszWDrbaofcURLzv3/FH/4EPgyG3D0+XhJe7E44bC3i8VLmVTmqbVg9tD/9W+VHFcj3lThWeej7Lc5UNhWsIenX/Ha/HP+NmNTeDe/oCT2Yr1GLUGoVKDxTbI/m+ZaPw8gZShetXe5xdy6ppWWvieqMgZhQGfDF4wdXwG7yiYRGkFHJS6GIpV2rF/9ezcikRMNL/gfaezroak7US37PqmnGS85ypDbINy1Sjpj5MdTDTpc1/OoPOrqRE+1jrGeaeJadrTYnddfwDf+jIj6vlqtQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/9370d4c5070cd8297ec0ea6963379996/a6d36/image-2.png&quot;
        srcset=&quot;/static/9370d4c5070cd8297ec0ea6963379996/222b7/image-2.png 163w,
/static/9370d4c5070cd8297ec0ea6963379996/ff46a/image-2.png 325w,
/static/9370d4c5070cd8297ec0ea6963379996/a6d36/image-2.png 650w,
/static/9370d4c5070cd8297ec0ea6963379996/c5bb3/image-2.png 680w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;중앙화된-구성설정-관리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A4%91%EC%95%99%ED%99%94%EB%90%9C-%EA%B5%AC%EC%84%B1%EC%84%A4%EC%A0%95-%EA%B4%80%EB%A6%AC&quot; aria-label=&quot;중앙화된 구성설정 관리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;중앙화된 구성(설정) 관리&lt;/h3&gt;
&lt;p&gt;Spring Cloud 는 &lt;code class=&quot;language-text&quot;&gt;Spring Cloud Config&lt;/code&gt; 라는 솔루션을 제공하여, 애플리케이션의 설정.구성 내용을 중앙에서 관리할 수 있는 기능을 제공한다. 마이크로 서버는 환경에 따라 다른 설정을 필요로 하며, 이런 설정을 일괄적으로 중앙화된 한 곳에서 관리하는 것이 복잡성이 줄어들 것이다.&lt;/p&gt;
&lt;p&gt;Spring Cloud Config 를 사용하면 중앙에서 설정을 관리하고, 각 서비스가 변경된 설정을 실시간으로 반영할 수 있도록 해준다. 이를 통해 개발자는 환경 설정 변경으로 인한 문제를 최소화하고, 더 집중적으로 애플리케이션 개발에 집중할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;서비스-디스커버리service-discovery&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B9%84%EC%8A%A4-%EB%94%94%EC%8A%A4%EC%BB%A4%EB%B2%84%EB%A6%ACservice-discovery&quot; aria-label=&quot;서비스 디스커버리service discovery permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서비스 디스커버리(Service Discovery)&lt;/h3&gt;
&lt;p&gt;MSA 환경에서는 서비스가 동적으로 배포되고 확장되므로 각 서비스의 위치(IP, 포트) 를 고정할 수 없다. 즉, 각 서비스는 위치값이 동적으로 계속 변한다. 이를 해결하기 위해 Spring Cloud 는 &lt;code class=&quot;language-text&quot;&gt;Eureka&lt;/code&gt; 와 같은 Service Discovery 솔루션을 제공하여, 각 서비스가 중앙 레지스트리에 자신의 정보를 등록하고 다른 서비스가 이를 동적으로 검색할 수 있도록 기능을 지원해준다.&lt;/p&gt;
&lt;p&gt;즉, 각 마이크로 서비스가 네트워크 상에서 서로를 자동으로 찾고 통신할 수 있게 해준다. 이를 통해 서비스간의 유연한 통신.확장성을 갖게 해주며, 마이크로 서버으 위치(IP, Port)가 동적으로 변하는 환경에서도 유연하게 동작할 수 있도록 해준다.&lt;/p&gt;
&lt;h3 id=&quot;로드밸런싱부하-분산&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1%EB%B6%80%ED%95%98-%EB%B6%84%EC%82%B0&quot; aria-label=&quot;로드밸런싱부하 분산 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로드밸런싱(부하 분산)&lt;/h3&gt;
&lt;p&gt;서비스 간에 호출이 발생할 때, 트래픽을 여러 인스턴스에 효율적으로 분산하는 것이 필수이다. Spring Cloud 는 Ribbon 과 같은 클라이언트 사이드 로드밸런서를 제공하여, 서비스간 부하를 자동으로 분산시켜서 시스템의 안정성을 높인다.&lt;/p&gt;
&lt;h3 id=&quot;장애-대응과-회복력fault-tolerance---서킷-브레이커&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%95%A0-%EB%8C%80%EC%9D%91%EA%B3%BC-%ED%9A%8C%EB%B3%B5%EB%A0%A5fault-tolerance---%EC%84%9C%ED%82%B7-%EB%B8%8C%EB%A0%88%EC%9D%B4%EC%BB%A4&quot; aria-label=&quot;장애 대응과 회복력fault tolerance   서킷 브레이커 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장애 대응과 회복력(Fault Tolerance) - 서킷 브레이커&lt;/h3&gt;
&lt;p&gt;MSA 환경에서는 개별 서비스에 장애가 터질 가능성이 높다. Spring Cloud 는 &lt;code class=&quot;language-text&quot;&gt;Hystrix&lt;/code&gt; 를 통해 서킷 브레이커(Circuit Breaker) 패턴을 지원하여, 장애가 발생했을 때 빠르게 감지하고 대체 경로를 활용하여 시스템의 안정성을 확보한다.&lt;/p&gt;
&lt;p&gt;Spring Cloud 에서 제공하는 서킷 브레이커는, 특정 기준에 따라서 실패한 서비스 API Call 을 자동으로 중단시키고, 시스템이 장애를 우회하고 계속해서 작동할 수 있도록 한다. 이 덕분에 서비스 간의 장애 전파 및 의존성을 최소화할 수 있게된다.&lt;/p&gt;
&lt;h3 id=&quot;api-gateway&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#api-gateway&quot; aria-label=&quot;api gateway permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;API Gateway&lt;/h3&gt;
&lt;p&gt;마이크로 서비스를 클라이언트에게 노출할 때, 단일 진입점(Entry Point) 를 제공하는 것이 효율적이고,좋다. Spring Cloud 는 &lt;code class=&quot;language-text&quot;&gt;Zuul&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;Spring Cloud GW&lt;/code&gt; 를 통해 API Gateway 역할을 수행하며, &lt;code class=&quot;language-text&quot;&gt;요청&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;라우팅&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;인증&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;로깅&lt;/code&gt; 등 다양한 기능을 제공한다.&lt;/p&gt;
&lt;h3 id=&quot;분산-트레이싱distributed-tracing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%84%EC%82%B0-%ED%8A%B8%EB%A0%88%EC%9D%B4%EC%8B%B1distributed-tracing&quot; aria-label=&quot;분산 트레이싱distributed tracing permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;분산 트레이싱(Distributed Tracing)&lt;/h3&gt;
&lt;p&gt;MSA 환경에서는 요청이 여러 서비스에 걸쳐 분산되어 처리되므로, 장애 분석과 성능 최적화에 어려움이 따른다. Spring Cloud는 &lt;code class=&quot;language-text&quot;&gt;Sleuth&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;Zipkin&lt;/code&gt;을 이용하여 분산 로그 추적 기능을 제공함으로써 요청의 흐름을 시각적으로 분석하고 문제 발생 시 원인을 파악하는 데 도움을 준다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;spring-cloud-의-주요-모듈&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#spring-cloud-%EC%9D%98-%EC%A3%BC%EC%9A%94-%EB%AA%A8%EB%93%88&quot; aria-label=&quot;spring cloud 의 주요 모듈 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Spring Cloud 의 주요 모듈&lt;/h2&gt;
&lt;p&gt;앞서 Spring Cloud 에서 제공해주는 여러 모듈.툴을 통해 &lt;code class=&quot;language-text&quot;&gt;서비스 디스커버리(Service Discovery)&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;API 게이트웨이&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;로드밸런싱&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;중앙화된 설정.구성 관리&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;서킷 브레이커&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;분산 트레이싱&lt;/code&gt; 등의 기능을 제공받을 수 있음을 알게 되었다. 그렇다면 Spring Cloud 내에서 제공하는 각 기능은 어떤 툴에서 제공하는지에 대해 살펴보자.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Spring Cloud 에서 제공하는 모든 모듈을 다루지는 않겠다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d3f669aae691e55ee0c46f115e9eff4c/b97f6/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.25766871165644%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAABhklEQVR42o1Ty07DMBDs//8CHwBnjpwQEofeOCBVQghVUDXNw3ZiJ87THXZcpReaFksrx5Z3ZnZns8LC6roO+/0eh8MBaZrGnXfH4xHX1urS5TAMUErBOYcQAqy1yPM8EtR1jb7v4b3HNE3XAck+jmOMoihiIldZlkiSJBI0TROVGmMi8VVAPmAima11osbG4JllM0jG8yV1fwD5SGuNShS5pof+eoT+fMAgucbos0LuWZbdVsiS2S/N/tUeXfUBb97RD0FMSSIZFRKU72728AyMEHdl7lGoOzCvaWoxqoj9a9v2fyXPZYdwelzZN2izlvPJGDpNhbNZNwE5Ipy3Uhzs+gCfruF2z/DtJOa488jMQaWLgASbVbStF3Ue+vsFavuEXFXSP3Ue8izPpBUFrLPLgDSEoHSwqqrIXtkmmjPfcdhzU4ppBjo3aOsO0zgtl8z+UAETSbDb/QhQGcGp3EgFartBbYTQDXC6wdAN18fmZEqI37NSzpuV73GS33Dzis7qRVN+AdVi9NWeJj5DAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d3f669aae691e55ee0c46f115e9eff4c/a6d36/image-1.png&quot;
        srcset=&quot;/static/d3f669aae691e55ee0c46f115e9eff4c/222b7/image-1.png 163w,
/static/d3f669aae691e55ee0c46f115e9eff4c/ff46a/image-1.png 325w,
/static/d3f669aae691e55ee0c46f115e9eff4c/a6d36/image-1.png 650w,
/static/d3f669aae691e55ee0c46f115e9eff4c/b97f6/image-1.png 958w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;서비스-디스커버리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B9%84%EC%8A%A4-%EB%94%94%EC%8A%A4%EC%BB%A4%EB%B2%84%EB%A6%AC&quot; aria-label=&quot;서비스 디스커버리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서비스 디스커버리&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; Spring Cloud Netfix Eureka : 서비스 인스턴스 등록 및 검색을 위한 서비스 디스커버리 서버를 제공하는 툴이다. 각 서비스는 Eureka 서버에 자신의 위치를 등록하고, 다른 서비스의 위치 정보를 조회할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; Spring Cloud Zookeeper : ZooKeeper 를 기반으로 서비스 디스커버리 기능을 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;api-gateway-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#api-gateway-1&quot; aria-label=&quot;api gateway 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;API Gateway&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; Spring Cloud Gateway : 동적 라우팅, 필터링, 인증, 권한 부여 기능을 제공하는 고성능 API 게이트웨이이다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; Spring Cloud Netfix Zuul : (Deprecated 된 기능이다.)  라우팅 및 필터링 기능을 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;중앙화된-설정구성-관리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A4%91%EC%95%99%ED%99%94%EB%90%9C-%EC%84%A4%EC%A0%95%EA%B5%AC%EC%84%B1-%EA%B4%80%EB%A6%AC&quot; aria-label=&quot;중앙화된 설정구성 관리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;중앙화된 설정.구성 관리&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; Spring Cloud Config : Git 레포지토리, SVN, 로컬 파일 시스템과 같은 외부 레포지토리로부터 설정.구성 정보를 가져와서 서비스에 적용한다. 설정 정보 변경 내용은 실시간으로 즉시 적용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;로드밸런싱&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1&quot; aria-label=&quot;로드밸런싱 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로드밸런싱&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; Spring Cloud Load Balancer : 서비스 디스커버리와 연동하여 Client-Side 로드밸런싱 기능을 제공한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; Spring Cloud Netfix Ribbon : (Deprecated 되었다) Client-Side 로드밸런싱 기능을 제공한다. Spring Cloud Load Balancer 로 대체되는 추세이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;서킷-브레이커&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%ED%82%B7-%EB%B8%8C%EB%A0%88%EC%9D%B4%EC%BB%A4&quot; aria-label=&quot;서킷 브레이커 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서킷 브레이커&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; Spring Cloud Circuit Breaker : 서비스 장애가 터졌을 떄, 해당 서버로의 요청을 차단하고 &lt;code class=&quot;language-text&quot;&gt;FallBack&lt;/code&gt; 처리를 한다. &lt;code class=&quot;language-text&quot;&gt;Resilience4j&lt;/code&gt; 를 기반으로 구현되었다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; Spring Cloud Netfix Hystrix : (Deprecated) 마찬가지로 서킷 브레이커 기능을 제공하지만, Deprecated 되었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;분산-트레이싱&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%84%EC%82%B0-%ED%8A%B8%EB%A0%88%EC%9D%B4%EC%8B%B1&quot; aria-label=&quot;분산 트레이싱 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;분산 트레이싱&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; Spring Cloud Sleuth : 서비스 간 호출 흐름을 추적하야 장애 원인을 찾아내는데 도움을 준다. 관련 기술로는 &lt;code class=&quot;language-text&quot;&gt;Span&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Trace&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;OpenTelementry&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Zipkin&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Jager&lt;/code&gt; 등이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위와 같은 기능 및 모듈외에도 Spring Cloud 에선 다양한 모듈을 통해 여러가지 기능을 제공한다. &lt;code class=&quot;language-text&quot;&gt;비동기 메시징&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;함수형 프로그래밍&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;보안&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;클라우드 연동&lt;/code&gt; , &lt;code class=&quot;language-text&quot;&gt;REST Client&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;배치 처리&lt;/code&gt; 등을 제공한다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cncf.co.kr/blog/spring-cloud-intro/&quot;&gt;https://www.cncf.co.kr/blog/spring-cloud-intro/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://freedeveloper.tistory.com/469&quot;&gt;https://freedeveloper.tistory.com/469&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elancer.co.kr/blog/detail/248&quot;&gt;https://www.elancer.co.kr/blog/detail/248&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pixx.tistory.com/273&quot;&gt;https://pixx.tistory.com/273&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[카프카 기본 스택 및 아키텍처 살펴보기]]></title><description><![CDATA[회사에서 카프카를 사용한다. OJT 를 듣고, 커뮤니케이션을 할때 카프카의 특징을 학습해두면 큰 도움이 될 것이라는 생각이 들었다. 이번 글에서는 카프카를 왜 사용하며, 어떤 특징이 있는지와, 간단한 내부 아키텍처에 대해 학습해보도록 한다. 참고…]]></description><link>https://haon.site/kafka/overview/</link><guid isPermaLink="false">https://haon.site/kafka/overview/</guid><pubDate>Mon, 18 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;회사에서 카프카를 사용한다. OJT 를 듣고, 커뮤니케이션을 할때 카프카의 특징을 학습해두면 큰 도움이 될 것이라는 생각이 들었다. 이번 글에서는 카프카를 왜 사용하며, 어떤 특징이 있는지와, 간단한 내부 아키텍처에 대해 학습해보도록 한다. &lt;a href=&quot;https://hudi.blog/what-is-kafka/&quot;&gt;참고1&lt;/a&gt; 을 통해서 빠르게 학습을 해보도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;메시지-큐와-mom&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%94%EC%8B%9C%EC%A7%80-%ED%81%90%EC%99%80-mom&quot; aria-label=&quot;메시지 큐와 mom permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메시지 큐와 MOM&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2714ec192f4e48f23dc1dfda53a688ef/01a87/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 35.58282208588957%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA5ElEQVR42pVRSQqEQAzs///Kk6AXPXgSUdx3cT241VCBlmGYw0xDkaSTVKrTCh/nvm/8c/q+R13XaJpGfDXP8xOM4/gToa6Zpgme5yGOY0RRhCzLoNq2RZ7noC2KQuy+71iWRWKiLMvHDsMgRBrHceA8T1zXJb7atg1USXXrugpZVVUwDAO2bcM0TbiuC8uy4DiODORreMceKgzDEEEQIE1TKE5mkCSJgMUk5SASM2aeZFRIVVRPpQTFdF0n4ADFJIuYICj9287e7/UOSeD7/kNOUvWtWTdo/z3+JOUKqJz/wN9+ASyCGBcHCyV9AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/2714ec192f4e48f23dc1dfda53a688ef/a6d36/image.png&quot;
        srcset=&quot;/static/2714ec192f4e48f23dc1dfda53a688ef/222b7/image.png 163w,
/static/2714ec192f4e48f23dc1dfda53a688ef/ff46a/image.png 325w,
/static/2714ec192f4e48f23dc1dfda53a688ef/a6d36/image.png 650w,
/static/2714ec192f4e48f23dc1dfda53a688ef/e548f/image.png 975w,
/static/2714ec192f4e48f23dc1dfda53a688ef/01a87/image.png 1288w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;메시지 큐를 사용하면 발신자와 수신자가 서로를 직접 참조하지 않고 메시지 큐를 통해서 통신한다. 즉, 서로를 직접 알 필요가 없기 때문에 &lt;strong&gt;(1) 느슨한 결합(decoupling)&lt;/strong&gt; 관계를 만들어낼 수 있다. 발신자와 수신자는 서로가 의존하지 않으므로 &lt;strong&gt;독립적으로 확장(Scale Out)&lt;/strong&gt; 할 수 있다. N:1:M 의 형태로 발신자, 수신자 사이에 메시지 큐가 메시지를 중개하기 때문이다. 이 덕분에 MSA 환경에서 메시지 큐는 빛을 발한다. 각 마이크로 서버는 메시지 큐를 활용하여 간접적으로 통신하고, 서로에게 미치는 영향을 최소화하며, 독립적으로 확장할 수 있기 때문이다.&lt;/p&gt;
&lt;p&gt;또한 메시지 큐는 발신자(producer)가 발행한 메시지가 소비자(consumer) 에게 반드시 전달된다는 &lt;strong&gt;(2) 보장성&lt;/strong&gt; 을 갖는다. 즉, 메시지는 유실되지 않는다. 이 덕분에 MSA 환경에서 각 마이크로 서버가 메시지 및 데이터 유실없이 안전하게 통신할 수 있음을 보장받게 된다.&lt;/p&gt;
&lt;p&gt;또한, 메시지 큐를 사용하면 &lt;strong&gt;(3) 비동기 통신(asynchoronous)&lt;/strong&gt; 를 구현할 수 있다. 만약 MSA 환경에서 마이크로 서버 A 와 B 가 통신하는 상황을 가정해보자. A 가 B 를 HTTP 통신을 통해 Request 를 날리는 경우에, 만약 메시지 큐가 없더라면 어떻게 될까? A 는 B 가 해당 Request 를 처리하고 동기적으로 Response 를 받을 때 까지 무한정 대기해야한다. 즉, 동기적 통신으로 인하여 A 는 B 에 대한 의존성이 커진다.&lt;/p&gt;
&lt;p&gt;반대로 A 가 메시지를 메시지 큐에 발행하고, B 가 해당 메시지를 컨슘한다면? 즉, 비동기적으로 통신이 이루어진다면 어떻게 달라질까? 비동기 통신 덕분에 B 는 A 의 처리가 완료되기 전까지 대기하지 않아도 된다. B 는 다른 테스크를 수행하다가 메시지 큐로 부터 메시지가 발행되었다는 알림을 받을 때가 되었을 때, 상황에 알맞게 메시지를 꺼내오면 된다.&lt;/p&gt;
&lt;p&gt;이러한 특성 덕분에 메시지 큐는 이미지 프로세싱, Batch 처리와 같이 &lt;strong&gt;매우 무거운 작업을 처리하거나, 혹은 이벤트 드리븐 아키텍처에서 이벤트가 발생헀음을 알리기 위한 용도로 사용하기 적합하다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;mq-의-모델--point-to-point-와-pubsub&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mq-%EC%9D%98-%EB%AA%A8%EB%8D%B8--point-to-point-%EC%99%80-pubsub&quot; aria-label=&quot;mq 의 모델  point to point 와 pubsub permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MQ 의 모델 : Point to Point 와 Pub/Sub&lt;/h3&gt;
&lt;p&gt;메시지 큐는 크게 &lt;strong&gt;Point to Point(P2P)&lt;/strong&gt; 와 &lt;strong&gt;Pub/Sub&lt;/strong&gt; 모델 2가지로 구분할 수 있다. P2P 모델은 한 대의 발신자가 한 대의 수신자에게 메시지를 보내는 방식이다. 즉, &lt;strong&gt;메시지 전송 대상이 한 대로 고정되어 있다.&lt;/strong&gt; 반면 Pub/Sub 모델은 발신자가 &lt;strong&gt;토픽(topic)&lt;/strong&gt; 이라고 불리는 공간에 메시지를 전송하면, 그 &lt;strong&gt;토픽(topic)&lt;/strong&gt; 을 구독하고 있는 수신자 모두 메세지를 수신하는 방식이다. 즉, &lt;strong&gt;전송 대상이 다수이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;그리고 &lt;strong&gt;MOM(Message Oriented Middleware)&lt;/strong&gt; 이라고 하면 일반적으로 Kafka, RabbitMQ, ActiveMQ 를 뜻한다. Redis 는 Pub/Sub 을 지원하긴 하지만, MOM 으로는 부르지 않는다. 그리고 RabbitQMQ 와 ActiveMQ 는 P2P 와 Pub/sub 모델을 함께 지원하는 반면에, &lt;strong&gt;Kafka 는 Pub/Sub 모델만을 지원한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;카프카&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B9%B4%ED%94%84%EC%B9%B4&quot; aria-label=&quot;카프카 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;카프카&lt;/h2&gt;
&lt;p&gt;카프카는 RabbitMQ, ActiveMQ 대비 &lt;strong&gt;높은 확장성과 내결함성, 대용량 데이터 처리, 실시간 데이터 처리에 특화&lt;/strong&gt;되어있는 메시징 시스템이다. 카프카는 어떤 문제를 해결하기 위해 등장했을까? 아래 그림은 카프카 등장 이전의 일반적인 대규모 아키텍처이다.  각 서비스 또는 데이터 저장소가 End-To-End 로 연결되어 아주 복잡하고, 의존.참조 관계를 파악하기 힘든 구조를 가지고 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9210c181618e99c40755717a4ddb062d/307e7/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.44171779141104%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABMklEQVR42n2SCQrDQAhF5/6XDNkTsu97LE+whNJWkBkd/X4dnTxk33fp+17atpVxHGWeZ+m6Tuq6VnuaJhmGQd95u+9b8zjt7rZt00AClmXR5DRNJcsyKctSwjAU3/fVRj3PU19VVVqMfANTQMCaplFmKEnYJBVFoUWMMRrHsSRJor4oijTGgBUQZrQBCGrBMKUAINyJgRVvMMdnMZwQgZw7z1NZEEQlKq7rqm3DgAR7ozhMUGKwEU7y8Dscx3EoAxz2MQTkea4+EmBHu7xhw4j7dV0aA0tm6Z6/zCOVYYOSQCF8tE672HQEqG0CJwTId59rY8MHkGTmgg0ogDAlDkbYCAwZnX6KfAi0YcD8giB4zwmfMUeNNaeBfQW0mdoawQYhydbruczPHfwJ+Eus/X/yAvkEU7NeiF3EAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/9210c181618e99c40755717a4ddb062d/a6d36/image-1.png&quot;
        srcset=&quot;/static/9210c181618e99c40755717a4ddb062d/222b7/image-1.png 163w,
/static/9210c181618e99c40755717a4ddb062d/ff46a/image-1.png 325w,
/static/9210c181618e99c40755717a4ddb062d/a6d36/image-1.png 650w,
/static/9210c181618e99c40755717a4ddb062d/e548f/image-1.png 975w,
/static/9210c181618e99c40755717a4ddb062d/3c492/image-1.png 1300w,
/static/9210c181618e99c40755717a4ddb062d/307e7/image-1.png 1354w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이렇듯 카프카가 없는 구조는 무엇이 문제가 될까? 당연하게도, 시스템을 확장하기 어려운 구조가 된다. 앞서 설명했듯이, 메시지 큐가 없다면 &lt;strong&gt;(1) 강한 의존성&lt;/strong&gt; 이 생기고, &lt;strong&gt;(2) 동기적 통신(synchoronous)&lt;/strong&gt; 이 발생하기 때문이다. 또한 앞선 그림처럼 &lt;strong&gt;(3) 복잡한 참조.의존관계&lt;/strong&gt; 를 가지게 된다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7fe9bad6c48febdac8cfa5263c548481/37048/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 77.91411042944786%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAABZklEQVR42p3T527DMAwEYL//C+ZvBrL33mHwCaDRJm3hVEAimSbvjie6Oh6PMZ1OY7vdxn6/j91uF6fTKWazWazX6zgcDrHZbEpstVrFfD6P8/lc8vOdXGe/6nq9xmg0KqBeKrCL9Xq9AqJ4uVxGp9OJ8Xj8LU98MpkUksvlEhVmxQqpA+yMVVyhZ8XeJxgQZ6p0olOrAGKVRMlisSg/yan6ayFbkMn3TCFCCgugv/v9XktWIBF4FiJE4lk8CdhFkPpcVbysx+NRE0jmY6vVKmBaztbk/bTeAG+3W222PQEAdrvdQkDZb+sNUNuDwaAUMp5KS8sJKKcxoFZ55oaBDIfDAiKGwAXwuTGgdhT2+/16xoCZQR4iTBs+ahkA33Ic2u12Aaect40BXQogqoxPjoXWKXTWcuNbfh0hbVMFBBlLPrrlvBg+5SBT6asBnhZ8DJifWs5lfpbi/wKkMHcK7dr+awYBPgGD09veKjbmZAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7fe9bad6c48febdac8cfa5263c548481/a6d36/image-2.png&quot;
        srcset=&quot;/static/7fe9bad6c48febdac8cfa5263c548481/222b7/image-2.png 163w,
/static/7fe9bad6c48febdac8cfa5263c548481/ff46a/image-2.png 325w,
/static/7fe9bad6c48febdac8cfa5263c548481/a6d36/image-2.png 650w,
/static/7fe9bad6c48febdac8cfa5263c548481/e548f/image-2.png 975w,
/static/7fe9bad6c48febdac8cfa5263c548481/3c492/image-2.png 1300w,
/static/7fe9bad6c48febdac8cfa5263c548481/37048/image-2.png 1352w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;반면 카프카를 도입하면 위처럼 시스템 아키텍처가 매우 단순해진다. 모든 이벤트와 데이터 흐름이 카프카에 중앙화되어 있다. &lt;strong&gt;생산자(Producer)&lt;/strong&gt; 는 이벤트 및 데이터를 카프카에 발행한다. 그리고 해당 이벤트 및 데이터를 필요한 곳(컨슈머) 에서 가져다 쓴다.&lt;/p&gt;
&lt;p&gt;예를들어 카카오 모행 서비스가 MSA 로 전환되고, 카프카를 도입하는 상황을 가정해보자. 사용자가 여행지 정보를 조회할 경우 여행지 조회 마이크로 서버로 Request 를 날려서 여행지 정보를 조회할 수 있을 것이다. 동시에 해당 마이크로 서버는 여행지가 클릭되었다는 메시지를 카프카로 전달한다. 이후 해당 메시지를 여행지 추천 시스템 마이크로 서버가 컨슘하여, 회원에게 어떤 여행지를 향후에 추천할지 정보를 최신화한다. 동시에 Logstash(로그 수집기) 또한 해당 메시지를 컨슘하여 개발자가 디버깅하기에 용이하도록 로그를 생성해둔다.&lt;/p&gt;
&lt;p&gt;만약 카프카가 없었더라념 각 서버의 통신은 어떻게 이루어졌을까? 여행지 조회 서비스가 추천 서버, Logstash 서버에게 각각 다른 데이터 파이프라인을 통해서 데이터를 전송해야 했을 것이다. 여기서 마이크로 서버가 더욱이 증설 된다면, 의존관계가 더욱이 복잡해 질 것이다. 이에 반해 카프카를 사용하여 데이터 흐름을 중앙화한하면, 복잡도와 성능이 극명히 개선될 것이다.&lt;/p&gt;
&lt;h2 id=&quot;카프카의-특징-vs-다른-mom&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B9%B4%ED%94%84%EC%B9%B4%EC%9D%98-%ED%8A%B9%EC%A7%95-vs-%EB%8B%A4%EB%A5%B8-mom&quot; aria-label=&quot;카프카의 특징 vs 다른 mom permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;카프카의 특징 (vs. 다른 MOM)&lt;/h2&gt;
&lt;p&gt;그렇다면, 카프카가 다른 MOM 와 다르게 갖는 특징이 무엇일까?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;높은 처리량(throughput) 과 낮은 지연시간(latency)&lt;/strong&gt; : 카프카는 대용량 데이터를 실시간으로 처리할 수 있도록 설계되었다. 따라서 높은 TPS 처리량을 가지며, 실시간 스트림, 로그 집계, 이벤트 드리븐 아키텍처 구현에 적합하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;메시지 내구성 (비휘발성)&lt;/strong&gt; : 카프카의 메시지는 휘발되지 않는다. 카프카의 메시지느 메모리가 아닌 디스크에 영구 저장된다. (이와 달리 Redis Pub/Sub 은 메시지가 디스크에 저장되지 않고, 구독자에서 장애가 터지면 메시지가 유실된다.) ActiveMQ, RabbitMQ 모두 디스크에 메시지를 영구 저장하는 옵션도 지원하지만, 기본적으로는 컨슘된 메시지를 유실된다. 반면, 카프카는 기본적으로 모든 메시지를 (이미 컨슘된 메시지까지) 디스크에 영구 저장한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;분산 아키텍처&lt;/strong&gt; : 카프카는 카프카 클러스터 내부에 여러대의 브로커(broker) 서버를 구성하여, 높은 확장성과 내결함성(Fault Tolerance) 을 갖는다. 이는 RabbitMQ, ActiveMQ 와 비교했을 떄 카프카만이 갖고 있는 차별점이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pull 기반 메시지 소비&lt;/strong&gt; : RabbitMQ 와 ActiveMQ 는 브로커가 컨슈머로 메시지를 Push 하는 방식인데 비해, 카프카는 컨슈머(consumer)가 능동적으로 알아서 브로커로부터 메시지를 가져오는 Pull 방식을 취했다. 이 덕분에 컨슈머(consumer)는 본인의 처리 능력에 따라서 메시지를 컨슘할 수 있기 때문에, 브로커 처리 능력에 연연하지 않고 본인만의 최적의 성능을 낼 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;카프카-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B9%B4%ED%94%84%EC%B9%B4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;카프카 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;카프카 아키텍처&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0b62dde784cc705884a9077a658eef96/75609/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 80.3680981595092%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC3klEQVR42o2U62/bVBiH87eDNIlSmMQHPjA0LuumSdsKG22BMsrKck+ci53bUjtX24kdO/ElS5zrw+naolbqoK/1ysdHx4/P+/PvPTFuifVmjTuzRA5w3w9wpoYYm4znQ5z3Jt7chS23Ruz6w/Zylbe0eVj+lEfKLt+XP+Pb7D32arv8KH/Ow+I9XmkP2G43dweOIp28+ZpoHFDOpaiXizTkEmpFJvJ9CsYpi8387kB3ZZFoPiNMPaV/8oDcsy9oHH6Nl36ElXxKvv+a5Ta6eGe7vZE3gZdlOAKYfPeEsPY3/sShUsxhdjWWswBL/hOp95sALi503N4UM2ZZNo7jYtsjPFHOBdAWwMf49RTmyKUkK7w7U3FcD7v6lnznVxaXO5xOp0wmHr4fEAQhsWQyQzyeJpMtCJ1k3KJCs1sgpe1jp18ylA4xcwcM8gdY0gFG9hcy2hFdKYUtFcim0iQSGRLxDMWiTKxWa1BRqlTEXW02mRkWvUGFpPqcWWqP/vFXZPc+obq/QxD/BjfxA7nuMeHEYuGOqdXq1OpNqtU6qta+pqHQwu90mI4mjLcOifoeYTOL501QSgV6bY1Z6DMq/f6h5JW4Qi/Ad0asooj1ei1yRezD3xG888mJouAUZFqGQqb1QuiVoKVWkdJvKOfjdLUqpgAW9GPcsYVdLBM2GqyEjlebil0NzmM5DZmdtWhrJZ5oO/zcuc+r3n2e13fYP9vlZedL9ju7HOrfMe73mUgSyyD4iA8FdLMRtlkJY891/lBfMPDGyK0GSWGbVFmiobfpOSan7SOieUAknHF9Qx81thMZHGmPUXSFROMtp8pfvJFPyKppij2Jk/ZPrDbR/3fKVaw2S4bzNsZcxV52LnLVwVqIudmZ+KB+t8Ph6qTpC330nonRN+m0upj6kIHIbrvHUNjK0AfCyP6/rfefwPMFjutgj2zROR4jYQt37Ar7eLiuy3jiis5yiKLbS/4HtWqjZkqG/fcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/0b62dde784cc705884a9077a658eef96/a6d36/image-3.png&quot;
        srcset=&quot;/static/0b62dde784cc705884a9077a658eef96/222b7/image-3.png 163w,
/static/0b62dde784cc705884a9077a658eef96/ff46a/image-3.png 325w,
/static/0b62dde784cc705884a9077a658eef96/a6d36/image-3.png 650w,
/static/0b62dde784cc705884a9077a658eef96/e548f/image-3.png 975w,
/static/0b62dde784cc705884a9077a658eef96/75609/image-3.png 994w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4b4a2590d8cb3f851e40feef6d6e5b3e/081d5/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.306748466257666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABfUlEQVR42n1Rx6oCQRCc/z8KIoo3c0IPogiCAcEVL4qKHlTMmAXFHOpRDbMMj/dsKHa2p7umqlu932/8h8fjgfv9jufzKdDnbz3qd+L1eoFxuVyQzWaRSCTg8/kQDoflXK1W5Z51Xwk/n4+dZFyvVwQCAUSjUSFMJpPwer3yCMPsM3uVaY+v8suC7XYLl8sFv98vxPF4HE6nE5FIRNSbo2CPTchmqrEsC5lMBoVCAblcDuVyGR6PRxQGg0Gk02m43W6xXSqVpKZYLCKfz6NWq+F4PIpypdXNZjOMRiNMJhPM53MMBgOxSDKqJBEJY7EYFouFXUuwl4opTplzYzDJOJ/PYjkUCgkJFTocDqRSqT9nqJekzO2al6fTCfV6XaxXKhWxSYvtdhu3201caXCONiEPBJP7/V6WMZ1OsVqt5P9wOGC322G9XmOz2WC5XGI8HotVEmsxGopJgoth0XA4RL/flxl2u11Bq9VCs9lEp9MR9Ho9NBoNeZwKdT/xA2Xp28KzfK+TAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/4b4a2590d8cb3f851e40feef6d6e5b3e/a6d36/image-4.png&quot;
        srcset=&quot;/static/4b4a2590d8cb3f851e40feef6d6e5b3e/222b7/image-4.png 163w,
/static/4b4a2590d8cb3f851e40feef6d6e5b3e/ff46a/image-4.png 325w,
/static/4b4a2590d8cb3f851e40feef6d6e5b3e/a6d36/image-4.png 650w,
/static/4b4a2590d8cb3f851e40feef6d6e5b3e/e548f/image-4.png 975w,
/static/4b4a2590d8cb3f851e40feef6d6e5b3e/081d5/image-4.png 1264w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;카프카 클러스터(kafka Cluster)&lt;/strong&gt; : 하나 이상의 카프카 브로커들의 집합이다. 카프카는 확장성과 내결함성을 위해 브로커들을 클러스터로 구성한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;브로커(broker)&lt;/strong&gt; : 브로커는 개별 카프카 서버이다. 브로커는 프로듀서로부터 메시지를 전달받아서 토픽(topic) 에 저장하고, 컨슈머에게 전달하는 역할을 한다. 브로커는 여러개의 토픽을 가질 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;토픽(topic)&lt;/strong&gt; : 토픽은 데이터가 저장되는 단위라고 할 수 있다. 마치 데이터베이스에서 테이블(Table) 과 거의 똑같은 개념이다. 토픽은 이름으로 식별되며, 토픽에 한번 추가된 데이터는 수정될 수 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;파티션(partition)&lt;/strong&gt; : 카프카의 확장성을 위해서 토픽은 1개 이상의 여러 파티션으로 나뉠 수 있다. 레코드에 키가 없다면 라운드 로빈으로 파티션에 나뉘어 저장되고, 같은 키를 가진 레코드는 같은 파티션에 저장된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;파티션의 내부 구조는 Queue 구조로 이루어져 있다. 다시말해, FIFO 의 특징을 갖는다.&lt;/li&gt;
&lt;li&gt;컨슈머는 파티션에 적재된 데이터를 가져가더라도, 파티션에 적재된 삭제가 되지 않는다. 즉, 컨슈머는 파티션에서 이미 가져온 데이터를 또 다시 가져가는 것도 가능하다. (몰론, 어떤 특정 컨슈머가 어떤 데이터까지 읽었는지는 내부적으로 기록을 하고 있다. 이렇게 어디까지 레코드를 읽었는지 기록하는 행위를 &apos;커밋&apos; 이라고 한다. 컨슈머는 레코드를 읽어온 뒤 커밋을 하고, 앞서 커밋한 내용을 기반으로 어디서부터 새로운 레코드를 읽어와야 하는지 정보를 가져온다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;오프셋(offset)&lt;/strong&gt; : 파티션에 저장된 레코드는 증가하는 정수 ID 값을 갖고, 이 정수 ID 를 오프셋이라고 부른다. 오프셋은 0부터 시작하며, 레코드가 파티션에 저장될 떄 마다 시퀀셜하게(순차적으로) 증가한다. 특정 파티션의 각 레콤드는 고유한 오프셋을 갖지만, 서로 다른 파티션 간에는 고유하지 않다. 파티션에서 데이터를 읽을 때 작은 것부터 큰 순서대로 읽는다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;레코드(record)&lt;/strong&gt; : 파티션에 저장되는 데이터이다. Key, Value, TimeStamp, Compression Type, Optional Headers, Partition and Offset Id 로 구성된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;프로듀서(producer)&lt;/strong&gt; : 카프카에 요청하여 토픽에 레코드는 추가하는 클라이언트이다. 카프카의 구성 요소가 아니며, 카프카 외부에서 카프카에게 Request 하는 애플리케이션이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;컨슈머(consumer)&lt;/strong&gt; : 하나 이상의 파티션과 토픽으로부터 레코드를 읽어오는 클라이언트이다. 기본적으로 사용 가능한 가장 낮은 오프셋부터 높은 오프셋까지 순서대로 레코드를 읽어온다. 하나의 토픽의 여러 파티션으로부터 레코드를 읽어올 때는 순서가 보장되지 않는다. 파티션 0, 1, 2 로 부터 레코드를 읽어올 떄 파티션 0의 레코드만 바라봤을 때는 순서가 보장되지만, 읽어온 전체 레코드를 바라볼 때는 파티션 0 ~ 2 의 레코드가 순서와 상관없이 섞여있을 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;컨슈머 그룹(consumer group)&lt;/strong&gt; : 동일한 컨슈머 인스턴스를 여러개 생성하여 컨슈머 그룹을 구성할 수 있다. 컨슈머 그룹을 구성하는 여러 컨슈머는 동일한 토픽의 각자 다른 파티션을 도맡아 메시지를 컨슘할 수 있다. 예를들어 토픽 A 에 파티션이 0, 1, 2 가 생성되어 있고, 컨슈머 그룹 A 에 컨슈머 a, b, c 가 있다고 가정하자. 이 경우 컨슈머 a 는 파티션 0을, 컨슈머 b는 파티션 1을, 컨슈머 c는 파티션 2를 컨슘한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/what-is-kafka/&quot;&gt;https://hudi.blog/what-is-kafka/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=catN_YhV6To&quot;&gt;https://www.youtube.com/watch?v=catN_YhV6To&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-09-19-message-queue/&quot;&gt;https://tecoble.techcourse.co.kr/post/2021-09-19-message-queue/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[ELK Stack 전반 개요 살펴보기]]></title><description><![CDATA[회사에서 ELK 스택을 통해 애플리케이션을 로깅하고, 애러를 검출하고 있다. ELK 스택에 관련한 설명을 들을 때 기술에 대한 이해도가 낮아서 학습의 필요성을 느끼게 되었다. ELK…]]></description><link>https://haon.site/infra/elk-stack-overview/</link><guid isPermaLink="false">https://haon.site/infra/elk-stack-overview/</guid><pubDate>Sun, 17 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/26001ed58b382ae084bd777f8e5d1ed5/7a18f/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.122699386503065%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABvklEQVR42pVTSy8DURjt1s5G2FrYSPgBYkGCjSZ2IrGRWAkW1DMkYmnDQmKBjUdiIWHlUY1HRJmZNJnKoAvaMm0y6lHTKTFt597jdoxqPRq+5Mud79wz3+Pce234atR495yY5nIM8ulf9mz4k1H81WzZP5CEiuTNLIzrKVD9lsEpvAYmkYpJJoOCmDx99wLxZTfzY5B7zUpBsxJagSwu4WrLDoVrhB7ZYngCqYMCELEmq1mKSOcCHspH8DKxyUKaM4TtA1A1DfbqKtQ0taFi4xGnMQYmVWxPl2JvvhKccg3/c9zkjrZ3oa+5AxEtbtWgP2jIwOGVRTTMzaDFLeDg7taE62vrUFhcgrLVOTgknulC0NrTjaLpMbR69hFPJqwGs0YmVoVzTcWQJGD8nIMv9mhiZ5KE3sEBdB/tYEm+NE92hj9Ev9cNB/Mz9Z1HcjTMNPnZuhp9QjQa/banMWkMw8jIRvNdm48qr7oOnucRDAYhiiJkWYbP50MoFIIgCAgGAmxyku/afDdFUeByuXByfAKO47C2vgav1wuPxwOn0/n/hOFw2Owo7X6/3yyQ/k6vaSl0NsW/EuZ9N/T3l/MGPS58OZ/eF+MAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/26001ed58b382ae084bd777f8e5d1ed5/a6d36/image.png&quot;
        srcset=&quot;/static/26001ed58b382ae084bd777f8e5d1ed5/222b7/image.png 163w,
/static/26001ed58b382ae084bd777f8e5d1ed5/ff46a/image.png 325w,
/static/26001ed58b382ae084bd777f8e5d1ed5/a6d36/image.png 650w,
/static/26001ed58b382ae084bd777f8e5d1ed5/e548f/image.png 975w,
/static/26001ed58b382ae084bd777f8e5d1ed5/7a18f/image.png 1284w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;회사에서 ELK 스택을 통해 애플리케이션을 로깅하고, 애러를 검출하고 있다. ELK 스택에 관련한 설명을 들을 때 기술에 대한 이해도가 낮아서 학습의 필요성을 느끼게 되었다. ELK 스택에서 각각의 컴포넌트가 어떠한 역할을 수행하며, 데이터가 어떻게 흐르고, 처리되고, 검색되고, 저장되고, 시각화 되는 것인지 전반 개요를 간단히 살펴봐야 겠다는 생각이 들었다.&lt;/p&gt;
&lt;p&gt;이번 포스팅에서는 ELK 스택 하나하나에 대한 기술을 깊게 공부하는 목적 보다는, 어떤 역할을 수행하고, 어떤 데이터 처리 흐름을 수행하는 것인지 개요를 학습해보도록 한다. 또한 &lt;a href=&quot;https://hudi.blog/elk-stack-overview/&quot;&gt;참고1&lt;/a&gt; 을 다수 참고.인용하여 빠르게 학습을 해보도록 하겠다.&lt;/p&gt;
&lt;h2 id=&quot;로그는-왜-필요할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EA%B7%B8%EB%8A%94-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;로그는 왜 필요할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로그는 왜 필요할까?&lt;/h2&gt;
&lt;p&gt;ELK 스택은 로그 수집, 처리 및 분석에 사용되는데, 왜 로그를 수집하고 처리하는 것일까? 그 이유를 정리해보자면 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;장애시 원인 파악&lt;/strong&gt; : 로그를 분석하여 장애가 터진 경우 빠르게 원인을 파악하고 해결할 수 있다. 비슷한 원리로, 대용량 배치 작업의 경우 작업 개수가 굉장히 많고 오랜 시간이 걸릴텐데, 전체 배치 작업 중에 실패한 작업에 대해 로깅을 하여 원인을 파악하고 복구하기가 쉬워진다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;사용자 패턴 분석&lt;/strong&gt; : 이슈가 발생하여 CS 가 인입되었을 때를 대응하기 위해서, 해당 사용자가 어떤 액션을 취했는지, 어떤 데이터가 요청되었는지를 확인할 필요가 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;시스템내 성능.병목.취약 구간 파악&lt;/strong&gt; : 사용자의 Request 가 들어오고 Response 가 나가기까지의 시간을 기록하여, 시스템내 성능 저하 및 병목 구간을 파악할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;elk-스택-기반-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#elk-%EC%8A%A4%ED%83%9D-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;elk 스택 기반 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ELK 스택 기반 아키텍처&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c2d53e8e09c86e11518b56fd850e4201/307e7/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.39877300613497%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABcklEQVR42o1Su0oDQRQdO/9AsE1p7y9o7Q/4C36BbQRLCwstRAyKQiwUEbSIQmJEEC2iJmziEmLMy2Rnd+PO7s7McXY3b1Ryh8OduY9zZ+4dAiUSQ/F9DkptuK6H/0T+YSCyt+nrb4dB00qK1Pw1VY5Y5C/sxHQlyiYfJvSYQ9Wv4lXV1ZuDGMp9lJgzZBESsmmrOA6yktAxH8/hPG+FPod5MAwKphICOo/m0L2NwUwvgjt12I6PpWwKM5dJHOrFqMBVDpXVXdD9O5CFzWeQtRts33+FTsPqQiu+o91uh2ezmoJ+MotScg6uUUC5YSJ2egSS2MF6NhPG1A/SeFyO42PjDOShyrD3ZMF2xdiTMXi6AK8dQ7QuBq3K0A62Kjq6PGqVoAzs+g38kwZDUUQKQkSEhmHi5bWAWi3qGecClmqX5ciwWLDAFRhXaVEJruyW4PCUJpPTD75LPq+pKVvj459SCKUUozAU6o0WOh0Dk75p8APA8qdGISP8ZwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/c2d53e8e09c86e11518b56fd850e4201/a6d36/image-1.png&quot;
        srcset=&quot;/static/c2d53e8e09c86e11518b56fd850e4201/222b7/image-1.png 163w,
/static/c2d53e8e09c86e11518b56fd850e4201/ff46a/image-1.png 325w,
/static/c2d53e8e09c86e11518b56fd850e4201/a6d36/image-1.png 650w,
/static/c2d53e8e09c86e11518b56fd850e4201/e548f/image-1.png 975w,
/static/c2d53e8e09c86e11518b56fd850e4201/3c492/image-1.png 1300w,
/static/c2d53e8e09c86e11518b56fd850e4201/307e7/image-1.png 1354w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;ELK 는 &lt;code class=&quot;language-text&quot;&gt;Elastic Search&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Logstash&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Kibana&lt;/code&gt; 를 합친 약자로, 로그 데이터를 수집하고 분석하는 도구 모음을 뜻한다. 여기에 &lt;code class=&quot;language-text&quot;&gt;Beats&lt;/code&gt; 라는 경량 데이터 수집 도구가 추가되어서, 이를 묶어 ELK Stack 이라고 부른다. &lt;code class=&quot;language-text&quot;&gt;Elastic Search&lt;/code&gt; 는 검색 엔진, &lt;code class=&quot;language-text&quot;&gt;Logstash&lt;/code&gt; 는 데이터 처리 파이프라인 도구, &lt;code class=&quot;language-text&quot;&gt;Kibana&lt;/code&gt; 는 데이터 시각 대시보드, &lt;code class=&quot;language-text&quot;&gt;Beats&lt;/code&gt; 는 경량 데이터 수집기이다. 그렇다면 ELK 스택에서 데이터는 어떻게 발생하고, 흐르고, 처리되고, 저장되며, 검색되고, 시각화되는 것일까?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 에이전트(Agent) 란? : 시스템에서 특정 작업을 수행하도록 설계된 SW 프로그램이다. 일반적으로 백그라운드에서 실행되고, 사용자의 개입없이 자동으로 특정 데이터를 수집하거나 작업을 처리한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;Beats&lt;/code&gt; 는 로그 또는 메트릭이 발생하는 서버에 에이전트로 설치되어 데이터를 수집하고, 이 데이터를 &lt;code class=&quot;language-text&quot;&gt;LogStash&lt;/code&gt; 로 전송한다. 위 다이어그램에는 &lt;code class=&quot;language-text&quot;&gt;Beats&lt;/code&gt; 는 &lt;code class=&quot;language-text&quot;&gt;LogStash&lt;/code&gt; 사이에 메시지 큐가 위치하는 것을 확인할 수 있는데, 이는 안정성 또는 확장성 등을 위함이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;Beats&lt;/code&gt; (혹은 메시지 큐) 로 부터 데이터를 전달받은 &lt;code class=&quot;language-text&quot;&gt;LogStash&lt;/code&gt; 는 데이터를 적절히 필터링 및 가공하여 &lt;code class=&quot;language-text&quot;&gt;Elastic Search&lt;/code&gt; 로 전달한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;LogStash&lt;/code&gt; 로 부터 데이터를 전달받은 &lt;code class=&quot;language-text&quot;&gt;Elastic Search&lt;/code&gt; 는 데이터를 굉장히 빠른 속도로 검색될 수 있도록 역 인덱싱(Inverted Indexing)한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;Kibana&lt;/code&gt; 는 REST API 를 통해 &lt;code class=&quot;language-text&quot;&gt;Elastic Search&lt;/code&gt; 로 부터 데이터를 가져와서 유저에게 시각화와 간편한 데이터 검색 기능등을 제공한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;elastic-search&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#elastic-search&quot; aria-label=&quot;elastic search permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Elastic Search&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fd4361db31cc1d1562ec8bc2782379aa/21482/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.920245398773%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAv0lEQVR42sWSTQrCMBCFexmv4z3Evfdw6xFcuxS7rjaptjZpom6kiyK6VrCZZ5oiKCj+ooHhDRPyvZkhHr58vB8DqZbjAtiLz4BExmm+FBj0mojTrjOgd4BEhLIsXT7yh2i0OmhPtw5oDLn7Kl7ssH5wsNrfrOHv8otF0PMdGmPAGQMLQ0Scg01C6DjBSkpEjCOexRgHAZTSV8Z3gdWoC60hhYQQAirLkCmF1ObzJLF1AW1hRVH88ducl/4obgFPQ9sLWPlOA2wAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/fd4361db31cc1d1562ec8bc2782379aa/a6d36/image-2.png&quot;
        srcset=&quot;/static/fd4361db31cc1d1562ec8bc2782379aa/222b7/image-2.png 163w,
/static/fd4361db31cc1d1562ec8bc2782379aa/ff46a/image-2.png 325w,
/static/fd4361db31cc1d1562ec8bc2782379aa/a6d36/image-2.png 650w,
/static/fd4361db31cc1d1562ec8bc2782379aa/e548f/image-2.png 975w,
/static/fd4361db31cc1d1562ec8bc2782379aa/3c492/image-2.png 1300w,
/static/fd4361db31cc1d1562ec8bc2782379aa/21482/image-2.png 1350w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 Elastic Search 에 대한 자세한 내용은 &lt;a href=&quot;https://haon.blog/database/elastic-search-basic/&quot;&gt;Elastic Search 역 인덱스(Inverted Index) 구조 살펴보기&lt;/a&gt; 에서 다룬적이 있으니 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ELK 스택은 Elastic Search 로 부터 시작된다. Elastic Search 는 Lucene 기반 검색엔진 (경우에 따라 NoSQL DB 로 분류하기도 한다) 이며, 대용량 데이터를 빠르게 저장하고 검색할 수 있게 해주는 툴이다. 데이터를 빠르게 검색하기 위해 &lt;strong&gt;역 인덱싱(Inverted Indexing)&lt;/strong&gt; 하여 저장한다. 역 인덱싱과 관련한 내용은 위 포스팅에서 다룬적이 있으므로 생략한다.&lt;/p&gt;
&lt;p&gt;Elastic Search 는 클러스터 구조로 분산되어 구성되어 있다. 이 덕분에 높은 가용성과 확장성이라는 특징을 갖는다. 또한 일반적인 관계형 DB 와 다르게 SQL 을 지원하지 않고, &lt;strong&gt;REST API 를 통해서 데이터를 추가하고 검색해야 한다.&lt;/strong&gt; SQL 의 SELECT, INSERT, UPDATE, DELETE 명령은 REST API 기반의 Elastic Search 에서 GET, POST, PUT, DELETE 에 대응된다.&lt;/p&gt;
&lt;p&gt;관계형 DB 의 데이터베이스, 테이블, row, 컬럼, 스키마는 각각 Index, Type, Document, Field, Mapping 에 대응된다.&lt;/p&gt;
&lt;p&gt;또한 엘라스틱 서치는 일반 관계형 DB 와 다르게 JOIN 절을 지원하지 않고, 트랜잭션을 지원하지 않는다는 점이 단점이 될 수 있겠다.&lt;/p&gt;
&lt;h2 id=&quot;logstash&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#logstash&quot; aria-label=&quot;logstash permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LogStash&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8af24d1f569a01ec1084251abf58ed3e/9685e/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.21472392638037%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB5klEQVR42nVTuY7TYBB2zQPQ00Yi5VJR8BxI6emRtowUnoEKIfEAJEFLs1tFYGVTRFxZKcviWArrNb7j+z4+/vk3B1HCSGPL8835zVgAk8VigVarhdFoRJ+oqgr/k7quoGkaZFmG4zgHuEAPz/PQ7XYxHo/RNA0LqtdwcxBgGAbSNEVRFEcLC67rYj6fb8EgCLjuZJeU7HEcI2Rv3TSgGjbsVbSfcDabodfr8fZ93+fV/2gm0kBBHd2gWufL8xy2bfNpvMBHHeTI7z4jds72CgtUkZy5iY0bhT68sEZjfQAuHwLxFcfSJIZlWUiSlJjE+bcCy0/PoV2dQPWKbbzQ6XQwHA75B3W5WjnIGH57fY43Lx/gvfgOX6IEdVkwbIUgilB4Ll5LFh6LZ3jx8S2+K/b9wijhZDIB8bgR6jjLM2iGhadPnuFR9xSnix9gW7gfl2mZZXDyBK/kX7gOZbqLLdMCVRVFERlzIg4pYHMOsnSDi69T/HQdlCyhw3zLsuRcKr9v4SgK9DsdRbnbtjAYDNBut6HrOjdQgGmajKsECguwNR1JEHKMbITx02JaMN+CUcEMu4RE9HQ65Sehqiofn24MRw8HbGkhlsslJEniEx3cYbPOTp32+/29P4Wwjf4rhNMkxw77L4ysN5zageuyAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/8af24d1f569a01ec1084251abf58ed3e/a6d36/image-3.png&quot;
        srcset=&quot;/static/8af24d1f569a01ec1084251abf58ed3e/222b7/image-3.png 163w,
/static/8af24d1f569a01ec1084251abf58ed3e/ff46a/image-3.png 325w,
/static/8af24d1f569a01ec1084251abf58ed3e/a6d36/image-3.png 650w,
/static/8af24d1f569a01ec1084251abf58ed3e/e548f/image-3.png 975w,
/static/8af24d1f569a01ec1084251abf58ed3e/3c492/image-3.png 1300w,
/static/8af24d1f569a01ec1084251abf58ed3e/9685e/image-3.png 1336w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Elastic Search 를 사용하면서 데이터를 손쉽게 수집할 방법이 필요해졌다. 따라서 데이터 처리 파이프라인 툴인 LogStash 가 등장했다. LogStash 는 &lt;code class=&quot;language-text&quot;&gt;Input&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Filter&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Output&lt;/code&gt; 순서로 데이터를 수집하고, 가공하고, 전달한다.&lt;/p&gt;
&lt;h3 id=&quot;input&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#input&quot; aria-label=&quot;input permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Input&lt;/h3&gt;
&lt;p&gt;Logstash 는 다수의 데이터 소스로부터 동시에 로그 또는 메트릭 데이터를 수집한다. 수집하는 데이터 소스는 Beats, File, TCP, HTTP, Kafka, Redis 등이 있다.&lt;/p&gt;
&lt;h3 id=&quot;filter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#filter&quot; aria-label=&quot;filter permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Filter&lt;/h3&gt;
&lt;p&gt;Logstash 는 Input 를 Output 로 변환하는 사이에 데이터를 Filter 하는 과정을 거치는데, Filter 과정을 통해 아래와 같은 일을 수행할 수 있다. Filter 란 Input 데이터를 필터링하거나, 원하는 형식으로 가공하는 작업이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;grok 플러그인을 사용해서 정규 표현식을 통해 비정형 데이터로부터 구조를 도출해낼 수 있다.&lt;/li&gt;
&lt;li&gt;IP 주소로부터 위치 자표를 얻어올 수 있다.&lt;/li&gt;
&lt;li&gt;개인 식별 정보를 익명화하거나 완전히 제거할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위와 같은 데이터 가공.필터링 작업은 Logstash 자체에서 제공하는 다양한 플러그인들로 코딩없이 간단히 작업할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;output&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#output&quot; aria-label=&quot;output permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Output&lt;/h3&gt;
&lt;p&gt;Input 과 마찬가지로 Logstash 는 다양한 데이터 소스로 출력할 수 있다. Output 의 예시로는 Elastic Search, Email, File, TCP, HTTP, Kafka, RabbitMQ, Redis, S3, WebSocket 등이 있다.&lt;/p&gt;
&lt;h2 id=&quot;kibana&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#kibana&quot; aria-label=&quot;kibana permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Kibana&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/98c79295e9dc33bc2f9a45265515b0c1/e5ca1/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.05521472392638%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC10lEQVR42m2S228TVxDG94/grc/9E3joA2pBoFIQtFWVtCWEVpWqqq36QqBSg1qR3oRaQIVHJEKq4MTxNQnpNfShQAgpSVBCfYvXG18SX4gd79pr79rr9a/jSLz1HH2ab86MznyaGWU0GMAbCOOd9OHxeAkGp5mZvkM4PLOH2dk55uZ+IyTcL3mBHiSnZ6f8IcYn/Xi8AUGQcV8IpVw1iGzkWI+rRNUM8dQWsWRmj6+rWWKJTYFGzWzSO91ul07H3bOu68qLcLnPY8pWvsT1MR9XR71c+3mKK2J/uDnB5VuTwn1cHpvimvhqZgvH6VCrm1R1A8uyMYw6zWYPDToSa1sWii4JG4UC2k4ZzTDZlMdss4lmmmi7VVLFEhvFImbT6okRFT0p/O/p6VXquo5T16GyhRu8Sf78EIuffEZl5AtYu4frtOQDl0fJGP7lBRbLWRYLGeYfJ3mUKPEwXmAl8YzM3X8wluMormXi6GXsdIT2T0PE97/IHy/so/TRmzgr81j6Dpg1PKlVziz4+Hg5zLmlu5wbj3LeE+XsRIyvbz8l9/kN9O8nUNqNOo6xg1Or0FGf4P4doDvvwY0v0dGf0ZIYdhNvYoUP7/sYWp3jwv3f+SqcZGQ2xfC0xqVQnPSFMYwfZ0Rhu0XXquPaJm69SqeSx90VSAFXGt5uGHv98WhPOPNwik9XZxl+8Au3RhcI31GZDj3mwa9rRP23yd+bRKlI48uVXXSjRqvVlolZ1GVQPW4LLFt62OkSKW3zZzrOX9kES9k02fUKmacVDLWIrZVp7iYkdw1l5LtL9L17mlOD7zPw3gf0nxqkX/y+dwZ4e2CQ19/q48uL36BL4cbmJnZ+m65sALRlWI7UcrHtBtVSmWpmG2X44rccevU1jp14g+MnTnL46DGOHD3Oy4eOcODgYV468Ip82k9R1icXy5CNaOT+TVJIbbOTK1NMpslFNshGVYmp/AeTSuOCK/5tuAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/98c79295e9dc33bc2f9a45265515b0c1/a6d36/image-4.png&quot;
        srcset=&quot;/static/98c79295e9dc33bc2f9a45265515b0c1/222b7/image-4.png 163w,
/static/98c79295e9dc33bc2f9a45265515b0c1/ff46a/image-4.png 325w,
/static/98c79295e9dc33bc2f9a45265515b0c1/a6d36/image-4.png 650w,
/static/98c79295e9dc33bc2f9a45265515b0c1/e548f/image-4.png 975w,
/static/98c79295e9dc33bc2f9a45265515b0c1/3c492/image-4.png 1300w,
/static/98c79295e9dc33bc2f9a45265515b0c1/e5ca1/image-4.png 1332w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Elastic Search 만으로 데이터를 검색하는 것은 가능하긴 하지만, Elastic Search 만 단독으로 사용하면 Rest API 를 호출해야만하기 때문에 불편하다. 따라서 Elastic Search 에 저장된 데이터를 보기 쉽도록 시각화 할 도구가 필요한데, 이를 위해 Kibana 를 사용한다. Kibana 는 HTML + JavaScript 엔진으로 사용자가 Elastic Search 에 저장된 데이터를 쉽게 검색하고, 분석하는데 도움을 준다.&lt;/p&gt;
&lt;p&gt;Kibana 는 일반적인 텍스트 데이터를 비롯하여, 히스토그램, 차트, 위치 데이터, 시계열, 그래프 등 다양한 시각화를 제공한다. 또한 사용자가 쉽게 데이터를 검색하고 필터링할 수 있는 기능을 제공한다. 추가적으로 Kibana 는 데이터를 모니터링하여 사용자에게 경고 알림을 제공하는 기능도 존재한다.&lt;/p&gt;
&lt;h2 id=&quot;beats&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#beats&quot; aria-label=&quot;beats permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Beats&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/16ce4619116876e74fb60202bb1ecc9e/47218/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.34969325153374%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElEQVR42oWSPW8TQRCG709Q0/MHKEGioUCigD9AQYkEiiJBIiQkKNKQAtHSpLBkpCBAJAQIwpDvDzlxYhPFIbJJ7MTx7d354+yzb/fuYe/sBAqSjLSa3dl33p15Z41OEKJC8MOQrl56G1ul02HFseJ9HJOHIJLaOz1A2Ecu/0bO/dLH3tnI1KrcmN5gzVZ0woBEMU+qUmVWCAYySzGopVfi5TCjiSFEt8ZB1aJQPojjr28OUkikem8EAcaMuc+F5A8WzYCNhsmVzxPcm8/wzazweHNVVw2BBo9OTHL94wLFOKDwlNI+5PaLEQay8xyb4UjJYdfHkQrhS4T0aevyZ0SVoc0VVNStxpT0eX1vB2EJ/rV8tUJ6r4gMgh5hruEwtpPH9RWWHzBeKLEq6sxbFsN9QqXBvu/jdmXsg37ycZtRpScVfjgocvHtGEvmEZ6+uzo5wchGlrzr8nRrnabqDSBKEU2XXDaLbdv9ufwd4gnhrtvgVXGbktfSgium9UDSTpMFW/Akl0ZGkunEqCZXy9Jut2MJTjPD65fb1r4uA2oqiJNnhcnDqOX+UKhNgZc9bvR0wqmSx+Xkd7brAtOHS+MrPM/9JF2z46FEYkfpmfd3eDd+l33VIzyN0thtdLn/Jc1Ry8OWXW6l5ni2abHsmDxYW4xB+h1Sn75y7dEgb4724lgQ/p/SaCiJpXzq+l85+tuUO56uVLLVbJDcL5wAPc+jVC7T0hqeZUYkYfRYeKYyxF8lDEPOsz/+soCc4Pvk8AAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/16ce4619116876e74fb60202bb1ecc9e/a6d36/image-5.png&quot;
        srcset=&quot;/static/16ce4619116876e74fb60202bb1ecc9e/222b7/image-5.png 163w,
/static/16ce4619116876e74fb60202bb1ecc9e/ff46a/image-5.png 325w,
/static/16ce4619116876e74fb60202bb1ecc9e/a6d36/image-5.png 650w,
/static/16ce4619116876e74fb60202bb1ecc9e/e548f/image-5.png 975w,
/static/16ce4619116876e74fb60202bb1ecc9e/3c492/image-5.png 1300w,
/static/16ce4619116876e74fb60202bb1ecc9e/47218/image-5.png 1344w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;마지막으로 Beats 는 로그, 메트릭 등의 다양한 데이터 소스에서 다양한 유형의 데이터를 수집하고 전달하는 경량 데이터 수집기이다. 일반적으로 Beats 는 Logstash 또는 메시지 큐에다 수집한 데이터를 전달한다.&lt;/p&gt;
&lt;p&gt;Beats 는 수집하는 데이터 소스, 형태에 따라 아래와 같이 종류가 구분된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;FileBeat&lt;/strong&gt; : 서버의 로그 파일을 수집한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;MetricBeat&lt;/strong&gt; : 서버의 CPU, 메모리, 디스크 용량과 같은 시스템 메트릭이나 MySQL, Nginx 같은 서비스 메트릭 정보를 수집한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PacketBeat&lt;/strong&gt; : 서버와 클라이언트 간의 트래픽을 실시간으로 모니터링해서 네트워크 상태 정보를 수집한다. HTTP, DNS, MySQL 과 같이 다양한 프로토콜에서 트래픽 정보를 수집한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;WinlogBeat&lt;/strong&gt; : 윈도우 시스템에서 발생하는 이벤트 로그를 수집한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AuditBeat&lt;/strong&gt; : 리눅스 시스템에서 발생하는 이벤트 로그를 수집한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Hearbeat&lt;/strong&gt; : 주기적으로 지정된 URL 로 요청을 보내서 서비스 상태를 모니터링한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;FunctionBeat&lt;/strong&gt; : AWS Lambda, Google Cloud Functions 등의 FaaS (Function-as-a-Service) 플랫폼에서 데이터를 수집한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/elk-stack-overview/&quot;&gt;https://hudi.blog/elk-stack-overview/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/ko/what-is/elk-stack/&quot;&gt;https://aws.amazon.com/ko/what-is/elk-stack/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[LINE 서버 개발자가 되기까지, 2025년 상반기 회고]]></title><description><![CDATA[LINE 개발자, LINER가 되었다  2025년 상반기 대학교 졸업과 동시에, LINE…]]></description><link>https://haon.site/회고/2025-success-iine-server-developer/</link><guid isPermaLink="false">https://haon.site/회고/2025-success-iine-server-developer/</guid><pubDate>Tue, 22 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;line-개발자-liner가-되었다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#line-%EA%B0%9C%EB%B0%9C%EC%9E%90-liner%EA%B0%80-%EB%90%98%EC%97%88%EB%8B%A4&quot; aria-label=&quot;line 개발자 liner가 되었다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LINE 개발자, LINER가 되었다&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/efea1094023c21f7b4d9c9eae0f4aa58/59058/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.668711656441715%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAADTUlEQVR42h2TbU/bZRjF/1/Ab2DM3hmjL8xi4gfQGM0WEx+yLSrMQWDSIZYNZqEdrNCuD9AHoKUPFNrSQltooVBoS6FrgTFYB+wBnS5icAQkTreMhS0KjJ83vDi5c65cOfe57uvckv1aDf12NfEePZ5WBdEeC4l+G6mohz63EYOqEqdRQbf1Ci0N3wsuw6i6QOOlEtqbq4l26Yi6tEQcGgJtDUjWxkr0deXoFeVoa8tw6BU4xCUuYz1+m4auFgUeAa+p/gg9rYf8Mh1NP9AqxE1KGR1XZXQ2V2FSVSAF9HLG3GryITNTfSamY25mRvzkBbLDXqaGfOTifoHeozMvMC3qM0Nd5MLtTPj0TAUMTPYaGHGqkN5/+xhWVTl/FgZYne3nj/kY64tjPN96yPPHj/hr7QHPNlcF/53tx2tsb/3G5p00G4U4j+YH2LgZYipsxqC9SKtwLh1/6w0aL5ymMGBhOmBkLmJlPtbBk60NDoD/9l/xz/4eT/f2eCUKL3d2uJvycTtm49ZgG0sxq3g/FbXKMmweA9J77xxDIy9iedjBXNjKgmicH7Lz9+Y67MGDF0/o3nlI/MUa7B6ws/2Me5kAdxJulkfcrIy6iHvVnK86xVfFnyC9++br6KqL+DXtoSCEbo84WBbNT7c2ORCO/hUOX+7usru3f8QPHa4IwcPepeFOfhrvIR1ooam+CI3qHNKpD47TJDtJV3MFIaOMhL2GmzEHP+eTLGUHuTszzGI+zf0b4xQyYX6Zy3IvHyfiMpIJuxj1WlFUFtOjPc/sSDeS3ygnarlEbcmXXDz7OYrSL1A3yFhfXiAXDRP3O5lLx5iMBkVezYz1Obk+1k9hIsL1QTd2XQ3KitNMeK6wNC4Evz7zITb9ZXztas5+8zGlxSfQ6OR8NKjFlgiymE6STUS4lcuQGxsmORoh7LXQ16Yk5tGhbZYTNNeSErHJ+K4h1X53Eotahs9aT1Xpp1SXfSZ+hYrXlCfQhi2sTotR032sTI+ymI2TGgoyFGwn4Ggm2dtCp6lO5NAoFmljJmJBiliq8OsqMf/4LQb5GdrqSgh1qAjZFeRiLuEwzELCx2IqxEIyzORANxGnhn5bAwn3VaLORmZFfApimbMRM/8DE36170+HpBEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/efea1094023c21f7b4d9c9eae0f4aa58/a6d36/image.png&quot;
        srcset=&quot;/static/efea1094023c21f7b4d9c9eae0f4aa58/222b7/image.png 163w,
/static/efea1094023c21f7b4d9c9eae0f4aa58/ff46a/image.png 325w,
/static/efea1094023c21f7b4d9c9eae0f4aa58/a6d36/image.png 650w,
/static/efea1094023c21f7b4d9c9eae0f4aa58/e548f/image.png 975w,
/static/efea1094023c21f7b4d9c9eae0f4aa58/3c492/image.png 1300w,
/static/efea1094023c21f7b4d9c9eae0f4aa58/59058/image.png 2376w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;figcaption&gt; LINE Plus 분당 오피스 - 공용 오피스 및 라운지 &lt;/figcaption&gt;
&lt;p&gt;2025년 상반기 대학교 졸업과 동시에, LINE 개발자로 최종합격 및 입사 예정을 앞두고 있다. 내 직무 역량을 좋게 봐주셨는지, 신입 개발자임에도 경력직 채용 서버 개발자 포지션에 최종 합격하여 입사하게 되었다. LINE 개발자가 되기까지 몇년동안 피땀흘리며 노력했던 과정들을 회고해볼까 한다. 2025년 상반기를 더 중점으로 회고해볼까 하지만, 2025년이 아니더라도 그 동안의 성장 과정을 다시금 길게 회고해볼까 한다.&lt;/p&gt;
&lt;h2 id=&quot;멋쟁이사자차럼-동아리-백엔드의-첫-걸음-시작&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B0%A8%EB%9F%BC-%EB%8F%99%EC%95%84%EB%A6%AC-%EB%B0%B1%EC%97%94%EB%93%9C%EC%9D%98-%EC%B2%AB-%EA%B1%B8%EC%9D%8C-%EC%8B%9C%EC%9E%91&quot; aria-label=&quot;멋쟁이사자차럼 동아리 백엔드의 첫 걸음 시작 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;멋쟁이사자차럼 동아리, 백엔드의 첫 걸음 시작&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bcc7c733cd31ddc0b8eb1de57a6fdda8/5e1f2/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.48466257668711%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAADp0lEQVR42i3O+U/TBxjH8W7ZQhjIGMgxjkGhk5bSKiBFjlIQiANRCkEtN6u0OLkqDnRTQC5L5YYiIFdpoRWoxs6Fa7iZOMPIzLLMZT+Y/bIsWbZk/8N73x/2wyef50mevPKIgkM+JFYsQa8vZmWwFustHcM3axhor6OvvZ7BLw2M36ljxqxnebQBx2QTTmsLa1ONbFircAn9qf4SfmESvD4IRxQSFEKMVEHTrXr+9Zzlt7V8Dm1n2Z0txTNdhntSx+bERTwzlewuG3jpquY7WzUvHJX8uZfJK5eO1s6rBIgV/4OBQcSmpGGy9/G38zRvNtW8Xs/glTB/b89nffQC9kEBHi/kJ3cGT6a1/OJW88e2ir+enWLH0coXM32EyFLwCowSwIBApFkaPlua4sf5ep4v1vN4ysgjayPu+ybsE23YR2r4fV/DPwcFbK1U8OtWCRtLHbx5VsOmc4jWiRECVXl4+0cIoJ8fsrIKShweFgc6WL3fhW1ayEwvzkUzLpuFZWsHy7PtHG41srd+md2HejqH5jjYu4HDMUq18xEBehO+7wUjChZAZUMH5XMb9HV2Mzx2D/OIBfPYKObJKYanxzHf66FvbIwf9tvY9Vxn/3E9N4fnOdg24XQMYpyxEa2/iq/3UUQBR0PpqMpkuKuF/iErczYbQ0sP6bLa6FrcoFM4vm19QM/COgffmHjqMvLyayO9Ew/4+VsTWztWeu5ayEpX4e0bhChWHInrdi7918q5axlmb8vO2pqVmSdb9K56GHC4GXLvYHFtc/hcADwtvHhaw+DkHOtzBtZsPUzOzqLOUPHOu36IUpUx3KhMQpt9HGPFeaYtn1Ndko2ppYa6y2VU1ZbSJ3zXPTrLyrSR/u5qOq/lYLyYjTZLSm7eaRbsy2i1RSgTExGpBFCbrcDHx5+TcjGalHiiIyNQxInxDwjjo7BQCvOy2HfP8/qr63ySp0AsSyZZHsH7R3xRa3JYWbWhq9JRXqtDpJRJSD0ex1tv+xAcEsGJeAmSqHBOpaUSHCXFy/sIMnkCRkM9zRVC12pI1hSRmplFcGg40TGxLNgWqKqrRFt2HlFygoSC9HjCwqKQKlM5k6PmTpMOwxUDcWmFBEbKkCSoyC3WkZ6hoa25kZyiS0RKEgiLjCZOKqPZ1MoVYw0XinMRKaTRlGbLyU05Rn6ajOpzKrobcinISUEmkyKXfiwkFkV8HInyY5zLSyNBLkUcHY5C2JMSlajTklCdPIFSLuM/kms4x3lsGEsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/bcc7c733cd31ddc0b8eb1de57a6fdda8/a6d36/image-1.png&quot;
        srcset=&quot;/static/bcc7c733cd31ddc0b8eb1de57a6fdda8/222b7/image-1.png 163w,
/static/bcc7c733cd31ddc0b8eb1de57a6fdda8/ff46a/image-1.png 325w,
/static/bcc7c733cd31ddc0b8eb1de57a6fdda8/a6d36/image-1.png 650w,
/static/bcc7c733cd31ddc0b8eb1de57a6fdda8/e548f/image-1.png 975w,
/static/bcc7c733cd31ddc0b8eb1de57a6fdda8/3c492/image-1.png 1300w,
/static/bcc7c733cd31ddc0b8eb1de57a6fdda8/5e1f2/image-1.png 2134w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;figcaption&gt; 멋쟁이사자처럼 10기 중앙해커톤 은상 수상 &lt;/figcaption&gt;)
&lt;p&gt;SOPT, 멋쟁이사자차럼, UMC, GDSC, ... 등등 대학교 내에도 유명한 IT 개발 동아리가 많이 생겼다. 그 중 나는 멋쟁이사자처럼 이라는 동아리에서 활동하면서, 처음으로 웹 프로그래밍을 접하게 되었다. 사실 멋사말고도 UMC 라는 동아리에서도 활동했었지만, 동아리 부원들과 가장 재밌게 활동하여 추억을 많이 쌓은 동아리는 단연코 멋사가 아닐까 싶다.&lt;/p&gt;
&lt;p&gt;부원들과 팀을 꾸려서 중앙해커톤을 참여했으며, 그를 마무리로 멋사의 공식적인 활동을 끝내게 되었다. 10명이 넘는 팀원들과 FE, BE, 디자이너로 역할을 구분짓고 열심히 서비스를 런칭해보는 경험을 가졌다. 그런데 지금 시점에서 다시 생각해보면, 당시 만든 서비스는 정말 엉터리였다 😅 엉터리 레거시 코드, 취약한 보안, 정말 낮은 가용성(availability), 성능 튜닝도 전혀 안된 상태, 다운타임도 심한 서비스이며, API 연동도 안된 결과물이기 때문이다.&lt;/p&gt;
&lt;p&gt;다만 웹개발을 처음 접하고, 단순 해커톤 참여인데 어떻게 지금 수준처럼 서비스를 런칭할 수 있을까 싶긴하다. 그 당시에는 고도화된 서비스를 완성시키는데 목적이 있는 프로젝트가 아니였기도 하다. 처음으로 웹개발이 무엇인지 감을 익혀서 만족스럽고, 전국 42개 대학이 참여하는 중앙해커톤에서 은상을 수상했다는 점에서 뿌듯한 경험, 추억이 되었다.&lt;/p&gt;
&lt;p&gt;멋쟁이사자처럼에선 백엔드 교육세션을 기본적으로 Python + Django 프레임워크로 진행한다. 이 점이 장점이자 단점이 될 수 있었다. 장점이라 함은 Django 특성상 스프링부트 프레임웤에 비해서 진입장벽이 낮기에 REST API 개발을 빠르게 익힐 수 있다는 점이다. 하지만 단점이라 함은 Django 를 깊이 학습해봤자 취업에 그닥 도움이 되지 않는다는 점이다. 안타깝게도 대한민국을 더붙어 전세계에선 스프링 프레임워크 개발자의 채용 수요가 압도적이다 보니, 파이썬 계열을 열심히 파봤자 그닥 좋은 회사로 취업하지 못할 것이 뻔하게 되었다. (스타트업 일부에서만 Django 를 사용하더라.)&lt;/p&gt;
&lt;p&gt;그래서 고민이 많았다. 멋쟁이사자처럼 중앙해커톤과 교육세션이 모두 종료된 시점이 되었을 때, Django 를 기반으로 커리어를 계속 쌓아나갈 것인지, 아니면 스프링부트 프레임워크로 새롭게 커리어를 쌓아나갈지 고민이 많았다. 지금까지 Django 를 열심히 공부했는데, 그냥 버리자니 너무 아깝기 때문이다. 하지만 고민끝에 결국 스프링으로 커리어를 새롭게 쌓기로 결정했다.&lt;/p&gt;
&lt;h2 id=&quot;umc-에서-스프링부트-첫걸음-때기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#umc-%EC%97%90%EC%84%9C-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%B2%AB%EA%B1%B8%EC%9D%8C-%EB%95%8C%EA%B8%B0&quot; aria-label=&quot;umc 에서 스프링부트 첫걸음 때기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UMC 에서 스프링부트 첫걸음 때기&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/31025d1f8ac23a6e49510715a795ce8d/966c1/image-9.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 28.834355828220858%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAABQUlEQVR42nVQTUtCQRSdfxUELSrF1m2KaFFm2xYtqj8QEQWuoygyCJ5EaIFp80zyAxf1oKzsQ6KkNMvyo7Kn+ZzTvMn30KAzHAbmnnPu3EvAoTXqUOsVMNbAf2g0a9fFBByyDSO0B+NBG8Zkq0n7vhWkVH3DojLNOYV8JSdMt6VLhDN+xDIUJy9xKLkwnj4fzHDlOQI7tWA40MVD+9pCSTwrg6a3UVTzPDArDBEetn7jxFJyFsvJOTgTMzgrHMGYRsdV4RSTkUEM+Dp4kAWO5m+Jqn0h+hjA3p3EQ1+F+L6cgnzsgy/ugTfqhhRyIaRQUWP8GOOXawWsXixglPZiyN/J19ANIrqyOmpaVYiFiTEE5QO41jbg2fJC2nRjx7PbtlNdY+jT7ymsnM9j4rAf5PeZmd1bxeJu4V+0enV8fJfxAwxcn4VNg6u1AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/31025d1f8ac23a6e49510715a795ce8d/a6d36/image-9.png&quot;
        srcset=&quot;/static/31025d1f8ac23a6e49510715a795ce8d/222b7/image-9.png 163w,
/static/31025d1f8ac23a6e49510715a795ce8d/ff46a/image-9.png 325w,
/static/31025d1f8ac23a6e49510715a795ce8d/a6d36/image-9.png 650w,
/static/31025d1f8ac23a6e49510715a795ce8d/e548f/image-9.png 975w,
/static/31025d1f8ac23a6e49510715a795ce8d/3c492/image-9.png 1300w,
/static/31025d1f8ac23a6e49510715a795ce8d/966c1/image-9.png 1694w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이후 이 때문에 UMC 라는 동아리로 들어가서 스프링부트를 처음 접하게 되었다. 이떄가 2022년 하반기로 기억하는데, 스프링부트가 당시 2점대 버전이었던 것으로 기억한다.&lt;/p&gt;
&lt;p&gt;UMC 에선 스프링 생태계의 정말 기초적인 내용을 학습해보며, 스프링 생태계에 대한 전반 오버뷰를 익힐 수 있었다. 교육 과정으로는 데이터베이스 ERD 설계, SQL 쿼리 기초, jdbcTemplate 을 기반으로 진행했다. 아울러, 맨 마지막 교육 세션에는 트랜잭션의 기초 개념에 대해 학습하게 되었다. (트랜잭션 기초 개념이라 함은 ACID, 격리수준과 같은 개념도 아니고, 단순히 스프링 트랜잭션 어노테이션이란 무엇이고, 트랜잭션은 원자성을 보장해 준다 정도의 수준으로 간단히 학습했다.)&lt;/p&gt;
&lt;p&gt;이 당시에는 정말 부끄럽게도 스프링 빈, DI, AOP 이런 개념도 들어보지 조차 못했다. 스프링부트 안에서 제공하는 어노테이션을 왜 클래스에 붙여야 하는지도 잘 모르고 그냥 붙였다. 일단 돌아가는 스프링부트 코드를 만들면서 흥미를 붙이는데 의미가 있었지 않나싶다.&lt;/p&gt;
&lt;p&gt;이후 2달간 앱 런칭 프로젝트를 시작했다. 7명이 팀을 꾸려 프로젝트를 진행했으며, IOS 2명, 디자이너 1명, BE 4명이었던 것으로 기억한다. (시간이 오래되어서 정확한지는 잘 모르겠지만) 이 당시에는 교육과정에서 배운 jdbcTemplate 이 아니라 JPA 를 기반으로 프로젝트를 진행했다. 교육과정 떄 jdbcTemplate 으로 API 를 만들어보다가 JPA 및 Hibernate 라는 ORM 을 기반으로 REST API 를 개발해보니, SQL 중심적인 개발의 문제점은 정말 많다는 점을 직접 체감하게 되었다.&lt;/p&gt;
&lt;p&gt;이 당시만 하더라도 완전 기초 프로그래밍 역량을 갖춘 상태로 개발을 했던 것 같다. 현 블로그를 운영하기 이전 블로그인 Velog 에서 포스팅을 할 때도 포스팅의 퀄리티 및 심도있고 깊은 내용을 다루냐의 수준을 비교해 보면, 지금과 차이가 극명하게 난다. 지금와서 다시금 생각해보면, 짧은 기간인 2년 동안 엄청난 성장을 했기 때문에 LINE 개발자가 될 수 있었던게 아닐까? (열심히 살았다 나 자신 🙂)&lt;/p&gt;
&lt;h2 id=&quot;서비스-운영-시작-하지만-실패했다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%9A%B4%EC%98%81-%EC%8B%9C%EC%9E%91-%ED%95%98%EC%A7%80%EB%A7%8C-%EC%8B%A4%ED%8C%A8%ED%96%88%EB%8B%A4&quot; aria-label=&quot;서비스 운영 시작 하지만 실패했다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서비스 운영 시작, 하지만 실패했다&lt;/h2&gt;
&lt;p&gt;2023년이 되었을 때 무렵 대학교 3학년이 되었다. 이떄쯤부터 실제 서비스를 운영하고 싶다는 마음이 커졌고, 마침 지인으로부터 서비스 운영 제안을 권유받았던 기억이 난다. 서비스 명은 직접 이곳에 언급할 수 없지만, 사이드 프로젝트치고 규모가 매우 큰 서비스였다. 그 서비스의 특징을 정리해보면 다음과 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;MAU 8,000명 가량이 접속하는 서비스&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;/blockquote&gt;
&lt;h3 id=&quot;서비스-운영-팀을-나오게-된-이유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%9A%B4%EC%98%81-%ED%8C%80%EC%9D%84-%EB%82%98%EC%98%A4%EA%B2%8C-%EB%90%9C-%EC%9D%B4%EC%9C%A0&quot; aria-label=&quot;서비스 운영 팀을 나오게 된 이유 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서비스 운영 팀을 나오게 된 이유&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ec73fce22fb30d9886fcabd609783b70/df56e/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.171779141104295%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABn0lEQVR42nWRX2sTQRTF9/P5GXzTD2BfFHwSQUGQ+KY++ZBWfFAoWESkWK0o2AepmDaNJk2zbaR/Qjab7p+Z3Z25Pye7axu0neFymTlzzzn3jldIQSY5qdUkVpVZ2QwrlnK5Mw4vM7aOGnJvMvc2NamrUeWdt3jyluu9+1zr3uO2/5gbew2u/rpDXw2rongTCZaRyRskXEWS72eUrajFo36DW52bLOwslOTeST6hnfT5mfoM9CE9PaSjfedU1TYyMIGLEAqXJUVqwkkxphO32U/3aUfbGDF4/LOsyiliTRbFGDVF5wnaVFhiDH6SEmQZ0zxjN0qIi8rvXxFPqLa1FdD6+IHVpUW+PXnK6UaTH3vv+OJHJbYdhjzcafN80Of17yHPdrtshUE9z4ryP4elmgON1me6uZVzbCbutiAXlc4R1grRUQ898t0nHGFGB5jjAXY6LrHDQPi0JQxHwvhUOA6FVF9KWLUcv28Sry2hNlZQX1dI11+gupsl9uqz4cpdS3PN0FguePCyYL1l65a5pOXZLGduRS4YxXmej3mHfwD5/KojCT3EbgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/ec73fce22fb30d9886fcabd609783b70/a6d36/image-6.png&quot;
        srcset=&quot;/static/ec73fce22fb30d9886fcabd609783b70/222b7/image-6.png 163w,
/static/ec73fce22fb30d9886fcabd609783b70/ff46a/image-6.png 325w,
/static/ec73fce22fb30d9886fcabd609783b70/a6d36/image-6.png 650w,
/static/ec73fce22fb30d9886fcabd609783b70/e548f/image-6.png 975w,
/static/ec73fce22fb30d9886fcabd609783b70/df56e/image-6.png 1188w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;figcaption&gt; 네카라쿠배당토 - 대한민국 IT 빅테크 대표 기업들 &lt;/figcaption&gt;
&lt;p&gt;다시금 생각해봐도 꽤나 매력적인 서비스 운영 경험이라고 생각한다. 하지만, 몇달이 지나고 서비스 운영 팀에서 금방 나오게 되었다. 우선 그 서비스는 학부생이 감당하기엔 미션 크리티컬한 도메인만 가득했다. 때문에 서비스를 운영하면서 자유롭게 기술을 적용하고, 고민해보는 경험을 편하게 할 수가 없는 환경이었다. 게다가 주변에 최상위 기업 (흔히 말하는 네카라쿠배) 에 취업하신 선배님들만 봐도 실제 서비스 운영을 하는가의 유무에 따라 취업을 잘 하는것이 아니였다.&lt;/p&gt;
&lt;p&gt;IT 대기업(네카라) 에 취업하기 위해선 서비스 운영을 했냐가 절대로 중요하지 않다. 실제로 나 또한 신입임에도 LINE 개발자가 된 것을 보면, 그닥 서비스 운영 경험이 필수적인 것은 아니라고 증명할 수 있다. (간혹, 주변 사람들을 보면 서비스 운영을 꼭 해야한다는 것에 집착하는 경향이 있는 사람들도 있다.) &lt;strong&gt;네카라 개발자가 되기 위해선 학습의 방향성과, 사용하는 기술들에 대한 내부 동작원리, 본질을 깨닫는 것이 정말 중요하다.&lt;/strong&gt; 그렇기 때문에 나 또한 네카라 개발자가 될 수 있었다.&lt;/p&gt;
&lt;p&gt;해당 서비스를 운영하면서 그닥 내가 배울 것이 없다고 생각했다. 해당 서비스 팀원들은 문서화 문화를 굉장히 중요시 했지만, 그 정도가 지나치게 과했다. 팀은 생각보다 보수적으로 움직였으며, 내가 원하는 방향성과 잘 맞지 않는다고 생각이 들었다. 간단한 서비스 기능 하나를 런칭하는데만 수개월이 걸렸다.&lt;/p&gt;
&lt;p&gt;나에게는 서비스를 자유롭게 운영하면서 실수도 해보고, 도메인을 개발할 때 스스로 고민하는 습관을 편하게 기를 수 있는 경험도 쌓길 원했으며, 트러블슈팅도 마주했을 때 그 장애 전파가 되었을 때도 고객들에게 미치는 파급력이 크지 않은, 마치 놀이터와 같은 프로젝트 운영 경험이 필요했다.&lt;/p&gt;
&lt;p&gt;또한 팀이 (내가 느꼈을땐) 보수적으로 움직이다 보니, 학습한 기술들에 대해 실제로 서비스에 적용해보면서 학습하고, 고민하고 싶었지만 전혀 그러지 못했다. 이 당시만 해도 무중단 배포 전략(카나리, 블루그린 같은), Redis 분산락과 같이 실무에서 자주 쓰이는 기술 및 전략들에 대해 기술 블로그 포스팅으로 다룬적이 있다. 하지만 실제 서비스 운영에 전혀 적용하지 못하고 있으니, 포스팅 개수만 늘어가고, 이론 기반 지식으로만 학습하면서 시간이 지나며 머릿속에서 모두 휘발되어 버렸다.&lt;/p&gt;
&lt;p&gt;이 외에도 여러가지 이유가 있었지만, 아무튼 이러한 이유로 서비스 운영팀에서 나오게 되었다.&lt;/p&gt;
&lt;h2 id=&quot;카카오테크-부트캠프-1기-수료&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%85%8C%ED%81%AC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-1%EA%B8%B0-%EC%88%98%EB%A3%8C&quot; aria-label=&quot;카카오테크 부트캠프 1기 수료 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;카카오테크 부트캠프 1기 수료&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bd6fc3c1f01d69f3fa2ab6b28f61ced2/1320e/image-7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.828220858895705%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAADAklEQVR42h2Sy1OaZxTGWXffZSeLrnqZLjtddZO0i6S2k7TOOJlhkZnUdjpNWqMjoJ8IaACRIFoUBblIQG6CiNyFj8vHnZJqos3EmU47Te3U/BO/fsnqPe/iec5zfucowiEPqUSIYnaP3MEuYjGBVDmgXc/R7zTodyW6HYlOq0FLqtKoVahVy1TKRcRyAbGUkbVJsqkIh4kdFPPzs4Qee+i3KnSaFVqN8ptXqublusST4YCT0985fnbG8PiE/qBHsylRLGRJ78fkENE3QTIJPweRDRSbG3bWHSsEA17S6QSNRoV2U5RTFOh2m9Q6PaTj5xw1e+ztJ9ly2jEZ1KgmviXgtpIIrpEM2klHfiHiNaPI7O9iNi8ycvMWN0ZHufHVdX6YNKP8XotWcx/jwiyG+Wl0wiS62QmEqXFUP97mrvImYd86fqeJmQd3cFgFfJvLKErpMPVeh8HFK6LZPGqtQEz6laXtMGaTDpt5jmw2xeDJkNiul0XDDLfv3kenvkY79zmBbTP2tRXiuRz1356hqFcyWIxaRq59zNiXn6L8+irjyi/4TjmCfUlLZMdBPh2lXi0S9Dk5jBt5tDTGN9ffppS4Sjy0QsC/SSTqRzp9IRuWD8kkg2w6lnGuPmRj1SQL1wlsmfE4jERlw6h/lZDHhs+1hhhR0XG+zyfvvYXNamAvuo17a42cKNI9O0dRk88kLXP0+lzytt1yNyfxaACbRYeg/plY2EcqvsNjzyo7Lism/QQffvAuV668w4JwD7djEa/M7iiXpFEtoXgtXNTPoNeq0ArTzGqmmFE/QDX9E9ZlI0OZ3cnTE3njbepiiXjQyfjoR4zd+ozJe3cIuCzU8rvUijHEQhyFRj2Fdk7DgkHA/FDLisXAI4ueOdlcENQyuxL//P0X/1685OWf57w4G1I7OiAV85HbD1BMB8nsuUnHXCRC8h0aFwSWTfPyiHoZtp7Xf4NOg3PDRrmQ5PnTLq8uL7i8/I8/zk85HtQYtsv0pAK9Rp5+s8igVaJTz1LOhvkfpbpzmPI1AwIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/bd6fc3c1f01d69f3fa2ab6b28f61ced2/a6d36/image-7.png&quot;
        srcset=&quot;/static/bd6fc3c1f01d69f3fa2ab6b28f61ced2/222b7/image-7.png 163w,
/static/bd6fc3c1f01d69f3fa2ab6b28f61ced2/ff46a/image-7.png 325w,
/static/bd6fc3c1f01d69f3fa2ab6b28f61ced2/a6d36/image-7.png 650w,
/static/bd6fc3c1f01d69f3fa2ab6b28f61ced2/e548f/image-7.png 975w,
/static/bd6fc3c1f01d69f3fa2ab6b28f61ced2/1320e/image-7.png 1292w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;figcaption&gt; 카카오테크 부트캠프 판교 교육장 &lt;/figcaption&gt;
&lt;p&gt;2024년 하반기, 카카오테크 부트캠프에서 진행된 교육과정을 성공적으로 수료했다. 카카오테크 부트캠프에서 활동을 시작하기 전까지는 정말 힘들고, 불안했던 시기들이 많이 있었다. 이와 관련해서는 &lt;a href=&quot;https://haon.blog/%ED%9A%8C%EA%B3%A0/2024-with-kakaotech/&quot;&gt;2024년 카카오테크 코스 최종 합격까지, 상반기 회고&lt;/a&gt; 포스팅에서 이미 회고를 자세히 해서, 굳이 다루지 않으려고 한다.&lt;/p&gt;
&lt;h3 id=&quot;카카오테크-부트캠프를-선택한-이유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%85%8C%ED%81%AC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84%EB%A5%BC-%EC%84%A0%ED%83%9D%ED%95%9C-%EC%9D%B4%EC%9C%A0&quot; aria-label=&quot;카카오테크 부트캠프를 선택한 이유 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;카카오테크 부트캠프를 선택한 이유&lt;/h3&gt;
&lt;p&gt;이미 내가 신뢰하고 큰 도움을 받고있는 &lt;a href=&quot;https://hudi.blog/&quot;&gt;동아리 선배&lt;/a&gt; 를 통해서 꾸준히 도움을 청했고, 평소에 많이 고민하고 도전하는 습관을 통해 학습의 방향성과 기술의 본질, 그리고 빠른 러닝커브를 증명할 수 있는 학습법까지 잘 알고있는 상태였다. 그럼에도 왜 부트캠프라는 교육 과정을 선택했을까? 그 이유는 우선, 함께 성장할 동료가 절실히 필요했다. 그 당시 장기간 혼자서 학습을 이어온 상태이다보니, 무언가 같이 스터디를 진행하거나 협업하여 프로젝트를 운영해야 하는 상황이 생기더라도 동료가 없으니 성장하기에 한계가 있었다.&lt;/p&gt;
&lt;p&gt;또한 취업의 관점에서도 어딘가의 조직에 속해있어야 함을 느꼈다. 프로젝트를 진행하여 서비스를 런칭하려고 해도 동료도 없으니, 이력서에 작성해낼 내용이 전혀 없었다. 앞서 운영중인 서비스에서 탈퇴했다고 했는데, 이 이후로 어딘가에 속해있는 조직이나 그룹이 없다보니 혼자서 많이 방황하고, 이력서에 채울 내용도 없었다.&lt;/p&gt;
&lt;p&gt;만약 부트캠프에 합류하게 된다면, 함께 성장할 동료를 찾을 수 있고, 이력서에 담백히 채울 수 있는 프로젝트 이력이 생길 것이 분명했다. 또한 배우는 것이 최우선 목적이 아니긴 했지만, 카카오 현직자분들을 통해 기술적인 성장이 분명히 있을 것이라 생각했다. 그간의 가꾸어낸 글쓰기+블로깅 기반 메타인지 학습법을 통해 가파른 러닝커브를 만들어낼 수 있을것이란 굳은 믿음이 있었다.&lt;/p&gt;
&lt;h3 id=&quot;카카오테크-부트캠프에서-무엇을-공부했을까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%85%8C%ED%81%AC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84%EC%97%90%EC%84%9C-%EB%AC%B4%EC%97%87%EC%9D%84-%EA%B3%B5%EB%B6%80%ED%96%88%EC%9D%84%EA%B9%8C&quot; aria-label=&quot;카카오테크 부트캠프에서 무엇을 공부했을까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;카카오테크 부트캠프에서 무엇을 공부했을까?&lt;/h3&gt;
&lt;p&gt;교육과정 속에서 내가 카카오 크루, 교육생들과 만나며 열심히 공부한 내용들을 회고해볼까 한다. 나는 카카오 부트캠프에서 무엇을 공부했을까?&lt;/p&gt;
&lt;h3 id=&quot;디자인패턴-웹개발-cs-인프라-오버뷰-학습&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%EC%9B%B9%EA%B0%9C%EB%B0%9C-cs-%EC%9D%B8%ED%94%84%EB%9D%BC-%EC%98%A4%EB%B2%84%EB%B7%B0-%ED%95%99%EC%8A%B5&quot; aria-label=&quot;디자인패턴 웹개발 cs 인프라 오버뷰 학습 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;디자인패턴, 웹개발, CS, 인프라 오버뷰 학습&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1636dc73aa5e08658c1daffc11368889/891d5/image-10.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.828220858895705%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC7UlEQVR42l2T+0+TZxTH+2+ZYFzMVGYvFAql0FIo0iJEKBSo2kIyXGnlUtAihYqIAUUu3qBA+7YvIGOICNUOiBUvyxQ1W7KwKCrRDT++KCbGH745J+cknzznnO8jM6ijZKsiaPaH0R4UPms7z1JG0auipEm5JlkgIzmEep+ARRNksr4Ni3oUZfEyaYcXKNNO01r7kqUKPzJz9hRX+p7gdcfxNSzhb16mxROnrjpGT9cqgVMrDHTdp969QmvTMsdLbzHk7KDD1oshfQJFeQK5PUFz1UPWp9uR5UnFuupFXM5FThxboLsjQZPrHqX5M1QV3uIXCfy0oYs79YM47TEqzL+i2jvBQE0Xf7a70GfeZM+Zf7F2bDLZGUOW/XUsSWkHQmjlAul7BHJbxzF6xlHsilKmGuRSxilSDwjkKccYMfnQKUSEk+2s+jzka0R2t65T4t9Epk+JYkgVydnR9k71cpHc0gimYwJGndRXTZGinuWiMcAlSboUken8Rqozhvij2821oXMkBV6R1Pv+C/B7GVKl+JN0rB9FCsQgNZfnsWXFKVFf5VmRk07DecZMp/lgK+e1v4bEbD39whDpR+5+A1RGMCiFzy/UyifIVEyQo49gtEQ4HA9S3Xeb2vKnFElXfmRxslFs41VeBZttdhiuYm7GS+5USAJKAIM8jMLxkLSSe+j2hXEeuU5ZZpCcQJTi0TFyjAJFc+O0eB/hLl2jxRpn/XQj//nKeOO2s7XkgFEHw7HAzg4Vks8OzZNpnkcjn6Kp8jKBn3s4pA1jHIlQEA5TeG2cXH+Ipiur9Da8o6flCX/Nedjy29hw2fkYd7A1cnxnZLWI/mAIXfIY+70v+MEkWaeyj+diHQMnOzm71oN1fZS7s15OJPpxtT3Gd/RvrKYwzyZdcKOS/+84+Ljk/ALc/in5ktvtBTHM1hXU3RskNfxDc/g3eFzFBVc3Wf0iv8808nqwBpt5mFrrAyy6eQp1QdZueyBylLeNdj4BjFxPyMIdF3sAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/1636dc73aa5e08658c1daffc11368889/a6d36/image-10.png&quot;
        srcset=&quot;/static/1636dc73aa5e08658c1daffc11368889/222b7/image-10.png 163w,
/static/1636dc73aa5e08658c1daffc11368889/ff46a/image-10.png 325w,
/static/1636dc73aa5e08658c1daffc11368889/a6d36/image-10.png 650w,
/static/1636dc73aa5e08658c1daffc11368889/e548f/image-10.png 975w,
/static/1636dc73aa5e08658c1daffc11368889/3c492/image-10.png 1300w,
/static/1636dc73aa5e08658c1daffc11368889/891d5/image-10.png 1520w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;figcaption&gt; 디자인패턴 &lt;/figcaption&gt;
&lt;p&gt;7 ~ 8월 사이에는 이론 강의를 중점으로 교육과정이 진행되었다. 9 ~ 12월 사이에는 대면.오프라인을 중점으로 판교 교육장 오피스에서 현장 중심의 프로젝트 경험을 쌓을 수 있도록 교육 과정이 진행되었다. 이론 수업을 진행할 때 기억에 남는것은 디자인패턴과 웹개발, CS 기초, 인프라의 전반 오버뷰에 대해 학습했다는 점이다.&lt;/p&gt;
&lt;p&gt;다만, 교육과정 중에 조금 아쉬웠던 부분도 존재한다. 사실 이론 수업을 들을 때 들은 내용들이 실무에서 정확히 어떻게 쓰인다는 것인지 연결고리를 잘 맺게 도와주셔야 한다고 생각하지만, 그리 순탄히 진행되지는 않았던 것 같다. &lt;a href=&quot;https://haon.blog/%ED%9A%8C%EA%B3%A0/growth-learning/&quot;&gt;프로그래밍을 공부하며 능동적인 성장을 위한 학습방법&lt;/a&gt; 에서도 다루었듯이, 모든 학습에는 이론 중심의 학습이 되어서는 안된다. 이 기술을 왜 배워야하고, 실무에서 적용해보면서 이론이 어떻게 되는것인지 깨닫는 과정을 통해 기억의 장기화가 될 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;자바-pojo-프로그래밍&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EB%B0%94-pojo-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D&quot; aria-label=&quot;자바 pojo 프로그래밍 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자바, POJO 프로그래밍&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/msung99/kakao-tech-archive.git&quot;&gt;깃허브 아카이브 레포지토리&lt;/a&gt; 에 교육과정동안 학습한 내용들이 모두 담겨있다. 나는 교육과정 동안 자바의 내용을 다시금 복습하고, 스프링의 근간과 Low-Level 을 집요하게 파고들며 학습했다.&lt;/p&gt;
&lt;p&gt;프로젝트를 진행하면서 객체지향 프로그래밍에 대해 제대로 고민해보았다. 이전에 공부했던 객체지향의 사실과 오해라는 책을 다시금 읽으면서, 하나의 컴포넌트 및 기능을 구성하는 여러 객체들간에 어떻게 역할과 책임을 나누고 협력할지에 대해 많은 고민과 노력이 있었다. 객체지향 생활체조 원칙 또한 다시금 복습하면서, 우리 서비스내 백엔드 코드에 어떻게 생활원칙이 잘 적용될 수 있읋지 매순간마다 많은 고민을 기울였다.&lt;/p&gt;
&lt;p&gt;서비스내 백엔드 코드에 디자인패턴 또한 부분적으로 적용했다. 다양한 서비스 정책이 있을텐데, 비슷한 역할을 수행하면서 구체적인 세부 구현 기능만 다른 경우에 대해서 디자인패턴을 적용했다. 예를들어, 할인정책이 있을때 1,000원 할인하는가, 10,000원 할인하는가에 따른 정책에 따라 캡슐화를 시켜놓는 방식으로 코드를 구현했다. 실제 우리 서비스에는 전략패턴, 데코레이터 패턴등이 코드내에 적용되고 있는 상태이다.&lt;/p&gt;
&lt;p&gt;또한 스터디를 진행하며 자바 프로그래밍의 근본이라 할 수 있는 POJO 프로그래밍을 진행하며, 자바 언어에 대한 깊은 학습을 진행했다. 자바 리플렉션, JDK 다이나믹 프록시를 공부하면서 프록시 패턴의 한계점에 대해 직접 체감해보는 경험을 가져봤다. 기초적인 자바 웹 애플레케이션 개발시 자잘한 성능 이슈에 대해 고민해보기도 했다. for 문 대신에 왜 Stream API 를 자주 사용하는지, 왜 성능은 느린것인지 학습해봤으며, String, StringBuilder/Buffer 등의 성능 문제 및 Thread-Safe 특성에 대해 공부하기도 했다.&lt;/p&gt;
&lt;h3 id=&quot;스프링부트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8&quot; aria-label=&quot;스프링부트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링부트&lt;/h3&gt;
&lt;p&gt;스프링부트 또한 기초적으로 대충 알고있던 내용들을 제대로 공부하기 시작했다. IoC, DI 와 같은 핵심 개념부터 제대로 근본을 이해하고자 시작했으며, 점차 JDBC 레벨로 파고들며 DataSource, JDBC Driver, HikariCP, 트랜잭션 동기화 및 추상화, TransactionManager 와 같은 스프링 생태계의 데이터베이스 Low-Level 근간을 공부하는데 주력했다. 프로젝트에선 향후 후술할 MySQL 레플리케이션 다중화 환경에서 DataSource 를 트랜잭션 readOnly 에 따라 적절히 라우팅 해줘야하는 Multi DataSource 구축 상황이 발생했는데, 이 과정에서 JDBC 단의 트랜잭션 내부 동작원리를 깊게 공부해보며 스프링의 내부 근간을 더 공부해볼 수 있었다.&lt;/p&gt;
&lt;p&gt;프로젝트 또한 Spring JPA 로 운영되고 있기 때문에 JPA 과 관련한 여러 트러블슈팅을 마주하며 마찬가지로 심도있는 내용을 공부해볼 기회가 생겼다. JPA 의 영속성 컨텍스트, 1차 캐시와 같은 중요한 개념은 몰론이고 Multi DataSource 환경에서 JPA OSIV(Open Session In View) 설정 관련 트러블슈팅 까지 마주하며 스프링부트 내부의 동작원리를 깊게 공부했다.&lt;/p&gt;
&lt;p&gt;개인 스터디로 Next Step 자바 웹 프로그래밍 서적을 다시금 복습해보았다.톰캣, Spring MVC, JDBC Template 등 현존하는 라이브러리, 프레임워크를 직접 바닥부터 내가 구현해봄는 경험을 해보았다. 바퀴를 재발명하는 경험을 통해 현존하는 기술들에 대한 내부 원리를 직접 체감할 수 있었다.&lt;/p&gt;
&lt;h3 id=&quot;tdd-테스트-커버리지-98-달성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tdd-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80-98-%EB%8B%AC%EC%84%B1&quot; aria-label=&quot;tdd 테스트 커버리지 98 달성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TDD, 테스트 커버리지 98% 달성&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/714a82c326f7a7acf2a4544ba5442ef6/9a1cf/image-12.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.306748466257666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAACIElEQVR42n2S22sTQRjFZ3dmd9OmmmzusYWExJrmQlJJHgKBhGCIiUofqqAPAUGoRBQRLYIigiIK4kMLDRKt0ZrWmiY2xgtYCT6IPvlHHWcnbr3iw2H4Zvb7zZmzH/H5fAgGg9B1Xax+vx+RSASBQEDU09MHQJkCpihQFA1jlnFo2hgkmcLj9SIcDiMajSKZTMLpdIIkEgnMzc0hHo8jl8shFouhVquhWq0in8/jUKkERdMgM8ZBFljHrQJIZBm+fX7Mzh5EOp1GJpOBw+EACYVCKBaLKJfLyGazIIRAkiQhmTcZYgrjAIKZ/WGcO3UCpWwaFg6XqQwiEdFjiDJeu91uYddwmEqlwLiT36ESB1Kxd2b+GIbNZTSuXkDA5wGhElSNQTXiUI1IRO/PG0yZsF2gcTPfn/K6sHbnOm7VT0NhVLhjKgNV+Ct4TU3gr4C/gSOYy+XCyePz2Fp5gOUbl3H0yGHoToc4G0Gp0H8dmrCpyUm0nz3Bty+fsdPv4V13E8MPb9F63MQMnwjjG0Wl3KX0b6AJZXSUXX1hAV8/fcTgVRdv+lzbHfR7HQzf93Hx/NkRUFNFjkTjI2G322Gz2XYdmUD6A3ht8Qp2Bj1sv2xj0NtCb3Md3Y2naK+uYPFSHeqEFVabHXt0HoExyJVKBYVCgf8l5Y9n81Hgo3H39k301lfxovUQrzsb6DxvYe1RA82le2gs3YfucUOb2CuA3wGMgigMwhaJHgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/714a82c326f7a7acf2a4544ba5442ef6/a6d36/image-12.png&quot;
        srcset=&quot;/static/714a82c326f7a7acf2a4544ba5442ef6/222b7/image-12.png 163w,
/static/714a82c326f7a7acf2a4544ba5442ef6/ff46a/image-12.png 325w,
/static/714a82c326f7a7acf2a4544ba5442ef6/a6d36/image-12.png 650w,
/static/714a82c326f7a7acf2a4544ba5442ef6/9a1cf/image-12.png 924w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;figcaption&gt; 토스ㅣSLASH 21 - 테스트 커버리지 100% 이응준님 발표 &lt;/figcaption&gt;
&lt;p&gt;서비스를 운영하면서 TDD 또한 제대로 공부해 볼 기회가 생겼다. 테스트 작성은 내가 가장 중요하게 생각하는 프로그래밍 관습 중 하나이다. 특히나 나는 높은 커버리지를 유지해야 한다는 강한 주장을 내세우는 사람중 한 명이다. &lt;a href=&quot;https://www.youtube.com/watch?v=jdlBu2vFv58&quot;&gt;토스ㅣSLASH 21 - 테스트 커버리지 100%&lt;/a&gt; 라는 토스 Slash 영상을 보면서 실무에서도 100% 라는 커버리지를 유지할 수 있으며, 100% 를 유지했을 때 얻는 장점들이 정말 많다는 점을 배울 수 있었다. 우리 팀 또한 100% 까진 달성못해도 유의미한 테스트를 작성하면서 높은 커버리지를 유지하자는 취지가 강했고, 커버리지 98% 를 달성했다. 추가적으로 90% 미만으로 떨어질 경우 빌드가 실패하여 배포 자체가 불가능하도록 만들었다.&lt;/p&gt;
&lt;p&gt;테스트가 있어야지 리팩토링를 자신있고 과감하게 진행할 수 있다고 굳게 믿는다. 잘못된 코드 작성물이 있다면 그 즉시 테스트가 잘못되었음을 알려주어, 빠르게 피드백 받을 수 있다. 또한 테스트 자체가 일종의 문서화 역할을 하여, 협업하는 팀원들간에 해당 도메인을 빠르게 이해시키는데도 도움이 된다.&lt;/p&gt;
&lt;p&gt;또한 테스트는 프로덕션과 비즈니스 정책에 대한 이해도를 높여주는데도 도움이 된다. 테스트를 작성하다보면 가끔 무엇을 테스트해야하는지 막히는 상황이 생길 때가 있었는데, 이떄가 바로 도메인과 정책에 대한 이해도가 낮다고 느껴질 때이다. 테스트를 통해 내가 잘못 알고있는 부분이 무엇인지 자연스레 깨닫게 되고, 실수없는 견고한 프로덕션을 만들 수 있게된다.&lt;/p&gt;
&lt;h3 id=&quot;mysql-분산-dbms-환경-구축&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mysql-%EB%B6%84%EC%82%B0-dbms-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95&quot; aria-label=&quot;mysql 분산 dbms 환경 구축 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MySQL, 분산 DBMS 환경 구축&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/265959fbb3888e732783f6f2d932acd9/d7e70/image-13.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.05521472392638%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACF0lEQVR42pVSXW/SYBTenzPx0hv+CZcyEuNiTCAh7AeYuLjMxMQrdbiNsUw3AoniMClopVRaaF9KaaGUj0If33MY3bUnad7T8/Gcr2cP97IZ97B2NBhGD5qmod1qYTqdsi+OY37X6zU2mw1c10Wz2URLxui6zjbykewxmEwYOzamQwNmvy8BO9C7Xfi+z6Cz2YyDA6mTzbIsKIrChUkPw5DtVDgBFMJG41sdPcPgZFVVUa/X4TgO5vM52+jtDwYolU4xHA7Zd3F+xsDL5ZK73AJu1lwll8uhXL7k5Gq1iqeZDALPRjDq4q/aQOh24NttHBy8kOMqME0T6XRaFrGwWq0eAEmh6vl8HleVCgN+b9zh2fOXWAUG5nYNkdPAQtQQWl/w9uRYrkSDN9Tx5vhErsJHKPNpl0mHtnDhWT8Ru1fbKy00LMwS9L4Hz/PZ5HgrOO138Ps3UH+cYam+AvxrWNpXCIdi7ndI8ut3B0rjGrH5Ht2eQGxfwmyX8al0AdcRHDNyJfhAQWjfIRx3gEgeTD9F5P/BbB5xY8nIu0vS6KlUCpXKdpe07J2PLklyeFhEZj/L+qPHT/Dh42fWoyh6AJxMJtux5PUKhQJqtVpSYMfHHeDR0WsUi0UEQYBsNoPb2xsQVRNAWiYFUydEWtLpowQqRAwgIT/9E2VGkjLCtlkXQjCFEmL/r1ABIrQ3HktQAVsCEygB/gPJZS5ORrAvWAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/265959fbb3888e732783f6f2d932acd9/a6d36/image-13.png&quot;
        srcset=&quot;/static/265959fbb3888e732783f6f2d932acd9/222b7/image-13.png 163w,
/static/265959fbb3888e732783f6f2d932acd9/ff46a/image-13.png 325w,
/static/265959fbb3888e732783f6f2d932acd9/a6d36/image-13.png 650w,
/static/265959fbb3888e732783f6f2d932acd9/e548f/image-13.png 975w,
/static/265959fbb3888e732783f6f2d932acd9/d7e70/image-13.png 1286w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;MySQL 또한 정말 집요하게 공부했다. 내가 면접을 볼 때마다 다른 지원자들과 다르게 갖고있는 강점이자 경쟁력을 무엇으로 내세울지 전략을 짜고, 많은 고민이 있었는데, 그 중 하나로 MySQL 및 데이터베이스 관련 고가용성 인프라 아키텍처 설계 역량을 내세웠다.&lt;/p&gt;
&lt;p&gt;우리 서비스는 MySQL 레플리케이션 아키텍처 구조를 취하고 있다. 레플리케이션과 관련해서도 정말 많은 포스팅을 발행하며 내부 동작원리와 근본을 깊게 공부하는데 시간을 가장 많이 투자하지 않았나 싶다. 바이너리 로그, 릴레이 로그, 어플라이어 등등 MySQL 복제 내부 아키텍처 및 동작원리를 공부하면서 사용하는 기술에 대해 높은 이해도를 갖출 수 있었다. 이 외에도 FailOver 를 할때 필요한 개념인 바이너리 로그 위치 복제 방식, GTID 복제 방식등을 깊게 공부해보았다. 이렇게 평소에 깊게 공부한 습관들은 향후 면접 대비에서 아주 큰 도움이 되었다 🙂&lt;/p&gt;
&lt;p&gt;또한 단순히 분산 데이터베이스 환경을 구축 하는것에 그치지 않았다. &lt;strong&gt;부하 분산, 지리적 분산.재해 대응, FailOver, 백업 및 복구&lt;/strong&gt; 등 모두 고려한 고가용성 분산 DBMS 환경을 구축하고자 많은 노력을 기울었다. 또한 레플리케이션 아키텍처 그 자체에서 아키텍처를 취할 수 있는 구조가 정말 다양하다. 우리 서비스의 경우는 많은 고민을 기울인 경과 Multi Replica 아키텍처 구조라는 토폴로지 전략을 취했다.&lt;/p&gt;
&lt;p&gt;레플리케이션 외에도 클러스터링, 파티셔닝, 샤딩 전략을 취한다면 우리 서비스에 어떻게 취할것인지, 1,000만건 이상의 대규모 데이터가 적재되었다면 어떻게 대응하고 아키텍처를 취할 것인지 고민이 많았다.&lt;/p&gt;
&lt;h3 id=&quot;10분-카카오-tech-talk-기술-발표-세미나&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#10%EB%B6%84-%EC%B9%B4%EC%B9%B4%EC%98%A4-tech-talk-%EA%B8%B0%EC%88%A0-%EB%B0%9C%ED%91%9C-%EC%84%B8%EB%AF%B8%EB%82%98&quot; aria-label=&quot;10분 카카오 tech talk 기술 발표 세미나 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;10분 카카오 Tech Talk 기술 발표 세미나&lt;/h3&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;350&quot; src=&quot;https://www.youtube.com/embed/tJEbhINVPA8?si=3nVocPhH-UyiHDnx&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;figcaption&gt; Tech Talk 기술 밢표 세미나 참여 영상 &lt;/figcaption&gt;
&lt;p&gt;카카오테크 부트캠프는 소프트 스킬 증진을 위해 10분 기술 발표 세미나를 진행한다. 매주 지정된 요일에 중앙 타운홀에 모여서 각 파트별(FE, BE, Cloud, AI 등) 로 발표를 진행했는데, FE+BE 는 매주 수요일에 발표를 하게 되었다. 나는 데이터베이스 인덱스의 기초 내용을 간단히 발표 내용으로 다루었다.&lt;/p&gt;
&lt;p&gt;발표를 하기 전까지는 그닥 긴장될 이유가 없어서 딱히 별 생각을 안하고 있었는데, 막상 중앙 타운홀에서 발표를 진행하니 떨리고 긴장되는 마음이 커졌다. 그래도 서로가 아는 얼굴들이고, 우리 하모니 팀 팀원들이 격려와 응원을 해주어서 잘 마무리 할 수 있었다.&lt;/p&gt;
&lt;h3 id=&quot;하모니-팀-기술-블로그-운영&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%98%EB%AA%A8%EB%8B%88-%ED%8C%80-%EA%B8%B0%EC%88%A0-%EB%B8%94%EB%A1%9C%EA%B7%B8-%EC%9A%B4%EC%98%81&quot; aria-label=&quot;하모니 팀 기술 블로그 운영 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;하모니 팀 기술 블로그 운영&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7ee49b9f3bedf7c947191231cded6805/1c3f2/image-15.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.828220858895705%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABk0lEQVR42oWT20rDQBCG+6DiS/TSl9Bbb0QQpPROrJReFYSWgnqhFkmThh4ghzbHTZtz8ruztrG1lS4Ms9nsfjPzz25tPp9DURTIsgzbtkFj+n6Jzv05nGUX0mgKw9CQxDGKPMOpUdN0A7brIslyxEkiFnXlBp27M0TsGY4bYbk0sV6t+J/yNHCxWMDlwDRNkWyAtuOB+a6YF0UhrCxPwwTQ9/1DIC89CH4yIlie58g3fhuA5seC1CzLwoqXk2VZBaRvy3bgOA4P5nHvwvM8bj58xs1nIomY63oApIYQlCL+AgMBDcMICc+cMiErinJnTjIcKXk83gfS5jAMsV6v/9VpCz2qoWW7YIztZajrOrrdLgaDgfC9Xg/9fl94VVUr6HGg8QnHtngTggpIhxqNBlqtFprNJtrtNiRJwnA45A2zMFJneH15E5L8hdfi0EcUhULgdKMXNYhKptJpjfTaPXh1fYt6/QLySKmuVgVM+YUmwO61YSzAZDKBaZqi/NlsBk3TRKdzvvfjS8bjwxN/OflByd86eEp+q0ywrQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7ee49b9f3bedf7c947191231cded6805/a6d36/image-15.png&quot;
        srcset=&quot;/static/7ee49b9f3bedf7c947191231cded6805/222b7/image-15.png 163w,
/static/7ee49b9f3bedf7c947191231cded6805/ff46a/image-15.png 325w,
/static/7ee49b9f3bedf7c947191231cded6805/a6d36/image-15.png 650w,
/static/7ee49b9f3bedf7c947191231cded6805/e548f/image-15.png 975w,
/static/7ee49b9f3bedf7c947191231cded6805/3c492/image-15.png 1300w,
/static/7ee49b9f3bedf7c947191231cded6805/1c3f2/image-15.png 2698w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;figcaption&gt; 하모니 팀 기술 블로그 &lt;/figcaption&gt;
&lt;p&gt;나는 기술 공유 문화를 중요시 생각하는 사람이며, 나의 최강점으로 내세울 수 있는 소프트스킬은 글쓰기.문서화 능력이다. 하모니 팀이 서비스를 운영하면서도 서로가 함께 성장할 수 있는 글쓰기 공유 문화를 만들고자 팀 기술 블로그를 만들고 운영했다.&lt;/p&gt;
&lt;p&gt;맨 처음에는 글쓰기와 필력에 자신있고 여러 내공이 있는 나랑 도로시가 주도적으로 팀 기술 블로그 운영을 적극 추진했다. 우리 팀은 서로가 역할을 분배했는데, 나는 팀 기술 블로그 운영 및 문서화 문화 활성화에 포커싱했다. 다른 하모니 팀원들에게도 계속 글쓰기 및 포스팅 발행을 독려하면서, 프로젝트 기간동안 45여개의 포스팅을 팀 기술 블로그에 발행할 수 있었다.&lt;/p&gt;
&lt;h3 id=&quot;교육과정-수료-취준기-시작&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B5%90%EC%9C%A1%EA%B3%BC%EC%A0%95-%EC%88%98%EB%A3%8C-%EC%B7%A8%EC%A4%80%EA%B8%B0-%EC%8B%9C%EC%9E%91&quot; aria-label=&quot;교육과정 수료 취준기 시작 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;교육과정 수료, 취준기 시작&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ecfae383f876ed433ca8f70ee6ee5219/11864/image-16.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.41717791411043%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAADnElEQVR42h2S60+bBRTGm6hkCeJmZDAU2WTAxga4Mdhk4z63wsxiuJVLW1paWlpKC733paUtd4ZsQ0UQZIMFhHCdsHEpZUJQXDDTmLjEmPiP+OnnGz88eb6c/PKccx7JG28eIzYuHqWyBoWiGrlcRpVMgaxGhUarRKtViK5GrWlArdbQoNGibdRhMjVjbWvBbm3BZTNhb9WL0iKRllWSdTmDuso76BuVKOsVeO2ZBIQMbI5GBu9/QVdfPx1dvXR0D9DZN8TQl6PY7DbsDjNupxnBacJp1eOx65Es/fkP0t45cuUOkjOLKS1J47dwGgeh93m2VcvmzgaLP6zx7fQc3zyaY2b5OSNjj0WYFZulCa8IFdwmEWag3WlAonEY+W5WzsS0neyyMuI/PMHyxHkONs7wdK2E5dUZZue/Z3Jmnqm5JeZWn3HvwTBut5Uplx9vqwGvYMLrMuJzNyF50l7Iv/On+TUQwUHwGFczU0lOjUYti0GlKMTfrsLntdDhFxDa3QS6AghWMw7ByMqTMR4GzLT/DzPid+tEoFDAyxkNRw+yeR2UUJQZzaW0FIrycmnWVYuDjfh9FoJBJwG/qC4P3RYl1wsySc46jbO5XExnwGNVIbQpkUxNTvDQJ2PWnoyx9AMiIk+SJw7WVt7CYakTn6Ojs6OFnm4RFnSI7sKir+LjtCQSz8WjUVVgNSuxN1fjMFQimRFv0upxUVyhJjIunaiYRPJulqBSluP3aOkJmunvsdPX56arx4ng0TPcXE9W+hlik49TJ79Ni74Ws64Me1OZuPLSCuPjw9zJTSI39R2KCnNQaZS06CrxuxoY6LExeE9goL9dXNeFXJ8hPjFInrSQT66I/a3IRl5VgEn9GW3au0hWVheZWlik01yErzyOhgYZTRaxqNZGfB4D/d02+nqd9Pa4aRXqOZ8Ti05VTv6n2WSnJXD5Shzldy/RLCZtbShFsh/e4JefXjA6MUH3wH1GRkeYfDTG4+kJUeOMT47w9cggQ8NBKnQ3SLl2kmsX3+PthLeoyr/AVzWfU1j8Eca6m5jrpUgO90L8/ccRf/3+M69fHXB0uMfe3i6h8Dbrm5ssPF1hfmmBvmEvF24kEH/xOPEZUZw6G0XKuWhKSpO4nh+LpjJXhN5C8mJrjVcv9zncD3HwY4jd3S1CO9tsh8M8D++yvr3DeijM2NQI0qqrpOfHcDbjXRKSTpCYFklccgQ5eacwqm5jVEj5D1yqT5IiFRnqAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/ecfae383f876ed433ca8f70ee6ee5219/a6d36/image-16.png&quot;
        srcset=&quot;/static/ecfae383f876ed433ca8f70ee6ee5219/222b7/image-16.png 163w,
/static/ecfae383f876ed433ca8f70ee6ee5219/ff46a/image-16.png 325w,
/static/ecfae383f876ed433ca8f70ee6ee5219/a6d36/image-16.png 650w,
/static/ecfae383f876ed433ca8f70ee6ee5219/e548f/image-16.png 975w,
/static/ecfae383f876ed433ca8f70ee6ee5219/3c492/image-16.png 1300w,
/static/ecfae383f876ed433ca8f70ee6ee5219/11864/image-16.png 2034w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;크리스마스 시즌 무렵, 12월 27일을 마무리로 카카오의 교육 과정은 끝이나게 되었다. 막바지 무렵 쯤에는 이력서를 작성하고, 수정하고, 다듬고, 면접에 참여하느라 정신이 없었던 것 같다. 그렇게 교육과정이 끝나자마자 대학교 마지막 학기에 복학함과 동시에 취준기에 본격적으로 뛰어들었다.&lt;/p&gt;
&lt;h2 id=&quot;line-개발자가-되다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#line-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EB%90%98%EB%8B%A4&quot; aria-label=&quot;line 개발자가 되다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LINE 개발자가 되다&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b5d2facd791cf70f20d516830b225b3b/511f0/image-17.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.25766871165644%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAADfUlEQVR42hWT60/aBxSG+beW7OMun5YtWdJ1TTe3yOZmFVERReUmF4EKKKAgCEoVkJsgCjXi2m460Vhr2rIqToerWK2dndestl2f/fbh5JxP5z3Pm/eIOuqvYVTUoJGJ6WioQtUsxjNgx+O8yYBVTX+PGoepHZtBjk3fQo+uhW61BKswO24qcQnltavxO/QM2jSIMgErMZ8dr02PobMJdWs9colY6DfQttXRJq1G0fgdbY1iNK0/YtE24rB0MGBTYzW2oe+UoGuvw6KS0ieIijYXJ9hYzrG2kGVpLkUhn+JeNsZcZpzZdIRcIkh61E3MbyPk7sZtaaPX0ITLJMNhaMBllOIyNOLskuAzyxDNJ9083Snx7Pk+m1slfi9vslUuUS5v8Od2kdV8mJXsEMsZD4tJBw8F8dW1Ozx4kGepMMUv8wnm74bITzqZChsF5CEL5d0KJ//C8Zt3vHoLl6/f8fINHJy9Ihtyk+jXEHepSPYr2V5fZbNS4vD8mBcXJxSf/sbPy2nWHua5v5JBlB22sr2zw/EllE5PsR09pv3FMnsXl+z/fc7tyCATHoNQOlJuDQuFHPce3WXrrwqVsyM2D7dZL6+wu/dYoFpBlBuxs10u8/If+OP0nPzpAXMXRzx/DYfC0pnoEJM+E5khE7lhM4FhIwv3Z1ksrfJTsUChtExxa4knpSU21hcQ5UNOdp9VOANOBNzjs7dUKvts7x1QfFJkRrhwSrBl2m8hKVwZ9JqJh8ykx7uYve0hlXQyGurH3ttDZMyLKO21MJ9LMiFEJzM6SD4VxqWsImCW4tBJGHebGO7TIPn+S8xyMRG/Gc+glljERJeiHqW0hoBDS7dejVGrEJAF1V+n0/i0ciSfvEe/UYtLL6evW47KaKROUscXP1yluvYKEa+BdNZPPOElkfTRXvsp3372AWN+F6noMErFDUSFmFPIXo7ZzCS3fD7GY3FcZiWG1mpkDWI+/vB99EYVkVsD1A8Y+NokR6tuRqVtwaq8Qv31j/A5dIQ83dTXXEWUdBuYSUbITsRIx0JkUlESIw5igxaiQ3YCwkcMC0j9PQrkrVI+r7qOrLkWnVZGwNpEuK+TieBNIu4uXDrp/7GxcGcyzNSYh/RIH7l4kOlogKlYkITgqV9Y6NJL6ZJ9Q3PdNSQNXxEdtTEetDLSqyLuMZINO4V4uciMWPgPnPf0QyFBzHMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/b5d2facd791cf70f20d516830b225b3b/a6d36/image-17.png&quot;
        srcset=&quot;/static/b5d2facd791cf70f20d516830b225b3b/222b7/image-17.png 163w,
/static/b5d2facd791cf70f20d516830b225b3b/ff46a/image-17.png 325w,
/static/b5d2facd791cf70f20d516830b225b3b/a6d36/image-17.png 650w,
/static/b5d2facd791cf70f20d516830b225b3b/e548f/image-17.png 975w,
/static/b5d2facd791cf70f20d516830b225b3b/511f0/image-17.png 1172w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;figcaption&gt; LINE Plus 분당 오피스 - 공용 오피스 및 라운지 &lt;/figcaption&gt;
&lt;p&gt;그렇게 대학교 막학기 재학과 동시에 취준을 병행하며 LINE Plus 서버 개발자 경력직 수시채용 공고에 이력서를 넣었고, 최종 합격을 하게 되었다. 다음달인 8월 초부터 분당 오피스로 출근 예정에 있다.&lt;/p&gt;
&lt;p&gt;취준기는 정말 지옥 같았고, 고통스러웠다. 앞으로도 살면서 이때보다 더 힘들고, 우울했던 시기가 있을까 싶을 정도로 정말 싫었다. 하지만 힘들고 어려운 시기를 묵묵히 버티고, 이겨냈기 때문에 LINER 가 된 것이 아니겠는가 😁&lt;/p&gt;
&lt;h3 id=&quot;지금까지-지원하고-면접까지-치룬-회사&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%80%EA%B8%88%EA%B9%8C%EC%A7%80-%EC%A7%80%EC%9B%90%ED%95%98%EA%B3%A0-%EB%A9%B4%EC%A0%91%EA%B9%8C%EC%A7%80-%EC%B9%98%EB%A3%AC-%ED%9A%8C%EC%82%AC&quot; aria-label=&quot;지금까지 지원하고 면접까지 치룬 회사 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;지금까지 지원하고, 면접까지 치룬 회사&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ec73fce22fb30d9886fcabd609783b70/df56e/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.171779141104295%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABn0lEQVR42nWRX2sTQRTF9/P5GXzTD2BfFHwSQUGQ+KY++ZBWfFAoWESkWK0o2AepmDaNJk2zbaR/Qjab7p+Z3Z25Pye7axu0neFymTlzzzn3jldIQSY5qdUkVpVZ2QwrlnK5Mw4vM7aOGnJvMvc2NamrUeWdt3jyluu9+1zr3uO2/5gbew2u/rpDXw2rongTCZaRyRskXEWS72eUrajFo36DW52bLOwslOTeST6hnfT5mfoM9CE9PaSjfedU1TYyMIGLEAqXJUVqwkkxphO32U/3aUfbGDF4/LOsyiliTRbFGDVF5wnaVFhiDH6SEmQZ0zxjN0qIi8rvXxFPqLa1FdD6+IHVpUW+PXnK6UaTH3vv+OJHJbYdhjzcafN80Of17yHPdrtshUE9z4ryP4elmgON1me6uZVzbCbutiAXlc4R1grRUQ898t0nHGFGB5jjAXY6LrHDQPi0JQxHwvhUOA6FVF9KWLUcv28Sry2hNlZQX1dI11+gupsl9uqz4cpdS3PN0FguePCyYL1l65a5pOXZLGduRS4YxXmej3mHfwD5/KojCT3EbgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/ec73fce22fb30d9886fcabd609783b70/a6d36/image-6.png&quot;
        srcset=&quot;/static/ec73fce22fb30d9886fcabd609783b70/222b7/image-6.png 163w,
/static/ec73fce22fb30d9886fcabd609783b70/ff46a/image-6.png 325w,
/static/ec73fce22fb30d9886fcabd609783b70/a6d36/image-6.png 650w,
/static/ec73fce22fb30d9886fcabd609783b70/e548f/image-6.png 975w,
/static/ec73fce22fb30d9886fcabd609783b70/df56e/image-6.png 1188w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;figcaption&gt; 네카라쿠배당토 - 대한민국 IT 최고의 기업들 &lt;/figcaption&gt;
&lt;p&gt;나는 IT 서비스 대기업에 취업하기를 몇년간 바라왔으며, 내 직무 역량을 스스로 믿고 굳게 IT 서비스 기업을 중점으로 이력서를 넣었다.&lt;/p&gt;
&lt;p&gt;네카라쿠배당토는 이미 신입 채용이 동결된지 오래이다. 2022년을 가장 마지막으로 신입.경력 모두 취업의 문을 모두 닫았으며, 2023년부터 나를 비롯한 모든 개발자들이 심각한 채용 한파를 직격으로 맞았다는 생각이다. 나 또한 심각한 채용 한파를 피해갈 수 없었으며, 23년도, 24년도를 지나서 25년이 되었을 때는 채용 한파가 더욱 심각해져만 갔다.&lt;/p&gt;
&lt;p&gt;IT 대기업의 3대장 네카라(네이버, 카카오, 라인) 또한 23년도 쯤부터 이미 신입 채용을 아예 동결시켜 버렸다. 나 또한 그래서 LINE 에 경력직 채용 공고로 지원하게 된 것이며, 공개채용 공고에는 단 한번도 이력서를 넣어보지 못했다. 그만큼 가혹하고도, 힘든 시기를 이겨내는데 정말 많은 노력이 있었던 것 같다.&lt;/p&gt;
&lt;p&gt;수시 채용의 특성상 경쟁률은 매우 치열할 수 밖에 없었으며, 채용 또한 언제 마감될지 전혀 모르기 때문에 하루하루가 매우 피말려갔다. 대부분의 채용 공고가 한 자리수 이내로 채용 인원을 선별했으며, 심한 경우는 1명 정도 뽑는 자리에 8,000명 이상의 인원이 지원하여 경쟁률이 1 : 8000 을 넘어가는 수준이었다. 요즘은 작은 스타트업만 해도 1명 뽑는 자리에 1,300명이 넘게 지원한다고 하니깐 정말 심각하다. 또한 합격자가 발생하는 그 즉시 채용 전형 진행중인 모든 지원자를 불합격 처리해버리니, 아무리 능력이 뛰어나도 채용 지원 시기를 잘못타면 불합격 소식을 들을 수 밖에 없었다. (나 또한 LINE 에서 최종합격 소식을 듣자마자 채용 공고가 2시간 뒤에 바로 내려간 것을 봤다.)&lt;/p&gt;
&lt;p&gt;서류 탈락을 제외했을 때, 즉 서류 합격 이후의 채용 절차를 밟았던 회사는 다음과 같다. 이 중에서 면접 및 코딩테스트 전형에서 불합격 소식을 들은 것도 정말 많고, 최종 면접 기회를 얻은 회사도 있지만 LINE 최종합격 소식을 듣자마자 면접 포기 메일을 보냈다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;서류 전형 합격 이후의 전형까지 채용 절차를 밟았던 회사 리스트&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;네이버 본사 (경력 3년 이상)&lt;/li&gt;
&lt;li&gt;네이버웹툰 (경력 3년 이하)&lt;/li&gt;
&lt;li&gt;카카오 본사 (경력 1년 이상)&lt;/li&gt;
&lt;li&gt;카카오페이 서버 개발자 (경력 3년 이상)&lt;/li&gt;
&lt;li&gt;카카오엔터프라이즈 스토리지 플랫폼 (경력 3년 이상)&lt;/li&gt;
&lt;li&gt;카카오모빌리티 서버 개발자&lt;/li&gt;
&lt;li&gt;LINE Plus - 서버 개발자 (경력 2년 이상)&lt;/li&gt;
&lt;li&gt;LINE Music (경력 8년 이하)&lt;/li&gt;
&lt;li&gt;LINE Plus - DBA&lt;/li&gt;
&lt;li&gt;당근마켓 - 추천 시스템 서버 개발자&lt;/li&gt;
&lt;li&gt;당근마켓 - 서버 개발자 인턴 포지션&lt;/li&gt;
&lt;li&gt;토스뱅크 - 수신 플랫폼 서버 개발자&lt;/li&gt;
&lt;li&gt;토스페이먼츠 - 서버 개발자 (경력 3년 이하)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;뭐 이렇게 많은 회사의 채용 절차를 밟얐냐는 생각이 들 수도 있겠지만, 지금 우리가 알아야 할 점은 채용 시장에 심각한 한파가 찾아왔다는 점이다. 또한 나는 애당초 신입 개발자임에도 경력직 채용 공고 다수에 서류 합격을 했다는 점에 감사하고 있다. 그리고 면접 전형까지 가기전에 코딩테스트 전형이 대부분 존재했는데, 나는 코딩테스트가 잘 준비되지 않은 상태라 코테 전형에서 떨어진 것도 많다. 또한 LINE 최종 합격을 하자마자 면접 포기 메일을 바로 전송한 회사도 많다.&lt;/p&gt;
&lt;h2 id=&quot;line-채용-절차&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#line-%EC%B1%84%EC%9A%A9-%EC%A0%88%EC%B0%A8&quot; aria-label=&quot;line 채용 절차 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LINE 채용 절차&lt;/h2&gt;
&lt;h3 id=&quot;서류-전형--코딩테스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%A5%98-%EC%A0%84%ED%98%95--%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;서류 전형  코딩테스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서류 전형 &amp;#x26; 코딩테스트&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d0f4c38940ca5264ce5d36bc5bf98050/cc6fe/image-18.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.41717791411043%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAABJElEQVR42qWSAU/DIBCF/f+/UJPNWl2dXYFCC3QttM+7SzQxdgnTIy/AJXz3OHjATtjFwmePv8TDXvLRP+E0nWS90ZB5237oLuB/4hdwXVd0XQfTG2it4ZwT8doYg7ZtRcXAnDOq6hmvdY3j8YCmabAsC2KMIu+96C6HSvdoLwrj6KVAplzOK1LKMnPuVh93gc25xVvzgUun0RG8UwYDwXvrZLZuwJJSOfCitEiZHtpYEbsNcRKoDwEjXZudF/WwJwfeB7hh/HZkCMRFQohyXS68lX4bbRzOraJHmDHPGdO0kJLsr9ckOda6bmVA03u81GfqoRXxXumBCrHzq0BjTPJARUDrAqr6HYeqIada3DEohJlAX24LgfwbIh3gwyw+vKdbwE/eo/wJ4K8JAQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d0f4c38940ca5264ce5d36bc5bf98050/a6d36/image-18.png&quot;
        srcset=&quot;/static/d0f4c38940ca5264ce5d36bc5bf98050/222b7/image-18.png 163w,
/static/d0f4c38940ca5264ce5d36bc5bf98050/ff46a/image-18.png 325w,
/static/d0f4c38940ca5264ce5d36bc5bf98050/a6d36/image-18.png 650w,
/static/d0f4c38940ca5264ce5d36bc5bf98050/e548f/image-18.png 975w,
/static/d0f4c38940ca5264ce5d36bc5bf98050/3c492/image-18.png 1300w,
/static/d0f4c38940ca5264ce5d36bc5bf98050/cc6fe/image-18.png 1860w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;서류 전형은 그 누구보다도 자신있었다. 내 소프트스킬 상의 강점은 글쓰기, 필력, 문서화 역량이다. 내 이력서와 자기소개서를 회사 측에서 읽었을 때, 꼭 나를 만나서 얘기해보고 싶게끔 매력적인 이력서를 담아내는 것에 자신감이 넘쳤다. 또한 나만을 위한 글을 쓰는 것이 아니라, 다른 사람들에게 공유하는 목적도 동시에 갖는 글쓰기를 평소에 습관처럼 이어왔다. 이 습관은 이력서와 포트폴리오를 만드는데도 아주 큰 도움이 되었다. 내 이력서를 읽을 때 내가 어떤 사람인지를 빠르게 파악할 수 있도록 하고, 내가 읽고 작성하기 편한 이력서가 아니라 다른 사람이 읽기 편해보이는 이력서를 작성했다.&lt;/p&gt;
&lt;p&gt;사실 이번 LINE 서버 개발자 자기소개서를 작성하고, 인적사항 기입 및 이력서를 최종 제출하는데만 30분이 걸리지 않았다. 사실 이 당시 다른 회사 서탈을 너무 많이 했던 시기라, 정신이 반쯤 나간 상태로 전혀 기대 안하고 자기소개서를 30분만에 작성하고 빠르게 제출했다. (근데 작성한 자소서 내용을 지금 다시보니 꽤나 잘 작성했다.) 그래서 서류 합격 소식을 들었을 때 깜짝 놀랐다.&lt;/p&gt;
&lt;p&gt;코딩테스트 전형은 매우 난이도가 쉬웠다. 간단한 기초 알고리즘 유형만이 등장했으며, 구현 위주의 문제가 나왔던 것으로 아마 기억한다. 코딩테스트 제한시간이 200분인데, 나는 30분만에 모든 문제를 푼뒤 빠르게 제출하고 코딩테스트를 종료했다. 모든 문제를 빠르게 문제없이 풀고 제출했기 때문에, 코딩테스트 전형은 문제 없기 통과하곘다는 기대를 하게 되었다.&lt;/p&gt;
&lt;h3 id=&quot;기술-인터뷰&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EC%88%A0-%EC%9D%B8%ED%84%B0%EB%B7%B0&quot; aria-label=&quot;기술 인터뷰 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기술 인터뷰&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3dc9efafb588e5eb9bb26a49beb90571/5faa8/image-19.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.717791411042946%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABUklEQVR42q3QzUoCURTA8Ss9g+9Qm6B2BhIkCCK2SB+gB/AdojLxRVq2KFdtWyRoZbs2zYBo43zgjDNOToPO/HOu2CaRFp3Dj3O5X9xzRfWtymHnkHL3hNJLifxzXtbKa4Xya1lK1jPtDAftg7WynSy7rV1qag2Ra+XYedim0CmQecyw/7hHrn3E8fMxxU6RwlOB9H0acStI3aUQd+KXreYW4kZw2j1FXA+vOVfOuVQuuVAuuFKvqCk1ObdSV+sbNdQGZ+9nNM0mggikfwqhmzq9QQ/btQnCgHiRURyxynk8/7PknPB9H8dxpIk/YTqdEgQB089lTTbFUry0yCSS8doXGobBYDBA13RMw0TTNIbDIdqHRt/sY8x0tK+PBQ091AmjcHPL/sTHdmyMwMAcm4ydMZZlYds23sRjNB5hjSxZk29xPRfP9WRHrutKs9ns58JvQJgXwDb2H70AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/3dc9efafb588e5eb9bb26a49beb90571/a6d36/image-19.png&quot;
        srcset=&quot;/static/3dc9efafb588e5eb9bb26a49beb90571/222b7/image-19.png 163w,
/static/3dc9efafb588e5eb9bb26a49beb90571/ff46a/image-19.png 325w,
/static/3dc9efafb588e5eb9bb26a49beb90571/a6d36/image-19.png 650w,
/static/3dc9efafb588e5eb9bb26a49beb90571/e548f/image-19.png 975w,
/static/3dc9efafb588e5eb9bb26a49beb90571/3c492/image-19.png 1300w,
/static/3dc9efafb588e5eb9bb26a49beb90571/5faa8/image-19.png 1444w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;코딩테스트 종료 2일만에 합격 소식을 듣게 되었고, 직무 인터뷰를 일정을 조율하게 되었다. 직무 인터뷰는 약 1시간 정도로 진행되었으며, 꼬리에 꼬리를 정말 깊게, 집요하게 파고들며 물어보는 질문들이 이어졌다. LINE 경력직 기술 인터뷰 후기를 찾아보면 지구 내핵까지 파고들며 영혼이 나갈 정도의 어려운 질문들을 DFS 형태로 진행한다는 후기가 많았는데, 나 또한 예외없이 정말 어렵고, 난해한 기술 질문들을 받았다.&lt;/p&gt;
&lt;p&gt;면접 진행중에 내 등 뒤에는 식은땀이 정말 많이 흘렀다. 다만, 어쩜 이렇게 전혀 생각치도 못한, 고민해보지 못한 질문들을 받을 수 있을까.. 속마음으로는 정말 훌륭하고 질 좋은 질문들이 가득하다는 느낌을 받았다. 개인적으로는 정말 어렵고 압박감있는 기술 면접 형태일지라도, 이런 유형의 기술 면접을 좋아한다.&lt;/p&gt;
&lt;p&gt;교과서에 잘 나와있는 정형화된 질문들을 달달 외워서 답변할 수 있는가를 검증하는 것이 아니라, 개발자에게 정말 중요한 본질이라 할 수 있는 &lt;code class=&quot;language-text&quot;&gt;문제 해결력&lt;/code&gt; 을 검증하겠다는 느낌이 강했다. 어려운 서비스 장애 및 문제 상황을 하나 주어지고, 주어진 문제를 스스로 해결해나가고, 논리적인 추론을 슬기롭게 해내가는지를 깊게 검증하려는 느낌이었다.&lt;/p&gt;
&lt;p&gt;모든 질문들이 하나같이 예상 질문밖에서만 등장했으며, 정말정말 어렵고 난처한 기술 질문들을 많이 받았다. 하지만 어려운 질문과 상황이 들어와도 끝까지 포기하지 않고, 합격한다면 함께 일하게 될 동료라는 입장으로 최대한 아는 지식들을 모조리 끌어모아서 답변을 드리고자 노력했던 것 같다. 함께 일할 수 있는 동료라는 느낌을 받을 수 있도록, 어려운 질문들에 절대 포기하지 않고 어떻게든 머리를 쥐어짜서 답변드렸다. 버벅 거린것도 많았지만 대부분의 답변에 어떻게든 답변을 드렸고, 다행히 직무 역량을 좋게 봐주시어 기술 인터뷰 합격 소식을 듣게 되었다.&lt;/p&gt;
&lt;p&gt;무엇보다, 내가 사용하는 기술들에 대해 매우 깊은 Low-Level 단의 내부 동작원리, 근간을 잘 설명드렸던 것 같다. 코드 레벨단까지 내려가면서 사례.예시와, 내부 동작원리를 잘 설명드렸다. 하도 깊은 내부 동작원리에 대한 설명을 드리다보니 JDBC 수준까지 내려가면서 DataSourceUtils, TransactionSynchornizationManager 등 과 같이 매우 깊은 내용까지도 설명드렸던 것이 기억난다. Nginx 또한 Graceful Shutdown 과 관련하여 Worker Process, Master Process 의 내부 동작 과정까지 설명 드렸기도 했다. 또한 HTTP/1.1 스팩의 Half-Closed Connection 동작 과정까지도 설명드리기도 했다. 이 점에서 기술 인터뷰 전형의 고득점을 얻지 않았을까 싶다.&lt;/p&gt;
&lt;p&gt;기술 인터뷰 결과가 나오는데 까지는 시간이 다소 오래걸렸다.. 😓 이 시간동안 정말 피말렸고, 인터넷에 라인 면접 결과가 보통 언제 나왔는지 후기를 수시로 찾아봤던 것 같다. 메일도 하루에만 수십번 열어봤던 것 같다.&lt;/p&gt;
&lt;h3 id=&quot;컬처핏--임원-인터뷰&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%AC%EC%B2%98%ED%95%8F--%EC%9E%84%EC%9B%90-%EC%9D%B8%ED%84%B0%EB%B7%B0&quot; aria-label=&quot;컬처핏  임원 인터뷰 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컬처핏 &amp;#x26; 임원 인터뷰&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6de12b3cd7dddfc736dc6ef6b1f0fba5/85e74/image-20.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.94478527607362%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABaElEQVR42q2RzUrDQBSFB3wFH0DBrehGLPhTV1U3Kr6Aaxcian2lrgV17aIiWn9wp3Gh1FaSpqHNTKbVJpN8JgFFF+rGgY9779zhwDkjtu632LF22LV22bf22b7fzvvyQ5myVWbP2sv3v5G92bzbpGJXEJPVCdauVymcTTN+Os7y1RIr1yssXpZYv1lnIt2LQ4E4+pmh4yHEgWDjdgMxW5th4bLIXG2OwkWBYm0+n0tXpbQuMHU+xUh1hNHq6I+MnY4xfDKcOxJBHCCNRBlFYNI+kt/I7l7j1z/px33CJEwF/YAkTkiShP84wvM8pJQMBoP/EbRtm5bTwnVd2u02juPgtT1abgtPevRML7fzYelPwUajwbP9jO3YuVg2N5tNmi8pqsGTeqQe1KnrOv5bl9jERCYiiiLCMMzr17iEr3zcyKXT66ADjVKKbreL1pqe7iM76Yf5Ci01gQryeHzfJ4sqqxnGmE/Bd54ZXV3dBPUUAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/6de12b3cd7dddfc736dc6ef6b1f0fba5/a6d36/image-20.png&quot;
        srcset=&quot;/static/6de12b3cd7dddfc736dc6ef6b1f0fba5/222b7/image-20.png 163w,
/static/6de12b3cd7dddfc736dc6ef6b1f0fba5/ff46a/image-20.png 325w,
/static/6de12b3cd7dddfc736dc6ef6b1f0fba5/a6d36/image-20.png 650w,
/static/6de12b3cd7dddfc736dc6ef6b1f0fba5/e548f/image-20.png 975w,
/static/6de12b3cd7dddfc736dc6ef6b1f0fba5/3c492/image-20.png 1300w,
/static/6de12b3cd7dddfc736dc6ef6b1f0fba5/85e74/image-20.png 1436w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;그렇게 직무 인터뷰 합격 소식을 듣고, 마지막 관문인 컬쳐핏 &amp;#x26; 임원 면접 전형만 남게 되었다. 마지막 관문 하나만 통과하면 나도 진짜 LINE 개발자가 되는건가? 절대 도달할 수 없을 것 같았던 LINE 기업에 재직하게 되는건가? 라는 상상만 머릿속에 가득했다. 하지만 마지막 관문에서 실수해서 떨어진다면 그 만큼 상실감과 아픔이 클 것이라고 생각이 드니, 마음이 뒤숭생숭했다.&lt;/p&gt;
&lt;p&gt;마지막 최종 인터뷰 전형 후기를 찾아보면, 대부분의 LINE 면접 후기가 2차 면접이 훨씬 어려웠으며, 더 깊고 심층적인 내용을 다루는 기술 면접이라는 후기가 대부분이었다. 특히나 직무 인터뷰에서 고득점을 얻지 못했더나 추가 검증이 필요한 지원자라면 높은 확률로 더욱 심화된 기술 면접을 보게 된다는 후기가 많았다.&lt;/p&gt;
&lt;p&gt;그래서 나 또한 직무 관련 질문이 주를 이루며, 더 심화된 기술 질문이 들어오겠다는 추측으로 면접 시나리오를 생각해보았다. 게다가 경력직 채용인데 나는 신입 개발자이다보니, 임원들이 나를 볼때 추가적인 검증을 꼭 하지 않을까라는 생각으로 면접에 임했다. 하지만, 그렇다고 해서 직무 면접을 더 준비하지는 않았고, 컬쳐핏 대비를 철저하게 준비했다. 만약 더 심층적인 질문이 들어온다면 내가 전혀 예측하지 못하고, 미리 준비하지 못하는 질문들이 올 것이 뻔하기에, 뭘 어떻게 미리 준비 할 수 없는 상황일 것이 분명했다.&lt;/p&gt;
&lt;p&gt;컬쳐핏 면접 대비를 위해 LINE 의 인재상은 몰론, 내가 지원한 팀의 코어벨류까지 모두 꼼꼼히 찾아봤다. 또한 지원한 팀의 기술 발표 및 관련 영상들 또한 모조리 찾아보았으며, 어떤 특성을 갖고 어떤 팀원을 뽑고 싶을지 계속 고민해보았다. 아울러, 내가 핏이 잘 맞는 부분들을 개인 사례와 함께 정리해보았다. 또한 인터넷에 나와있는 정형화된 인성 질문(ex. 본인의 장단점, 지원동기, 왜 개발자가 되고 싶은지, LINE 에서 이루고 싶은 목표 등) 들 또한 꼼꼼하게, 체계적으로 모두 준비했다.&lt;/p&gt;
&lt;p&gt;다행히도 왠걸? 🙂 내가 받은 질문들은 대부분 인성 질문이 주를 이루었다. 기술 질문이 조금 들어왔긴 하지만, 인성+기술을 섞은듯한 느낌의 쉬운 질문을 주셨다. 정리하자면 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;인성 질문&lt;/code&gt; : 80%&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;기술+인성 질문&lt;/code&gt; : 20%&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;LINE 의 대부분의 면접 후기는 2차 면접이 훨씬 어려운 기술 면접이라고 했으나, 나는 직무 인터뷰에서 이미 좋은 점수를 얻었다는 확신이 들었다. 내가 실수만 하지않고, 나의 강점과 매력점을 잘 어필할 수만 있다면 충분히 합격하겠다는 생각이 들었다.&lt;/p&gt;
&lt;p&gt;3명의 임원 분둘이 참여했으며, 나의 매력과 강점, 경쟁력을 모두 어필하고 보여주겠다는 각오로 면접에 임했다. 면접 분위기는 정말 편한하게 진행되었고, 내가 왜 LINE 에 채용되어야 하는지, 어떤 강점과 특별한 경험들을 가지고 있는지를 한 없이 보여주었다. 내가 가지고 있는 LINE 에 대한 애정 또한 과감하게, 진심을 다해 말씀드리며, 면접이 끝나고도 아쉬움과 후회가 없도록 최선을 다해 답변드렸다.&lt;/p&gt;
&lt;h3 id=&quot;line-최종합격&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#line-%EC%B5%9C%EC%A2%85%ED%95%A9%EA%B2%A9&quot; aria-label=&quot;line 최종합격 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LINE 최종합격&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/53ebd6ec4c9c11ef03d12cca7a2c0b31/7db30/image-21.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 79.14110429447852%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB3UlEQVR42qVTS4viQBD2Z3v06NGDF0FQRNiDgqv4WGQUhBVmFofdkycfSdTEVcnDmI7m9Y1V0Ms6MzDObsOXrq5Uf1VdjxSuywpsjK0xdF+nI6I4QhzHjM+uFH1E4mEVanBjF/+7mNDQDTx8e0C9VsdoNELzaxP9fh+9Xg9RFCFJEsbdhPP5HOVyGZlMBvV6Hfl8HrVaDZVKBZfLhQ3vJWXC/X6PRqOBarWKbreLVquFwWCA4XAIRVE46ul0eldOmXCxWCCdTqNUKqFQKKDZbCKXyyGbzaLT6aBYLKLdbsPzvA+jTUmD0+nEFxzHwdUch/0B5sG85jDE+XyG7/sIguC+CF3XxXa7ha7r0DQNvvDhBQJ27MATHjsSQjApOZYg/es0MGEYhmwQhAFfco8uLGHj93kH9+TymZySzfF4ZFnubwhlLrSNhvGP71BWyp88/XMfCl9g8muC6pcqnn9O+EecxJxLWYCPcENoWRYenx5h2zYMw7jx+NlImdCxHW4dVVO5KMvlkvfZbMZ60zS5+pS313kkPck3hDReVEVqD9plVWVlCVKWerIhcql/0zY0ERTVZrPBer3GarViWYLOpFdVlbHb7Zjs3SdTw0rPBJpfgoz478ilTK96b2peANbPxOktlsMqAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/53ebd6ec4c9c11ef03d12cca7a2c0b31/a6d36/image-21.png&quot;
        srcset=&quot;/static/53ebd6ec4c9c11ef03d12cca7a2c0b31/222b7/image-21.png 163w,
/static/53ebd6ec4c9c11ef03d12cca7a2c0b31/ff46a/image-21.png 325w,
/static/53ebd6ec4c9c11ef03d12cca7a2c0b31/a6d36/image-21.png 650w,
/static/53ebd6ec4c9c11ef03d12cca7a2c0b31/e548f/image-21.png 975w,
/static/53ebd6ec4c9c11ef03d12cca7a2c0b31/3c492/image-21.png 1300w,
/static/53ebd6ec4c9c11ef03d12cca7a2c0b31/7db30/image-21.png 1494w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;최종 면접이 끝나고 4일 뒤쯤, 모르는 번호로 유선 연락이 왔다. 누군가 했지만 혹여나 해서 받았는데, LINE 채용 담당자님의 연락이었고, 최종 합격했다는 전화를 받게 되었다 😆 채용 담당자님도 같이 기뻐하시고, 진심으로 축하해주셨다. 최종 합격 소식을 듣자마자 몇년동안 한번도 쉬지않고 노력했던 과정들이 헛되지 않았구나, 잘 살고 있었구나 라는 생각이 들면서 안도의 한숨을 내쉬었다. 최종합격 소식들을 듣자마자 우리 가족들이 옆에 있어서 함께 기쁨을 나누었다.&lt;/p&gt;
&lt;p&gt;LINE 개발자라는 것은 나에겐 정말 이상적인 꿈의 직장처럼 보였다. 2년 전만 하더라도 대학교 동아리 친구들한테 나는 네카라쿠배 갈거야!! 라고 장난스럽게 말했는데, 진짜 현실이 될줄은 몰랐다. 절대 안될거라 생각했던 일이, 꿈같던 것이 내가 쉬지않고 몇년동안 노력하니 되는구나... 라는 생각만 가득했다.&lt;/p&gt;
&lt;h2 id=&quot;line-개발자로써의-목표-향후-목표&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#line-%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A1%9C%EC%8D%A8%EC%9D%98-%EB%AA%A9%ED%91%9C-%ED%96%A5%ED%9B%84-%EB%AA%A9%ED%91%9C&quot; aria-label=&quot;line 개발자로써의 목표 향후 목표 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LINE 개발자로써의 목표, 향후 목표&lt;/h2&gt;
&lt;h3 id=&quot;교육자의-길&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B5%90%EC%9C%A1%EC%9E%90%EC%9D%98-%EA%B8%B8&quot; aria-label=&quot;교육자의 길 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;교육자의 길&lt;/h3&gt;
&lt;p&gt;학부 시절부터 &quot;나는 언젠간 꼭 교육자의 길을 걷고 말거야!&quot; 라는 목표가 있었다. 지금도 그 목표는 변함없다. 난 앞으로 교육자의 길을 걸어볼까 한다. LINE 개발자로써 실제 현업에서의 경험을 쌓은뒤, 여유가 된다면 부업으로 교육의 길로 빠져볼까 한다. 최근에 카카오테크 부트캠프에서도 취업 소식을 듣고 인터뷰/특강 초청 연락이 왔는데, 이를 시작으로 조금씩 취미로 하여 교육의 재미를 느껴볼까 한다.&lt;/p&gt;
&lt;p&gt;카카오테크 부트캠프, 우아한테크코스, 네이버 부스트캠프 등 교육 사업으로 뻗을 수 있는 길은 어디든지 열려있다고 생각한다. 어서 빠르게 현업에서 경력을 쌓은뒤에, 소프트웨어 교육자가 되어야 겠다 🙂&lt;/p&gt;
&lt;p&gt;혹 현업의 일정이 벅차서 여유가 안될경우라면, 교육자가 되는 것이 힘들 수 있다는 생각도 든다. 꼭 오프라인 교육장에서의 교육자가 아니더라도, 인프런.유데미와 같은 교육 플랫폼에서 내 지식을 전파하면서 교육자가 되는 방법도 있을 것이다. 내가 알고있는 지식을 영상으로 공유하면서, 간단히 수익을 창출해보는 것이다.&lt;/p&gt;
&lt;h3 id=&quot;의도적으로-일에서-멀어지기-취미-활동-즐기기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%98%EB%8F%84%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%9D%BC%EC%97%90%EC%84%9C-%EB%A9%80%EC%96%B4%EC%A7%80%EA%B8%B0-%EC%B7%A8%EB%AF%B8-%ED%99%9C%EB%8F%99-%EC%A6%90%EA%B8%B0%EA%B8%B0&quot; aria-label=&quot;의도적으로 일에서 멀어지기 취미 활동 즐기기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;의도적으로 일에서 멀어지기, 취미 활동 즐기기&lt;/h3&gt;
&lt;p&gt;지금까지 몇년동안 쉬지 않고 달려왔다. 그래서 나는, 의식적으로 일에서 멀어지는 연습을 할 것이다. 이미 &quot;열정&quot; 은 내 몸안에 가득하기 때문에, 이 열정을 LINE 에서 너무 빠르게 태워버려서 번아웃이 오지 않도록 적절히 휴식하는 방법을 탐구해볼까 한다. 쉴때는 쉬어야하고, 휴식 기간 동안에는 업무 생각을 전혀 하지 않는 환경에 놓여있어야지 꾸준한 페이스로 나아갈 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;제대로 휴식하기 위한 가장 좋은 방법은 바로 나만의 취미 생활을 갖는 것이다. 취미 생활이 있다면 정신적으로 건강하게 스트래스도 풀리며, 자연스럽게 업무 생각도 전혀 하지 않게될 것이다. 제대로 휴식을 해여지 업무 시간에 활력을 넣어줄 것이며, 내 성장을 부스트시킬 수 있을 것이다. 지금까지 생각해 놓은 취미생활은 복싱, 수영, 마라톤 달리기, 밴드부에 들어가 보컬 활동등이 있다. 앞으로 내 인생에 꽃길만 가득하기를.&lt;/p&gt;
&lt;h3 id=&quot;운동-및-다이어트-시작&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%B4%EB%8F%99-%EB%B0%8F-%EB%8B%A4%EC%9D%B4%EC%96%B4%ED%8A%B8-%EC%8B%9C%EC%9E%91&quot; aria-label=&quot;운동 및 다이어트 시작 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;운동 및 다이어트 시작&lt;/h3&gt;
&lt;p&gt;취준기 동안 살이 너무 쪘다. 그래서 최근부터 다이어트를 시작하고 있는데, 효과는 은근히 잘 나오는 것 같다. 땀을 열심히 흘려면서 다이어트도 하고, 예전의 멋진 상태의 나로 되돌아오기를 🙂&lt;/p&gt;
&lt;h3 id=&quot;line-에서-사용하는-기술스택-빠르게-배우기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#line-%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EA%B8%B0%EC%88%A0%EC%8A%A4%ED%83%9D-%EB%B9%A0%EB%A5%B4%EA%B2%8C-%EB%B0%B0%EC%9A%B0%EA%B8%B0&quot; aria-label=&quot;line 에서 사용하는 기술스택 빠르게 배우기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LINE 에서 사용하는 기술스택 빠르게 배우기&lt;/h3&gt;
&lt;p&gt;LINE 에서는 지금까지 사용하고, 경험해보지 못한 기술 스택과 경험들이 정말 가득하다. 지금까지는 Java, Spring, MySQL 에만 주력을 해왔다면, LINE 플랫폼은 이보다 훨씬 광범위한 기술 스택을 다룬다.&lt;/p&gt;
&lt;p&gt;기본적으로 LINE 서버 개발자로 적응하려면 MSA 이벤트 드리븐, Spring Cloud, Redis, Kafka, Hadoop, Apache Kylin, ELK Stack, Kotlin, ... 등등을 다양하게 사용한다고 한다. 이 외에도 정말 많지만, 전부 나열하기에는 끝도 없어보인다. (MySQL 은 안쓴다. 그 대신 하둡을 쓴다.) 아직까지는 무엇을 우선순위로 삼고 공부해야 하는지 감이 잡히지 않으니, 현재로써는 회사에 합류하여 도메인을 파악하고 내가 어떤 것을 우선순위로 삼고 공부해야하는지 목표로 해두자! 정도만 생각해 두고있다.&lt;/p&gt;
&lt;p&gt;그럼에도 서버 개발자 1년차동안에는 개인적으로 공부해보고 싶은 것을 간략히 나열해보면 다음과 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MSA 이벤트 드리븐&lt;/strong&gt; : &lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000202596905&quot;&gt;마이크로서비스 아키텍처 구축&lt;/a&gt; 발췌독&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hadoop 및 데이터 파이프라인&lt;/strong&gt; : &lt;a href=&quot;https://search.daum.net/search?w=bookpage&amp;#x26;bookId=1583723&amp;#x26;tab=introduction&amp;#x26;DA=LB2&amp;#x26;q=%EB%8D%B0%EC%9D%B4%ED%84%B0%20%EC%A4%91%EC%8B%AC%20%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%20%EC%84%A4%EA%B3%84&quot;&gt;데이터 중심 애플리케이션 설계&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redis&lt;/strong&gt; : &lt;a href=&quot;https://www.yes24.com/product/goods/123182350&quot;&gt;개발자를 위한 레디스&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;네트워크&lt;/strong&gt; : &lt;a href=&quot;http://www.kocw.net/home/cview.do?cid=6166c077e545b736&quot;&gt;컴퓨터네트워크 - 한양대학교 이석복 교수님&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;그 누구보다 열심히, 피땀흘리며 살았다고 자부할 수 있을만큼 LINE 이라는 좋은 기업에 취업하게 되었다. 신입임에도 불구하고 LINE 서버 개발자 경력 포지션으로 합류하게 되었다. 채용 한파를 뚫고, 다른 경력직 지원자를 모두 제치고, 치열한 경쟁률을 모두 이겨내며 LINER 가 되었다.&lt;/p&gt;
&lt;p&gt;앞으로는 현직자로써 어떤 개발자가 되고 싶은지, 어떤 커리어를 쌓아나가며 어떤 꿈을 중.장기적으로 이루고 싶은지 고민하면서 살 것이다. 훌륭한 개발자가 될 수 있도록 꾸준한 페이스로, 멋진 사람이 되도록 살아나가야 겠다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[MSA 환경에서 SAGA 패턴과 2PC 패턴를 통해 분산 트랜잭션 구현하기]]></title><description><![CDATA[MSA 환경에서 글로벌 트랜잭션 구현하기 기존 모놀리식 환경에서는 DBMS 가 트랜잭션의 ACID…]]></description><link>https://haon.site/article/toss-slash/distribution-transaction/</link><guid isPermaLink="false">https://haon.site/article/toss-slash/distribution-transaction/</guid><pubDate>Sat, 11 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;msa-환경에서-글로벌-트랜잭션-구현하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#msa-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EA%B8%80%EB%A1%9C%EB%B2%8C-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0&quot; aria-label=&quot;msa 환경에서 글로벌 트랜잭션 구현하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MSA 환경에서 글로벌 트랜잭션 구현하기&lt;/h2&gt;
&lt;p&gt;기존 모놀리식 환경에서는 DBMS 가 트랜잭션의 ACID 를 보장해주었다. 이는 로컬 트랜잭션으로 동작하기 덕분이다. 트랜잭션 작업 중 일부가 실패하면, 전체 작업을 커밋 또는 롤백하는 구현이 간단했다.&lt;/p&gt;
&lt;p&gt;하지만, MSA 환경에서는 하나의 기능이 여러 마이크로 서버를 걸쳐서 수행된다. 즉, &lt;strong&gt;MSA 환경에서는 하나의 트랜잭션이 마이크로 서비스의 물리적인 로컬 트랜잭션 여러개가 묶여서 구성된다.&lt;/strong&gt; 더 이상 DBMS 가 지원하는 단순한 로컬 트랜잭션 만으로는 데이터 일관성을 유지할 수 없게 된다. &lt;strong&gt;일부 마이크로 서버에서 로컬 트랜잭션 커밋에 실패하여 롤백하게 되면, 데이터 일관성을 깨지게 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;예를들어 MSA 환경에서 회원 서비스, 멤버쉽 혜택 관리 서비스, SNS 친구 관리 서비스가 있다고 해보자. 각 마이크로 서비스는 독립적인 데이터베이스를 바라보고 있다. 이때, 유저가 회원가입을 시도하면 어떻게될까? 우선 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 회원 서비스에서 회원가입과 동시에 회원 데이터를 데이터베이스에 저장하는 트랜잭션이 생성될 것이다. 그리고 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 신규 회원을 위한 멤버쉽 혜택을 회원 서버에게 전달하며, 신규 회원을 위한 멤버 혜택 정보 및 등급을 저장하는 트랜잭션이 실행된다. 마지막으로 &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 친구 추천을 제공하기 위한 트랜잭션이 실행된다.&lt;/p&gt;
&lt;p&gt;각 트랜잭션 1, 2, 3은 서로 다른 마이크로 서비스내의 완전히 분리된 데이터베이스에서 동작한다. 그런데 트랜잭션 2를 실행하다 장애가 발생하여 롤백해야 한다면? 트랜잭션 1은 이미 커밋된 상황이라면 어떻게 될까? 아불싸. 달리 방법이 없다. 각 트랜잭션들은 별도의 처리가 없다면 로컬 트랜잭션으로 동작하므로 각자 알아서 커밋되고, 롤백되기 때문이다. 2번 트랜잭션을 수행하다 장애가 터져 트랜잭션 1, 2를 모두 함께 롤백하고 싶다면, 이 로컬 트랜잭션들을 하나의 트랜잭션 덩어리로 묶어서 커밋, 롤백하는 기법이 필요하다. 이를 &lt;strong&gt;글로벌 트랜잭션(Global Transaction)&lt;/strong&gt; 이라고 한다.&lt;/p&gt;
&lt;p&gt;그렇다면, MSA 환경에서 여러 마이크로 서비스에 걸친 로컬 트랜잭션들을 마치 하나의 트랜잭션으로 합쳐서 수행하는 방법으로 무엇이 존재할까? 즉, 글로벌 트랜잭션 구현 방법 종류에 어떠한 것들이 있을까? 이 구현 방법에는 크게 2PC 와 SAGA 패턴이 있다.&lt;/p&gt;
&lt;h3 id=&quot;글로벌-트랜잭션&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%80%EB%A1%9C%EB%B2%8C-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98&quot; aria-label=&quot;글로벌 트랜잭션 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;글로벌 트랜잭션&lt;/h3&gt;
&lt;p&gt;글로벌 트랜잭션과 관련해서는 &lt;a href=&quot;https://haon.blog/database/global-transaction/&quot;&gt;글로벌 트랜잭션 (feat. GTID)&lt;/a&gt; 에서도 간단히 다룬적이 있다. 복습 차원에서 다시 개념을 짧게 학습해보겠다.&lt;/p&gt;
&lt;p&gt;만약 하나의 트랜잭션에서 데이터베이스 여럿에 접근해 데이터를 추가해야 한다면 어떻게 해야할까? 또는 데이터베이스에 데이터를 추가하고, 그 이후에 메시지 큐에 메시지를 추가하는 작업을 하나의 트랜잭션으로 묶고 싶다면? 즉, 2개 이상의 작업 단위를 하나의 트랜잭션으로 처리하고 싶다면?&lt;/p&gt;
&lt;p&gt;이는 앞서 설명했듯이 로컬 트랜잭션으로는 처리가 불가능하다고 했다. 로컬 트랜잭션은 하나의 커넥션에 종속적이기 떄문이다. 따라서 &lt;strong&gt;다수의 자원 및 작업에 대한 요청을 하나의 트랜잭션으로 처리하기 위해선 각 DB 에 종속적으로 만들어지는 커넥션을 통해서가 아니라, 별도의 트랜잭션 매니저를 통해 트랜잭션을 관리하는 글로벌 트랜잭션 방식으로 처리해야한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;스프링 진영에서는 JTA(Java Transaction API) 라는 트랜잭션 매니저 API 를 제공하고 있다. &lt;strong&gt;스프링에서는 JTA 기반 트랜잭션 매니저에 글로벌 트랜잭션(2PC) 가 구현되어 있다고 했었다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;2pctwo-phase-commit-패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2pctwo-phase-commit-%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;2pctwo phase commit 패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2PC(Two-Phase Commit) 패턴&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7ce78de202c254794f0b0eaea6b76bad/e4611/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.12883435582822%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABFklEQVR42lWQ646CMBCFef+n0+wv1yURBbkXUAGRy9l+zda4k0xohznfnGkwDIOmaVLf9yL4lkWpx+OhqqrUte37vCyL5nkWmsY0rr5tm6bn092bplFQ17W6rnv/NMaoKEt9Hw62yehyuejneFSWZZotkPt+t1MYhhrG0cG/9nvFtk4E3uHtdtO2rrrf74rj2AE444whAPI81/l8dsPpiaLI/Sdfr5dWqw9opgGwX8lY1wzILdSDkyRxYEBsxGbX69WdPXC2+c8hsM+ITicnpBk4DhGPdlXeGrjfAncYCsqiUGsfHiBgiiTvSQ0HPDbuUuuIGkOMraFN01TeFIYCJpGIELdN6xx9Bo3U6EHoxegw49b92+4XyAEXi5apg7AAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7ce78de202c254794f0b0eaea6b76bad/a6d36/image.png&quot;
        srcset=&quot;/static/7ce78de202c254794f0b0eaea6b76bad/222b7/image.png 163w,
/static/7ce78de202c254794f0b0eaea6b76bad/ff46a/image.png 325w,
/static/7ce78de202c254794f0b0eaea6b76bad/a6d36/image.png 650w,
/static/7ce78de202c254794f0b0eaea6b76bad/e548f/image.png 975w,
/static/7ce78de202c254794f0b0eaea6b76bad/e4611/image.png 1298w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;2PC 에 대해 다시금 학습해보자. 2PC 는 &lt;a href=&quot;https://haon.blog/database/global-transaction/&quot;&gt;글로벌 트랜잭션 (feat. GTID)&lt;/a&gt; 에서도 간단히 다룬적이 있다. 2PC 는 &lt;strong&gt;코디네이터(coordinator)&lt;/strong&gt; 와 독립된 여러 데이터베이스간의 합의를 통해 트랜잭션이 커밋/롤백이 결정된다. (어쩌면 합의라는 표현보다는, 코디네이터와 DB 간의 질의를 통해 커밋/롤백이 결정된다고 표현하는 것이 적합하겠다.)&lt;/p&gt;
&lt;p&gt;2PC(Two-Phase Commit) 은 이름 그대로 두 단계의 Phase 를 거쳐서 트랜잭션을 수행한다. 각 Phase 에서는 어떠한 작업을 수행하는가? 글로벌 트랜잭션 수행되는 플로우를 3단계로 나누어보면 아래와 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 코디네이터(트랜잭션 매니저) 는 트랜잭션을 시작하고, 각 데이터베이스에게 쿼리를 날려 연산을 수행한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 1-Phase : 1-Phase 에서는 Coordinator 가 각 데이터베이스에게 커밋을 해도 되는지 (또는 롤백해야 하는지) 를 질문한다. 이에 따라 글로벌 트랜잭션에 참여하는 각 데이터베이스는 커밋이 가능한 상태 또는 불가능한 상태임을 코디네이터에게 응답한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 2-Phase : 1-Phase 응답 과정을 분석하여, 코디네이터는 모든 트랜잭션을 커밋 또는 롤백할지 결정한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;1-Phase 는 Prepare 과정으로도 불리고, 2-Phase 는 Commit 과정으로 불린다. 두 단계의 Phase 로 나뉘므로 Two-Phase Commit 으로 불린다. 이를 통해 서로 다른 DB 간의 정합성을 보장할 수 있다.&lt;/p&gt;
&lt;p&gt;그리고 그림을 보면 알 수 있듯이, 데이터에는 쓰기 시작할 때 부터 커밋할 때 까지 락을 건다. 따라서 두 데이터베이스를 함께 조회하더라도 커밋되지 않은 쪽은 락이 잡혀있어서, 두 데이터베이스가 불일치된 상태로 조회되지 않는다. &lt;strong&gt;즉, 락을 이용해 정합성을 보장한다.&lt;/strong&gt; Phase-1 에서 모든 리소스(데이터베이스) 가 OK 응답을 보재면, Phase-2 에서는 모든 리소스 매니저에게 Commit 을 지시하고, 락도 해제한다.&lt;/p&gt;
&lt;h3 id=&quot;단점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90&quot; aria-label=&quot;단점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;p&gt;다만, 2PC 패턴은 아래의 단점들로 인해 MSA 환경에서 잘 사용되지 않는다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;락을 사용하므로 성능이 저하된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;NoSQL 등 일부 DBMS 에서 지원하지 않는 경우가 존재하여, 2PC 를 구현 불가능한 경우가 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이종의 DBMS 에서는 사용하기 어렵다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MSA 에서 일반적으로 마이크로 서비스는 각자의 데이터베이스를 소유하고, 다른 마이크로 서비스에게는 Public API 를 통해 데이터를 제공한다. 여러 데이터베이스에 걸친 글로벌 트랜잭션을 하나의 코디네이터가 관리하는 구조에서 적합하지 않다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;코디네이터에 의존적이다. 코디네이터 장애 상황에서 각 데이터베이스는 커밋/롤백 여부를 스스로 결정할 수 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;saga-패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#saga-%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;saga 패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SAGA 패턴&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e202cdf7beff07500064dec981397567/fbf76/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.576687116564415%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAABvklEQVR42o1SXW+jMBDk//+iKs3dy53UNg+ne7pKCbQ6QR1IAocBG2M+5rwbjGjah1paeeXxjsc7G2C1ZCnxJt6QZimyU4bT6cSRpinO5zOmacI0ThjHkfNhGDj3i/JgTaiUQlmWTFgUBS6XC7IsQ1VXKGUJ0xlXNKDrOvR9v5B8SkgvetAYA1lJ1HXNOS1rLZqmYeV1UzPplwmpWBwFjumRCx3EiqqqQhiFvNOdLxMqrVgZfbFRzfJI27awvdtNuyj/QEhE6wMKajYpsi4m17NDblCZAZM7t7ZnzN+9rQ+8ujWwqJ73u6hG2vTzT1b4XOeDCcnFz8AlSPH4vsiPyy0ZKQ/INa01dKu5T/6yD+pbo2oeKTuPihfAmHOe6omMagNy8fXllQeYclIcJzH8Q5Ws8PznGVl6gpQSSZJACIE8z6GVRngIUeQFkzGhaAW24jse8if8a93wasNqO9tBVQqResEm2eKX/A3ZSH5AurGpyxphE2Ejtngqduw+E8ZljG30DT/+/oRs5XtTXO8ORYjN/h67ZAdt9GzW1Zn9ZY/N4R4P8SOMNVfCcRivdrrttn+DGxmPE/ktvsa8Kf8Brm/x0NRJhtsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e202cdf7beff07500064dec981397567/a6d36/image-4.png&quot;
        srcset=&quot;/static/e202cdf7beff07500064dec981397567/222b7/image-4.png 163w,
/static/e202cdf7beff07500064dec981397567/ff46a/image-4.png 325w,
/static/e202cdf7beff07500064dec981397567/a6d36/image-4.png 650w,
/static/e202cdf7beff07500064dec981397567/e548f/image-4.png 975w,
/static/e202cdf7beff07500064dec981397567/fbf76/image-4.png 1252w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;SAGA 패턴은 MSA 같은 분산 환경에서 데이터 일관성을 보장하기 위해 등장한 설계 패펀이다. 이 패턴에서는 &lt;strong&gt;연속된 개별 마이크로 서비스에서 로컬 트랜잭션이 실행되고 이어지면서, 전체 비즈니스 트랜잭션을 구성한다.&lt;/strong&gt; 첫번째 트랜잭션이 완료되면 두번째 트랜잭션이 트리커 되고, 두번째 트랜잭션이 완료되면 3번째 트랜잭션이 트리커된다.&lt;/p&gt;
&lt;p&gt;만약 트랜잭션 중 하나가 실패하면, SAGA 패턴은 이미 성공적으로 완료된 로컬 트랜잭션들을 **보상 트랜잭션(compensating transaction)**을 발생시켜 모두 취소하여 전체 데이터 일관성을 유지한다. 보상 트랜잭션이란 이전에 커밋된 트랜잭션을 취소하는 연산이다. 즉, SAGA 패턴에서는 &lt;strong&gt;데이터 일관성을 보장하는 주체가 DBMS 가 아니라 애플리케이션&lt;/strong&gt;이다. 이것이 핵심이다. 보장 트랜잭션이 적용되기 전까지 일시적으로 데이터베이스 일관성이 잠시 깨져있을 수 있으나, 보장 트랜잭션이 완료되면 &quot;결과적으로&quot; 정합성을 보장한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 보상 트랜잭션은 이미 발생한 트랜잭션이 만들어낸 변경을 되돌리기 위해 새로운 트랜잭션을 추가하는 방식이다. 고객이 이미 결제 금액을 지불했다면 지불된 돈을 돌려주는 것이고, 고객에게 포인트를 부여했다면 그 포인트를 다시 회수하는 것이 보상 트랜잭션의 예시가 될 수 있다. 이렇기 때문에 보상 트랜잭션을 의미론적인 롤백(semantic rollback) 이라고도 말한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;SAGA 패턴는 2가지 종류가 있다. 코래오그래피 기반 사가와 오케스트레이션 기반 사가 이 2가지가 있다.&lt;/p&gt;
&lt;h3 id=&quot;코레오그래피-기반-사가-choreography-based-saga&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BD%94%EB%A0%88%EC%98%A4%EA%B7%B8%EB%9E%98%ED%94%BC-%EA%B8%B0%EB%B0%98-%EC%82%AC%EA%B0%80-choreography-based-saga&quot; aria-label=&quot;코레오그래피 기반 사가 choreography based saga permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;코레오그래피 기반 사가 (Choreography-based Saga)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/20aef10697c368628f6dca4c6c7528ab/46115/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.89570552147239%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACJ0lEQVR42oVTTW/TQBD1H4Yb4sIJJNRDJY6VoHBGouKjCAIhCBAgoAUUkQbHaeLUITF2nI8mjr921/uYcWy1BwpjjWyvd55n3ntrSJUjEQpJJhHFCTIhwWscWmvKvLjn+Wbtf2EopTDwljCHAdrtFnq2jTgTxUdFwK7nwfk1wpAyjGIIqcofUf4NUBIgA8SpoO4EkjRFJuUGMNf0rM66pSvOMnANT7HOItjBBJPVKWIZ0ZqEUYyEvBxtM14VisacrhK0nSm+H7uwxnOkRAnvEVT84Nsudt7dwN0PW7j9/iaceR/GRVzkVJRQ8ckkxGFnhE8tG0cDnzqUmNhDrMM1Gt0nBHoH9w928bB5D+5yBMMPf6M/68BZ9GDPLPA7x8IZIyWRJI2tzmVE1CzDiDko9qUkqLeIS1qIw7r5CNsvrmDnzXXcql9Frb0HFjQjLpmCVSzw7LOFvbdNPD/sYRiE9JO8xNP0rApq8pIuYx5N4Z46OOibxMEAi2h2NjZtFKWFmAJFyeIlJCI7gGUWkrsuhWOVq+JKC01j5YXvdFG0TgTaJz5atovueFb4lV2Qphpd38Jrq4a6+QpfnAZ6QYdV1qWyGxNz6xVfbI0wyfDVGmH/4080+17BWa4VgQJPf+xj++VlousatmqX0Og8vljlyjYMwLGiTkXhWYmEDJ6uIkzCAKZ3hGPqtDc16d3/NyB3LsnYfDoYPCPwVNJRJcuEfgBdEXfupPwBE66R02GiOPkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/20aef10697c368628f6dca4c6c7528ab/a6d36/image-1.png&quot;
        srcset=&quot;/static/20aef10697c368628f6dca4c6c7528ab/222b7/image-1.png 163w,
/static/20aef10697c368628f6dca4c6c7528ab/ff46a/image-1.png 325w,
/static/20aef10697c368628f6dca4c6c7528ab/a6d36/image-1.png 650w,
/static/20aef10697c368628f6dca4c6c7528ab/e548f/image-1.png 975w,
/static/20aef10697c368628f6dca4c6c7528ab/46115/image-1.png 1290w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;각 마이크로 서비스는 트랜잭션이 완료되면, 완료 이벤트를 메시지 큐에 발행한다. 만약 그 다음에 수행되어야 할 로컬 트랜잭션이 있다면, 해당 이벤트를 구독한 마이크로 서비스가 이어서 로컬 트랜잭션을 실행한다. 이때 만약 중간에 로컬 트랜잭션이 실패하면, 이를 상쇄하기 위한 보장 트랜잭션 이벤트를 발생하여 롤백을 시도한다.&lt;/p&gt;
&lt;h4&gt;장점&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;구성이 편리하다&lt;/li&gt;
&lt;li&gt;하나의 서비스에 장애가 터져도, 시스템 나머지 부분이 영향을 덜 받는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;SAGA 참여자가 많을 경우 트랜잭션 흐름이 복잡하고, 길어진다.&lt;/li&gt;
&lt;li&gt;마이크로 서비스간 순환 종속성이 발생할 수 있다.&lt;/li&gt;
&lt;li&gt;각 마이크로 서비스가 서로를 알고있는 구조로, 결합도가 높다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;오케스트레이션-기반-사가-orchestration-based-saga&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%A4%EC%BC%80%EC%8A%A4%ED%8A%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EA%B8%B0%EB%B0%98-%EC%82%AC%EA%B0%80-orchestration-based-saga&quot; aria-label=&quot;오케스트레이션 기반 사가 orchestration based saga permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;오케스트레이션 기반 사가 (Orchestration-based Saga)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d7b077587f761170bfcf8698bf88155c/9cea8/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.122699386503065%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACfklEQVR42l2T21PTUBDG81/rOKLOeB0fdAQZRXEcHfRBHZ1BHgS5KqJIUblUkZbSlt7SpiFJ05xc+3PTiiCbfJOTc9n99ts9Wlf5VA2Hck2nWCxS6KOEadn0er1jJAlxHAuiPiIZp/PIe4R0XQuikKrpUtFbVCsHlMolLMuSAwlBEGAYRh+O45Ba6uPIbN/CVIagzaEyiXsJGqcsjZo6S+RgEIZY4sjquLh+QChzXdURmBidgMefRhiZO8fd+fOML1/DVjZarpnl484Mn3fnWMpOs13/Jux7JElIN4hp5fapf11Hz2zg1pq4gYfr2fhRwmpxiYXdScFblgvTeKFCm9l5yZ3ZswzPX+f+yiVeb42jAoXlNoWFT61YoZnL08wX6JpWn/lJS5JBRkemLeQneZq5ybjQf5K5xevNZyjlYXdaKGFhVmrUszu0fufx2uLwlERBFEmQ4yjaYmGSB8tXGZka4t7iZSbWnotGCk9Z6LainitibmVpb//C0w0JEgirqH/YtBuy1x1o//fR3m29ZPj9RR59GWZ08Qqv1sf7LdGWzVHcoyGsClVpqXqLpuOjmw0OO4Y4BV/0TJL/OWuz2TeMLd3g4cqYpH2bqe0J6SeoG2U83ydXrpH5ucf33SK/Km0MxxONI1mLaDg6ZWuPA3tfvgVUWhQv9PCiDvlGnUPPIv1PW8cPXHSrKymXaG1uS8o7qGZ7oKH026Eb8CLzTIp5htH5C4zMD1GxSsd9GEu14rj3V4+BeWFMLb/PwY9NDjayGBWRoX8jIgyryq6e5fPeHKv7S6yVPuCI7v81dlr+VLdO1xENa4RSZV/mlAQLxFGYDK7hoF2Sf+OTGv4B34B6/MIbKbcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d7b077587f761170bfcf8698bf88155c/a6d36/image-3.png&quot;
        srcset=&quot;/static/d7b077587f761170bfcf8698bf88155c/222b7/image-3.png 163w,
/static/d7b077587f761170bfcf8698bf88155c/ff46a/image-3.png 325w,
/static/d7b077587f761170bfcf8698bf88155c/a6d36/image-3.png 650w,
/static/d7b077587f761170bfcf8698bf88155c/e548f/image-3.png 975w,
/static/d7b077587f761170bfcf8698bf88155c/9cea8/image-3.png 1278w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;중앙 집중된 1대의 SAGA Orchestrator(코디네이터) 가 SAGA 참여자들에게 어떤 로컬 트랜잭션을 실행해야하는지 알려주는 방식이다. 즉, 중앙화된 1대의 코디네이터가 전체 프로세스의 로컬 트랜잭션 실행 순서를 정의하고, 필요하다면 보상 트랜잭션을 발생시켜 롤백한다.&lt;/p&gt;
&lt;h4&gt;징점&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;모든 로컬 트랜잭션을 오케스트레이터가 관리하여, 트랜잭션 흐름이 명확하다. (모니터링 하기도 쉬워질 것이다.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;각 마이크로 서비스는 서로를 알 필요가 없다. 따라서 결합도가 낮다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;오케스트레이터가 중앙 집중되어 SPOF(단일 장애 지점) 이 될 수 있다.&lt;/li&gt;
&lt;li&gt;같은 이유로 확장성과 유연성이 낮다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;MSA 환경에서 각 마이크로 서비스별로 데이터베이스를 분리하는 것은 일반적인 패턴이다. 데이터베이스를 분리할 때는 서비스간의 독립성과 확장성을 확보할 수 있다는 장점이 있지만, 서비스 전체의 데이터 일관성을 유지하는 것이 기존의 단일 데이터베이스보다 어려워진다는 이슈도 존재한다.&lt;/p&gt;
&lt;p&gt;이를 해결하기 위해 2PC 와 같은 2단계 커밋 프로토콜이 등장하기도 했지만, 이는 모든 DB 가 응답할 떄까지 코디네이터가 기다려야하는 방식이므로 네트워크 지연등으로 인해 전체 트랜잭션 처리 시간이 길어질 수 있다는 성능 문제가 있다.&lt;/p&gt;
&lt;p&gt;이후 등장한 SAGA 패런은 연속적인 로컬 트랜잭션의 조합으로 데이터 일관성을 유지하고 하는데, 2PC 과 달리 특정 데이터에 락을 걸지 않아도 된다는 장점이 있다.&lt;/p&gt;
&lt;h3 id=&quot;코레오그래피-기반-사가-vs-오케스트레이션-기반-사가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BD%94%EB%A0%88%EC%98%A4%EA%B7%B8%EB%9E%98%ED%94%BC-%EA%B8%B0%EB%B0%98-%EC%82%AC%EA%B0%80-vs-%EC%98%A4%EC%BC%80%EC%8A%A4%ED%8A%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EA%B8%B0%EB%B0%98-%EC%82%AC%EA%B0%80&quot; aria-label=&quot;코레오그래피 기반 사가 vs 오케스트레이션 기반 사가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;코레오그래피 기반 사가 vs 오케스트레이션 기반 사가&lt;/h3&gt;
&lt;p&gt;SAGA 패턴의 구현 방식 2가지 중 어떤 방식을 선택하는게 적합할까? 우선 코레오그래피 방식은 마이크로 서비스간의 결합도가 낮고 중앙화된 코디네이터를 필요로 하지 않기 떄문에, 오케스트레이션 방식에 비해 장점이 크다고 보지만 그만큼 프로세스의 실행 순서와 진행 상황을 파악하고, 모니터링하기 어렵다. 따라서 &lt;strong&gt;코레오그래피 방식은 팀 내에서 적절한 모니터링과 로깅, 이벤트 전략을 갖추었을 때에만 선택하는 것이 적합하다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;saga-패턴은-트랜잭션이-격리isolation되지-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#saga-%ED%8C%A8%ED%84%B4%EC%9D%80-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%B4-%EA%B2%A9%EB%A6%ACisolation%EB%90%98%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;saga 패턴은 트랜잭션이 격리isolation되지 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SAGA 패턴은 트랜잭션이 격리(Isolation)되지 않는다&lt;/h3&gt;
&lt;p&gt;SAGA 패턴은 트랜잭션 ACID 원칙 중에 격리성(Isolation) 을 보장하지 않는다. 따라서 이를 고려한 설계가 필요하다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/global-transaction/&quot;&gt;https://hudi.blog/global-transaction/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=xpwRTu47fqY&quot;&gt;https://www.youtube.com/watch?v=xpwRTu47fqY&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@greg.shiny82/%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%82%AC%EA%B0%80-%ED%8C%A8%ED%84%B4-544fc1adf5f3&quot;&gt;https://medium.com/@greg.shiny82/마이크로서비스-사가-패턴-544fc1adf5f3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://microservices.io/patterns/data/saga.html&quot;&gt;https://microservices.io/patterns/data/saga.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MSA 환경에서 BulkHead 패턴을 사용한 장애 전파 최소화하기]]></title><description><![CDATA[BulkHead…]]></description><link>https://haon.site/msa/bulkhead/</link><guid isPermaLink="false">https://haon.site/msa/bulkhead/</guid><pubDate>Fri, 10 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;bulkhead-패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bulkhead-%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;bulkhead 패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BulkHead 패턴&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/02696536f93c68107efdc8d0bd3eb433/21335/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 69.32515337423312%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB+UlEQVR42nWUh44iMRBE5///CETOiCXHReQw5MyRoXden3w7IJ2lxmN7XF1V3YMlzng+nxruwfr1eukzszs+h3vf6na7Eo/HJZ1OSyKRkEKhIK1WSzqdjqzXawU+HA5yv9/1wuPx0DBJ3aAKWC6XJZfLSTKZlFAoJKlUSrxer3g8HvH7/ZqIc5KMx2MZjUYyn89lu93KdDqV0+kk5/NZEwJqsQDE5/NJOBzWiMViUiwWlS0JS6WSzgTAqOK8VqupEvYGg4GCWtCcTCaSyWQkn89LNptVZpFI5B9wpVLRy2YGuNFoKDCAzMvl8q9k/LhcLrrR6/Wk2WzqJVjhKSD1el3XnLGHHdFoVO0IBoOawHhsmQrtdjvZbDZi27YMh0O9THY8I9rttrLHkkAgoBahghlvr9frL6CpFtWEbb/fV0m8CBAMsQRpMIUddlBIFIBxu93eGTIo0H6/V0+RT8AOIIBJAkuAkErRUPXWNgYQDwCjJWgFepBnWBpwKokVAMMUJcY7o9IyyHiAZIBgulqtNAG9BghFgyUtUq1WpeQUwnaUPD++oDfJZJvNZgpEsF4sFgpgfGQuO1LzXxmxneK9aGji+QFogqLA0Hxix+NRfQIYluydHaBjsyH772/5027JyfH54dxTQPnPcP8xUEGAjE/8IlXl8o7r3g8+cg/6Ra4xKgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/02696536f93c68107efdc8d0bd3eb433/a6d36/image.png&quot;
        srcset=&quot;/static/02696536f93c68107efdc8d0bd3eb433/222b7/image.png 163w,
/static/02696536f93c68107efdc8d0bd3eb433/ff46a/image.png 325w,
/static/02696536f93c68107efdc8d0bd3eb433/a6d36/image.png 650w,
/static/02696536f93c68107efdc8d0bd3eb433/e548f/image.png 975w,
/static/02696536f93c68107efdc8d0bd3eb433/21335/image.png 1082w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;벌크해드란 한국어로 번역하면 격벽을 뜻한다. 격벽이란 선박의 공간을 여러 구획으로 분할하고자 사용되는 벽을 의미한다.&lt;/p&gt;
&lt;p&gt;벌크헤드 패턴은 선박의 격벽으로부터 영감을 받아 만들어졌다. 리소스를 격리하여, 일부 컴포넌트에 장애가 발생하더라도, 그 장애가 시스템 전체로 전파되는 것을 막게끔 하는 패턴이다.&lt;/p&gt;
&lt;h2 id=&quot;bulkhead-패턴-미사용시-장애-발생&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bulkhead-%ED%8C%A8%ED%84%B4-%EB%AF%B8%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%9E%A5%EC%95%A0-%EB%B0%9C%EC%83%9D&quot; aria-label=&quot;bulkhead 패턴 미사용시 장애 발생 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BulkHead 패턴 미사용시 장애 발생&lt;/h2&gt;
&lt;p&gt;빠른 학습을 위해 &lt;a href=&quot;https://internet-craft.tistory.com/77&quot;&gt;참고1&lt;/a&gt; 과 &lt;a href=&quot;https://hudi.blog/bulkhead-pattern/&quot;&gt;참고2&lt;/a&gt; 를 다수 참고하여 학습을 이어가보도록 하겠다. MSA 환경에서 서비스 A, B, C 가 존재한다고 해보자. 서비스 A 의 B-API 는 서비스 B 를 호출하여 응답을 만들고, C-API 는 서비스 C 를 호출하여 응답을 만들어낸다고 해보자.&lt;/p&gt;
&lt;p&gt;그런데, 서비스 B에서 장애가 발생하여, 응답을 매우 느리게 주는 상황이라면 어떻게 할까? 이런 상황에서 서비스 A의 B-API 호출이 발생한다면, 서비스 A 는 서비스 B의 응답을 기다리느라 쓰레드를 계속 점유할 것이다. 장애 시간이 길어짐에 따라, 서비스 A의 톰캣 쓰레드 풀은 고갈될 것이다. 그렇다면 C-API 는 서비스 B에 의존하지 않지만, 톰캣 쓰레드를 할당받지 못하여 덩달아 장애 전파 영향력을 받게 된다.&lt;/p&gt;
&lt;h3 id=&quot;bulkhead-패턴을-적용하면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bulkhead-%ED%8C%A8%ED%84%B4%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%98%EB%A9%B4&quot; aria-label=&quot;bulkhead 패턴을 적용하면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BulkHead 패턴을 적용하면?&lt;/h3&gt;
&lt;p&gt;여기에 BulkHead 패턴을 사용하게 된다면? 두 쓰레드 풀을 격리하면 어떻게 될까? 여전히 서비스 B에 의존적인 B-API 의 요청을 철하는 쓰레드 풀은 고갈 상태가 유지되지만, 서비스 C에 의존적인 C-API 의 요청을 처리하는 쓰레드 풀은 고갈 상태가 아니므로, 서비스 B 장애 여부와 무관하게 요청을 잘 처리할 것이다.&lt;/p&gt;
&lt;p&gt;즉, BulkHead 패턴의 핵심 아이디어는 &lt;strong&gt;&quot;리소스 격리를 통한 장애 격리&quot;&lt;/strong&gt; 이다. 앞서들은 예시에선 쓰레드 풀을 예로 들었지만, 커넥션 풀과 같은 다른 자원에도 이 패턴을 적용해서 장애 격리를 해볼 수 있다. 또한 꼭 리소스 풀을 물리적으로 분리하지 않고도, 세마포어 등의 &lt;code class=&quot;language-text&quot;&gt;상호배제(Mutual Exclusion)&lt;/code&gt; 기법을 사용해서 BulkHead 패턴을 구현해볼 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;bulkhead-패턴-구현을-통한-장애-격리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bulkhead-%ED%8C%A8%ED%84%B4-%EA%B5%AC%ED%98%84%EC%9D%84-%ED%86%B5%ED%95%9C-%EC%9E%A5%EC%95%A0-%EA%B2%A9%EB%A6%AC&quot; aria-label=&quot;bulkhead 패턴 구현을 통한 장애 격리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BulkHead 패턴 구현을 통한 장애 격리&lt;/h2&gt;
&lt;p&gt;백문이 불여일타. 간단한 실습을 통해 BulkHead 패턴을 적용해보자. 우리는 의도적으로 장애 전파가 발생하는 상황을 가정해보고, 이에 BulkHead 패턴을 적용하여 장애를 격리해볼 것 이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;concurrent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ExecutorService&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;concurrent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Executors&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Main&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ExecutorService&lt;/span&gt; tomcatThreadPool &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Executors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newFixedThreadPool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; startTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            tomcatThreadPool&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callBService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;startTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            tomcatThreadPool&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callCService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;startTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        tomcatThreadPool&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shutdown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 작업 완료 후 shutdown&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callBService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; callTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;* 서비스 B 호출 완료 | 소요 시간: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; callTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ms&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callCService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; callTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;* 서비스 C 호출 완료 | 소요 시간: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; callTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ms&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드를 실행하면, &lt;code class=&quot;language-text&quot;&gt;tomcatThreadPool&lt;/code&gt; 의 쓰레드들은 서비스 B 를 호출하기 위해 모두 할당된 후, 작업을 잘 마치고 난 뒤 서비스 C 를 호출하기 위해 할당될 것이다. 그럼 아래와 같이 정상적으로 작업이 완료된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;* 서비스 B 호출 완료 | 소요 시간: 1ms
* 서비스 B 호출 완료 | 소요 시간: 1ms
* 서비스 B 호출 완료 | 소요 시간: 1ms
* 서비스 B 호출 완료 | 소요 시간: 1ms
* 서비스 C 호출 완료 | 소요 시간: 6ms
* 서비스 C 호출 완료 | 소요 시간: 6ms
* 서비스 C 호출 완료 | 소요 시간: 6ms
* 서비스 C 호출 완료 | 소요 시간: 6ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 서비스 B의 데이터베이스에 장애가 발생하여, 서비스 B가 굉장히 느리게 응답을 하기 시작했다. 이를 아래처럼 &lt;code class=&quot;language-text&quot;&gt;Thread.sleep()&lt;/code&gt; 을 사용해서 표현한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callBService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; callTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 데이터베이스 장애로 인해 응답이 매우 느림&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentThread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;interrupt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 인터럽트 상태 복구&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;서비스 B 호출 중 인터럽트 발생&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;* 서비스 B 호출 완료 | 소요 시간: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; callTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ms&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 서비스 B 에 장애가 발생한 상황에서는. 서비스 B를 호출하기 위한 쓰레드를 비정상적으로 오래 보유한다. 그렇게 되면 서비스 C만 필요한 요청이 톰캣 쓰레드를 할당받지 못하고, 아래처럼 장애가 전파될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;* 서비스 B 호출 완료 | 소요 시간: 20008ms
* 서비스 C 호출 완료 | 소요 시간: 20026ms
* 서비스 C 호출 완료 | 소요 시간: 20027ms
* 서비스 C 호출 완료 | 소요 시간: 20027ms
* 서비스 C 호출 완료 | 소요 시간: 20027ms
* 서비스 B 호출 완료 | 소요 시간: 20008ms
* 서비스 B 호출 완료 | 소요 시간: 20007ms
* 서비스 B 호출 완료 | 소요 시간: 20007ms&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;서비스 C를 호출하는 함수는 호출 즉시 결과를 반환할 수 있었으나, 서비스 B 호출로 인해 쓰레드 풀이 밀려 정상적으로 쓰레드를 할당받지 못하게 되었고, 그 결과 서비스 C를 호출하는 함수의 응답까지 덩달아 굉장히 느려졌다. 장애가 전파된 것이다. 실제 스프링 애플리케이션이었다면, 타임아웃이 발생하여 아예 응답을 주지 못하고 예외가 발생했을수도 있다.&lt;/p&gt;
&lt;h3 id=&quot;bulkhead-를-적용해-장애-격리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bulkhead-%EB%A5%BC-%EC%A0%81%EC%9A%A9%ED%95%B4-%EC%9E%A5%EC%95%A0-%EA%B2%A9%EB%A6%AC&quot; aria-label=&quot;bulkhead 를 적용해 장애 격리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BulkHead 를 적용해 장애 격리&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;concurrent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ExecutorService&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;concurrent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Executors&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Main&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ExecutorService&lt;/span&gt; bulkheadForB &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Executors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newFixedThreadPool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ExecutorService&lt;/span&gt; bulkheadForC &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Executors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newFixedThreadPool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; startTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// 서비스 B는 bulkheadForB에서 실행&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            bulkheadForB&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callBService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;startTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// 서비스 C는 bulkheadForC에서 실행&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            bulkheadForC&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callCService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;startTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// 작업 완료 후 자원 해제&lt;/span&gt;
        bulkheadForB&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shutdown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        bulkheadForC&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shutdown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callBService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; callTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// DB 장애로 지연&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentThread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;interrupt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;서비스 B 인터럽트 발생&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;* 서비스 B 호출 완료 | 소요 시간: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; callTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ms&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callCService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; callTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;* 서비스 C 호출 완료 | 소요 시간 : &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; callTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ms&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그럼 결과는 아래와 같을 것이다. 서비스 C를 호출하는 메서드는 더이상 서비스 B의 장애에 영향을 받지 않는다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;* 서비스 C 호출 완료 | 소요 시간: 2ms
* 서비스 C 호출 완료 | 소요 시간: 2ms
* 서비스 C 호출 완료 | 소요 시간: 2ms
* 서비스 C 호출 완료 | 소요 시간: 2ms
* 서비스 B 호출 완료 | 소요 시간: 20010ms
* 서비스 B 호출 완료 | 소요 시간: 20009ms
* 서비스 B 호출 완료 | 소요 시간: 20008ms
* 서비스 B 호출 완료 | 소요 시간: 20009ms&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;resilience4j-bulkhead&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#resilience4j-bulkhead&quot; aria-label=&quot;resilience4j bulkhead permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Resilience4j Bulkhead&lt;/h2&gt;
&lt;p&gt;실무에선 위와 같이 직접 BulkHead 를 구현할 필요가 없다. Resilience4j 에선 이미 벌크헤드 패턴 기능을 제공하기 떄문이다. Resilience4j의 Bulkhead 는 쓰레드 풀 또는 세마포어를 사용하여 동시 호출 수를 제어하며, 이에 따라 특정 작업으로 인해 과도한 리소스를 차지하지 않도록 리소스를 격리할 수 있도록한다.&lt;/p&gt;
&lt;p&gt;application.properties 에 아래와 같이 추가한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;resilience4j.bulkhead.instances.backendA.maxConcurrentCalls=4
resilience4j.bulkhead.instances.backendA.maxWaitDuration=0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;maxConcurrentCalls&lt;/code&gt; 는 허용할 최대 동시 호출 수, &lt;code class=&quot;language-text&quot;&gt;maxWaitDuration&lt;/code&gt; 은 최대 동시 호출 수에 도달했을 때 추가 요청이 들어온 경우 얼마나 기다려줄 것인지를 나타낸다. 지금과 같은 설정은 최대 4개의 동시 요청을 허용하며, 5번째 요청부터는 즉시 실패하도록 한다. backendA 는 벌크헤드의 이름이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resilience4j&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bulkhead&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;annotation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bulkhead&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resilience4j&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bulkhead&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bulkhead&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Type&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;web&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bind&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;annotation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GetMapping&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;web&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bind&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;annotation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RestController&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BulkheadController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bulkhead&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;backendA&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SEMAPHORE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/bulkhead&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;bulkhead&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 10초 지연&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentThread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;interrupt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;INTERRUPTED&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DONE&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 간단하게 컨트롤러를 작성했다. &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt; 은 &lt;code class=&quot;language-text&quot;&gt;Bulkhead.Type.SEMAPHORE&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;Bulkhead.Type.THREADPOOL&lt;/code&gt; 두가지가 있으며, 생략하면 기본값은 세마포어이다. 해당 엔트포인트로 4개를 초과한 동시 요청을 넣게 되면, &lt;code class=&quot;language-text&quot;&gt;BulkheadFullException&lt;/code&gt; 이 발생한다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/bulkhead-pattern/&quot;&gt;https://hudi.blog/bulkhead-pattern/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://internet-craft.tistory.com/77&quot;&gt;https://internet-craft.tistory.com/77&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[글로벌 트랜잭션 (2PC, XA, JTA Manager)]]></title><description><![CDATA[데이터베이스 레플리케이션에 대해 깊이있게 학습하던 중,  에 대해 알게 되었다. 그런데, 이 중에 글로벌 트랜잭션이라는 키워드가 등장한다. 이 글로벌 트랜잭션이란 무엇인지에 대해 찾아보니, 레플리케이션에서 GTID…]]></description><link>https://haon.site/database/global-transaction/</link><guid isPermaLink="false">https://haon.site/database/global-transaction/</guid><pubDate>Wed, 08 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;데이터베이스 레플리케이션에 대해 깊이있게 학습하던 중, &lt;code class=&quot;language-text&quot;&gt;GTID(Global Transaction ID)&lt;/code&gt; 에 대해 알게 되었다. 그런데, 이 중에 글로벌 트랜잭션이라는 키워드가 등장한다. 이 글로벌 트랜잭션이란 무엇인지에 대해 찾아보니, 레플리케이션에서 GTID 에 등장하는 글로벌 트랜잭션의 개념과 그냥 글로벌 트랜잭션은 전혀 다른 개념이었다. 글로벌 트랜잭션에 대해 글을 작성하며, GTID 에서 등장하는 개념과 무엇이 다른지 더 확실히 개념을 짚어보고자 한다.&lt;/p&gt;
&lt;h2 id=&quot;글로벌-트랜잭션&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%80%EB%A1%9C%EB%B2%8C-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98&quot; aria-label=&quot;글로벌 트랜잭션 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;글로벌 트랜잭션&lt;/h2&gt;
&lt;p&gt;지금까지 별다른 트랜잭션 처리가 없었던, 우리는 로컬 트랜잭션을 사용하고 있었던 것이 된다. &lt;strong&gt;로컬 트랜잭션&lt;/strong&gt;이란 하나의 데이터베이스의 커넥션에서 만들어지는 트랜잭션을 뜻한다. 그렇다면, 얼핏 글로벌 트랜잭션은 여러개의 데이터베이스 커넥션에서 만들어지는 개념일까 생각해 볼 수 있지만, 그건 아니었다.&lt;/p&gt;
&lt;p&gt;상황을 하나 생각해보자. 만약 하나의 트랜잭션으로 여러개의 데이터베이스에 접근하여 연산을 처리하고 싶다면 어떻게 해야할까? 이는 앞서 설명한 로컬 트랜잭션으로는 처리가 불가능하다. 로컬 트랜잭션은 단 하나의 데이터베이스에만 커넥션이 종속적으로 연결되어 있기 때문이다. 우리가 원하는 것은, 한 트랜잭션에서 여러 데이터베이스에 커넥션을 맺고 연산을 처리하는 것이다. 하지만, 한 트랜잭션에서 하나의 커넥션으로 여러 데이터베이스에 접근한다는 것은 말이 되지 않는다.&lt;/p&gt;
&lt;p&gt;이를 해결하는 방법이 바로 글로벌 트랜잭션이다. &lt;strong&gt;글로벌 트랜잭션은 실제 여러개의 데이터베이스와 커넥션을 맺고 통신하는 방식이 아니라, 별도의 트랜잭션 매니저를 활용해 트랜잭션을 관리&lt;/strong&gt;하는 방식이다. 즉, 트랜잭션에 참여중인 여러 독립적인 작업들 중에 하나라도 다른 데이터베이스에서 일어나는 경우를 처리하는 트랜잭션이다.&lt;/p&gt;
&lt;h3 id=&quot;jtatransactionmanager&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jtatransactionmanager&quot; aria-label=&quot;jtatransactionmanager permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JtaTransactionManager&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 JTA 는 Java Transaction API 의 준말로, XA 리소스 (분산 리소스) 간의 트랜잭션을 처리하는 API 이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이때, 자바는 글로벌 트랜잭션을 위해 &lt;strong&gt;JTA(Java Transaction API)&lt;/strong&gt; 라는 트랜잭션 매니저의 표준 API 를 제공하여 글로벌 트랜잭션을 관리할 수 있도록 한다. (트랜잭션 추상화를 학습할 때 등장하는 그 개념이 맞다.) &lt;code class=&quot;language-text&quot;&gt;PlatformTransactionManager&lt;/code&gt; 라는 추상 인터페이스의 구현체로는 JDBC 를 활용한다면 &lt;code class=&quot;language-text&quot;&gt;DataSourceTransactionManager&lt;/code&gt;, JPA 를 활용하는 경우라면 &lt;code class=&quot;language-text&quot;&gt;JpaTransactionManager&lt;/code&gt; 구현체를 사용하게 되는데, 여러 리소스(데이터베이스) 를 사용하는 경우에는 &lt;code class=&quot;language-text&quot;&gt;JtaTransactionManager&lt;/code&gt; 를 활용한다. &lt;code class=&quot;language-text&quot;&gt;JtaTransactionManager&lt;/code&gt; 라는 구현체를 통해 우리는 JTA 라는 API 를 사용하여 글로벌 트랜잭션으로 동작하게 되는 것이다.&lt;/p&gt;
&lt;p&gt;글로벌 트랜잭션 매니저인 &lt;code class=&quot;language-text&quot;&gt;JtaTransactionManager&lt;/code&gt; 는 데이터베이스와 메시징 서버를 제어하고 관리하는 각각의 리소스 매니저와 XA 프로토콜을 통해 연결된다. 이를 통해 트랜잭션 매니저가 여러 리소스에 대해 종합적으로 트랜잭션을 제어할 수 있게된다.&lt;/p&gt;
&lt;h2 id=&quot;xa-2pc&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#xa-2pc&quot; aria-label=&quot;xa 2pc permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;XA, 2PC&lt;/h2&gt;
&lt;p&gt;XA 란 2PC(Two-Phase Commit) 을 통한 분산 트랜잭션 처리를 위한 표준이다. 그리고 JTA 트랜잭션 매니저는 각 리소스 매니저간에 XA 프로토콜을 통해 통신한다고 했다. 보통 1PC 라면 begin, end, commit 의 절차로 수행되는 반면, 2PC는 begin, end, prepare, commit 의 절차로 수행된다. (이때, prepare 과정은 아마 트랜잭션에 참여한 리소스들이 서로 같은 리소스라고 확인하는 과정을 뜻하는 듯하다. 그 때문에 로컬 트랜잭션은 prepare를 안해도 되고, 1PC 로 처리된다. 정확한 개념은 추후 학습이 필요하다.)&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f77b75ced8d50a8da92d2b1d0236bd60/37048/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.12883435582822%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABJUlEQVR42k2R626CQBCFef+X85eJ1ZiIVSwgl+UuoKfzTVPjJptdZufchqhtW43jqKHv9Xq91NuZpqk6qxdFoaos1TSNivtd67r6bkKjsigFFsw0Td5HLeLSdd2bEBIID4eDqqpSfDppv98rSzPN86zketVms9HxeHTxYRi03W51jmOxIshw2Datq6N6js+6m6O6rpXbyf1rt/PzZAJgvi8XF8uyzE0g9nw+zaG5QGk0pWVZfJfmOoSgn9vNSSBIksSBV3MYTIgeaj4Oqz8eD8dGWMYhD8u86HPhJs9zV75Bbne+6e+HXhdzCQ5RxkXCiAaidW3nKv+DZxGfN1LgCNIQ/hxRA4tLTPFj3g6JVFe1x4SAeXwuBGiGhDeSUAMLZl3Wd7pfRaoXalvb3bwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/f77b75ced8d50a8da92d2b1d0236bd60/a6d36/image.png&quot;
        srcset=&quot;/static/f77b75ced8d50a8da92d2b1d0236bd60/222b7/image.png 163w,
/static/f77b75ced8d50a8da92d2b1d0236bd60/ff46a/image.png 325w,
/static/f77b75ced8d50a8da92d2b1d0236bd60/a6d36/image.png 650w,
/static/f77b75ced8d50a8da92d2b1d0236bd60/e548f/image.png 975w,
/static/f77b75ced8d50a8da92d2b1d0236bd60/3c492/image.png 1300w,
/static/f77b75ced8d50a8da92d2b1d0236bd60/37048/image.png 1352w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;2PC 는 둘 이상의 리소스에 대한 트랜잭션을 원자적으로(atomic) 처리하기 위한 알고리즘이다. 위에서도 다른 리소스 둘에 대한 요청을 원자적으로 처리하기 위해 1PC 에서 prepare, commit 이 추가되었다. 이 방법으로 서로 다른 데이터베이스간의 정합성을 보장할 수 있다고 한다.&lt;/p&gt;
&lt;h2 id=&quot;gtid-vs-글로벌-트랜잭션&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#gtid-vs-%EA%B8%80%EB%A1%9C%EB%B2%8C-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98&quot; aria-label=&quot;gtid vs 글로벌 트랜잭션 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GTID vs 글로벌 트랜잭션&lt;/h2&gt;
&lt;p&gt;그래서 GTID 에서 등장하는 글로벌 트랜잭션과 무엇이 다른가? GTID 에서 등장한 글로벌 트랜잭션은 &lt;strong&gt;레플리케이션 환경에서 트랜잭션을 전역적으로 유일하게 식별하고 추적한다는 점에 초점&lt;/strong&gt;이 맞춰진 개념이다. 즉, 출처가 식별되는 트랜잭션 개념에 가깝다. 반면 위에서 살펴본 일반적인 의미의 글로벌 트랜잭션은 분산 데이터베이스 환경에서 하나의 논리적 작업 단위로 실행되는 트랜잭션을 의미한다. (실제로 레플리케이션 환경에서, 지금까지 학습했던 트랜잭션들은 커넥션을 맺었고, 각기 다르게 처리되었다.)&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/global-transaction/&quot;&gt;https://hudi.blog/global-transaction/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://heni.tistory.com/10&quot;&gt;https://heni.tistory.com/10&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://yeonyeon.tistory.com/279&quot;&gt;https://yeonyeon.tistory.com/279&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://manhyuk.github.io/transaction/&quot;&gt;https://manhyuk.github.io/transaction/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ibm.com/docs/ko/was-nd/8.5.5?topic=server-global-transactions&quot;&gt;https://www.ibm.com/docs/ko/was-nd/8.5.5?topic=server-global-transactions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MSA 환경에서 SAGA 패턴으로 안전한 분산 트랜잭션 구현하기]]></title><description><![CDATA[코어뱅킹 서버의 환전 기능 토스뱅크는 코어뱅킹 서버의 도메인들은 Oracle DB 를 그대로 참조하고 있다. 반면 새롭게 개발중인 도메인들은 MySQL…]]></description><link>https://haon.site/article/toss-slash/msa-reward-transaction/</link><guid isPermaLink="false">https://haon.site/article/toss-slash/msa-reward-transaction/</guid><pubDate>Thu, 02 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;코어뱅킹-서버의-환전-기능&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BD%94%EC%96%B4%EB%B1%85%ED%82%B9-%EC%84%9C%EB%B2%84%EC%9D%98-%ED%99%98%EC%A0%84-%EA%B8%B0%EB%8A%A5&quot; aria-label=&quot;코어뱅킹 서버의 환전 기능 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;코어뱅킹 서버의 환전 기능&lt;/h2&gt;
&lt;p&gt;토스뱅크는 코어뱅킹 서버의 도메인들은 Oracle DB 를 그대로 참조하고 있다. 반면 새롭게 개발중인 도메인들은 MySQL 을 참조한다. 예를들면 예전부터 존재했던 원화 계좌 서버는 변동없이 기존 코어뱅킹의 데이터베이스인 Oracle 을 참조하고 있다. 반면 신규 상품인 외화 계좌 서버는 DB 까지 분리된 MSA 환경에서 MySQL 을 참조하고 있다.&lt;/p&gt;
&lt;p&gt;즉, 환전 기능은 데이터베이스와 마이크로 서버 모두가 독립적으로 분리된 원화 계좌 서버, 외화 계좌 서버로 2개를 동시에 활용하며 구현되어야 했다. 토스뱅크의 환전 기능은 토스뱅크 앱으로 간편하게 원화를 지불하고 외화로 바꿔받는 기능이다. (또는 그 반대이다.)&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/986f8fc0bf87d7257b522217528338ea/38cea/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.14723926380368%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB1klEQVR42pWSPWhTURTH38tHk+eQEpIh1Rc/SEpC0AaaKhlaELRbIA9NIVKN2NGlSxd3OxRF0UICteoiBLXFEg1Ya4eqQ7WDNn6g7aCpS3y8YFD3n303pq+tIvTCj3u4957/+bhHkiSJnSDLskAy+fcbCYfDsRWnE+cf0mmNXO4sg4On1zmD3+/fEO7t7ePc0BAnMxm8Xq8luJ1AIEA0GiUWi6HrOt90g89f1jBXKpUSb3bvUZl//oqlykdWqjXSJwaa/oqicHF0lHyhwPXxcbGHw2GRXTAY5N37D3xaWWW58hbdMDje398M2tHB1fwtCreL3Jkq03f0WFPQ5/PRaDT48fMXtZousshms+LSZrMRiURI9PTQ3Z2gqyuO2+3eqMTT3k7s4CHU4F6rQo/Hw+PZORZfLrHw7AWv31RIJpPY7XbRr2LxLqWHj7h3f5onc0+Jx+PCUVF2cfnKNWZKZaYelEgcPmL10Myk9SGmbYqpqkooFMIw6tTr36lWv4rsNU0TjqFwJzOzC0yX51lcXkXLZC1BWW5i2TZhu1wuxsYuMTExST5f4MbkzfX+doo7Z1sbA6dyDI9c4PzwCPv2H9gsKG9hp7P51xz+j+0zujmg2ZoWrfPfPgZJbe85e98AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/986f8fc0bf87d7257b522217528338ea/a6d36/image.png&quot;
        srcset=&quot;/static/986f8fc0bf87d7257b522217528338ea/222b7/image.png 163w,
/static/986f8fc0bf87d7257b522217528338ea/ff46a/image.png 325w,
/static/986f8fc0bf87d7257b522217528338ea/a6d36/image.png 650w,
/static/986f8fc0bf87d7257b522217528338ea/38cea/image.png 678w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;여기서 문제는, 원화 계좌 서버와 외화 계좌 서버가 서로 다른 데이터베이스를 바라보고 있다. 이에 따라 분산 트랜잭션 기법이 필요함을 느낄 수 있다. 즉, 글로벌 트랜잭션을 구현해야 한다.&lt;/p&gt;
&lt;h2 id=&quot;분산-트랜잭션-구현하기-2pc-vs-saga&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%84%EC%82%B0-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-2pc-vs-saga&quot; aria-label=&quot;분산 트랜잭션 구현하기 2pc vs saga permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;분산 트랜잭션 구현하기 (2PC vs SAGA)&lt;/h2&gt;
&lt;p&gt;2PC 와 SAGA 패턴은 &lt;a href=&quot;https://haon.blog/article/toss-slash/distribution-transaction/&quot;&gt;MSA 환경에서 SAGA 패턴과 2PC 패턴를 통해 트랜잭션 일관성 보장하기&lt;/a&gt; 에서도 다룬적이 있다. 코어뱅킹 서버의 환전 기능에는 어떠한 구현 방식이 적합할까?&lt;/p&gt;
&lt;p&gt;2PC 의 경우 모든 데이터에 락을 걸기도 하고, 글로벌 트랜잭션에 참여하는 모든 참여자로부터 OK 신호를 받을 때 까지 기다려야한다. 즉, 참여자중에 트랜잭션 처리가 가장 느린 참여자들을 위해 다른 모든 참여자들이 대기해야하므로 성능이 느리다.&lt;/p&gt;
&lt;p&gt;반면 SAGA 패턴의 경우 각 서비스들에서 로컬 트랜잭션만 진행한다는 점에서 높은 가용성과 확장성을 갖는다. 몰론 일부 로컬 트랜잭션들만 커밋된 중간 상태가 노출되며, 롤백을 위한 보장 트래잭션을 직접 구현해야 한다는 단점이 있긴하다. 하지만 토스뱅크는 환전 서비스가 높은 트래픽을 견디고 카드, 회계등 다른 참여자들도 향후에 더 생길 수 있다는 점에서 SAGA 패턴을 택했다.&lt;/p&gt;
&lt;h3 id=&quot;오케스트레이션-기반-사가-vs-코레오그래피-기반-사가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%A4%EC%BC%80%EC%8A%A4%ED%8A%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EA%B8%B0%EB%B0%98-%EC%82%AC%EA%B0%80-vs-%EC%BD%94%EB%A0%88%EC%98%A4%EA%B7%B8%EB%9E%98%ED%94%BC-%EA%B8%B0%EB%B0%98-%EC%82%AC%EA%B0%80&quot; aria-label=&quot;오케스트레이션 기반 사가 vs 코레오그래피 기반 사가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;오케스트레이션 기반 사가 vs 코레오그래피 기반 사가&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ee1f60f24637452475e965b145702ba8/78612/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.809815950920246%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABwElEQVR42j2RW28SURSFZzABudgKZYAWucPAMDPAUKZcLZei1VopWiupVsKDrbFVX2psfMHEX+Crv/bzME192Fkn2V/WXnsfyeNfR9MN8kWNWFojni07tZkukShUMOwxQWWTYCSOVTOJZ0psZW65lUaTKvlKl2K1jdvjRfI9jDA5njE+mJA1e2SMFqlyS0Admk9OOf/5l7xWIZ4zmS/mmM09ilafgtlCrT1GtwcczJecXS7xeP1Ia6EIVu8FRnOMsTPg9fsLZosruuMJtd5LTi5+kchpIk2ZRv9IJB6iijT96RXtpzM0q8Nwesnh6ec7wyjadh9dVL27z/nXG758XzJ8foy1e8TbT7/JqAaxVEkMHDmGlZ0heyfX9A8/iHefgTCfnn3D4wsgrW9EKdV3KVQ7Ys02Ka1BstQgp9vYozcsrv+QVXXnptr2gIJgCtWuWNkW2kard9l/d8Ps4w/cXh+Sfy1IUq2SzFecSuRNoSaPcjqpYp3m8BXRrRQbsSRp1frPJFaaW31SWQwaYfee4b4vDGVZRpZdSJJMIPAAJRJBURRCoRCrniRJTu+Oc7nuEQyGCIcVwoLz+f0Oc8vK/AN1KdV+zQ7GMwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/ee1f60f24637452475e965b145702ba8/a6d36/image-1.png&quot;
        srcset=&quot;/static/ee1f60f24637452475e965b145702ba8/222b7/image-1.png 163w,
/static/ee1f60f24637452475e965b145702ba8/ff46a/image-1.png 325w,
/static/ee1f60f24637452475e965b145702ba8/a6d36/image-1.png 650w,
/static/ee1f60f24637452475e965b145702ba8/e548f/image-1.png 975w,
/static/ee1f60f24637452475e965b145702ba8/78612/image-1.png 1260w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;코레오그래피 사가는 중앙 제어가 없기 떄문에 SPOF 가 발생하지 않고, 각 서비스들간에 메시지 큐를 활용해 통신하므로 결합도가 느슨하다.&lt;/p&gt;
&lt;p&gt;하지만, 토스뱅크는 모니터링이 중요했기 떄문에 오케스트레이션 사가를 택했다. 클라이언트 요청을 받아 환전을 시작하는 환전 서버가 필요했고, 현재 진행중인 환전들의 상태를 실시간으로 관리해야 했기 떄문이다. 예를들어 환전 한도를 구현하려면, 현재 진행중인 환전의 금액과 상태도 추적해야 했기 떄문이다.&lt;/p&gt;
&lt;h2 id=&quot;환전-구현-방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%99%98%EC%A0%84-%EA%B5%AC%ED%98%84-%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;환전 구현 방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;환전 구현 방법&lt;/h2&gt;
&lt;p&gt;환전 기능 이해를 위해, 한화 1300원을 지불하고 외화 1달러를 지급받는 것을 가정해보자. 이떄, 실패 하는 경우를 가정해보겠다. 실패의 종류는 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 정상적인 실패와 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 비정상적인 실패(에러) 로 나뉜다.&lt;/p&gt;
&lt;p&gt;정상적인 실패란 잔액 부족, 고객/계좌 거래 제한등으로 인해 입출금이 실패하는 것으로, 당연히 실패하는 것이 정상적인 케이스를 뜻한다. 반대로 네트워크 에러, 타임아웃등의 에러가 발생하는 경우이다.&lt;/p&gt;
&lt;h3 id=&quot;환전-실패-case1---입금-실패&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%99%98%EC%A0%84-%EC%8B%A4%ED%8C%A8-case1---%EC%9E%85%EA%B8%88-%EC%8B%A4%ED%8C%A8&quot; aria-label=&quot;환전 실패 case1   입금 실패 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;환전 실패 CASE1 - 입금 실패&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 608px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1fa5ea8ce5fe99f056f0e553ca0bba3f/18872/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.37423312883436%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABqUlEQVR42o2Su04CURCGF8KdsCwIUTovVCoJDY0llxYeAKPGSyWWRO3USkCjDyBoQnwXYGs1Gok3BGKjYhQWY35nD643dMMmX84mM/PPP3MOx3Ec1NBqtdBoND9QrZETdDpdF2pF8wsL2NnZxWYqjVQ6w/B6vUq8u0BxYTKZEAgE4PP5EAqFYDabYbVaIYoiJKmNy6sb3FSqkL9YLNap5+12zM3NI7GYQCKxxIjHJ6HT61mCxWKBIAjweDxwOBzsv1QS0X59Q+W2hlr9jglGFUG5e7MpoSW94vq6woK1Wh3eoSHwPA8bNTSRM3mXygThcASzZGJqepqYYWd//0AnPk6CLy0JzVYb5fIFE6xW6xgfGYGDXPKE/tdF+P1+RCIRBINBIsTWITtncZfLhbX1DaQzW8hsbTOWV1ZhNBpZgkCnh8YetNnQZzCAJwqFImt8/9DAY+OpM3I0+v+lKBejoXOYhPxOJybcbggkZidKRRGNp2ccHZ/g9Oz8QzD29WxkN98xUJHae0smk8jnD7GXzSFL5PYPMDo2pu7w0+k3uN7oOfHnOv5Ajr8DdEFjpLtV5lcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/1fa5ea8ce5fe99f056f0e553ca0bba3f/18872/image-2.png&quot;
        srcset=&quot;/static/1fa5ea8ce5fe99f056f0e553ca0bba3f/222b7/image-2.png 163w,
/static/1fa5ea8ce5fe99f056f0e553ca0bba3f/ff46a/image-2.png 325w,
/static/1fa5ea8ce5fe99f056f0e553ca0bba3f/18872/image-2.png 608w&quot;
        sizes=&quot;(max-width: 608px) 100vw, 608px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;만약 사용자가 원화 계좌로 1300원을 출금하는데는 성공했지만, 외화 계좌로 1달러는 입금받는데 실패했다면 어떻게 처리해야할까? 이는 SAGA 패턴에서 보상 트랜잭션을 통해 롤백하여, 원화 계좌로 입금된 1300원을 사용자에게 원상태로 되돌려줘야 한다.&lt;/p&gt;
&lt;p&gt;이를 위해 보장 트랜잭션을 내부 로직을 원화 계좌로 1300원을 입금해주는 방식으로 구현하여, 사용자에게 돈을 다시 되돌려줄 수 있다. 즉, 유저 입장에서는 -1300원 되었던것이 다시 +1300원 되어 0원이 되어서, 원상 복귀 된 것이다.&lt;/p&gt;
&lt;h3 id=&quot;왜-출금이-입금보다-선행-되어야-할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-%EC%B6%9C%EA%B8%88%EC%9D%B4-%EC%9E%85%EA%B8%88%EB%B3%B4%EB%8B%A4-%EC%84%A0%ED%96%89-%EB%90%98%EC%96%B4%EC%95%BC-%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;왜 출금이 입금보다 선행 되어야 할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 출금이 입금보다 선행 되어야 할까?&lt;/h3&gt;
&lt;p&gt;SAGA 패턴의 특성상, 일부 로컬 트랜잭션만 커밋된 중간 상태가 노출될 수 있다고 했었다. 이 특성 때문에 &lt;strong&gt;출금이 입금보다 선행되어야 한다.&lt;/strong&gt; 만약 입금이 선행된다면 무슨 문제가 발생할까?&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2879100c6fc36f34fa79b333d5b0890d/ef6b9/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.39877300613497%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABj0lEQVR42oVSTUtCURD1vee3KX7l00SLR4phfmwMq2Vpf0Ah+h+2sx+QUNmism21LloVrapFpiZBEa4LKoxSWhStTvfensIzrAuHGYaZMzNnrkqlUqEXHMcxa7fbkUrPIU2QSEx04/9AJpGtqNdj2u0GT/z5TAb0vbXaaDQaMJlMioZ9CfWExOl0wkYmGiFIulwQeQGxaBSl0jbWiuvI55eg0WhYEc/zsFqtcDgczAqCoCRcLhTw8fmFVvsduzt7P0UkqXcSnuOZ9fl8aL684um5yaaPxeLdRowwl1vEze0davVrFFdWYdHrMBoIwOv1skRBJqegRX7/MC7KFZQvq6hUrxAKjSkJqaPVamEkqw8aDJCsNgxZLEjNzOLo+AT7B4fY2NxikriJvqIoQq1WQ6fTdWX4dZQOYjYbpkgB9ReyWdw/PKJaq+P07BwDZjPC4TDi8fhfx1EG1CRJK4tMJxiPRJBMTsLj8bAY3aQjAb06hYFsZTQaOw36/ylOnkLWRgFKHCA6B4NBdiRJktj633CvAbChMFD4AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/2879100c6fc36f34fa79b333d5b0890d/a6d36/image-3.png&quot;
        srcset=&quot;/static/2879100c6fc36f34fa79b333d5b0890d/222b7/image-3.png 163w,
/static/2879100c6fc36f34fa79b333d5b0890d/ff46a/image-3.png 325w,
/static/2879100c6fc36f34fa79b333d5b0890d/a6d36/image-3.png 650w,
/static/2879100c6fc36f34fa79b333d5b0890d/ef6b9/image-3.png 832w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;외화 계좌로 1달러를 가장 먼저 입금했다고 해보자. 그 두에 원화 계좌로부터 1300원을 출금해오려고 하는데, 이때 실패하여 보상 트랜잭션으로 롤백해야한다. 그런데 이때, 카드 서버에서 결제 요청이 와서 외화 계좌로 부터 입금되었던 1달러를 가져간다면, 외화 계좌는 0달러가 된다. 맙소사. 이 상황이 발생하면 보상 트랜잭션 처리가 불가능해진다. 외화 계좌가 카드 결제로 인해 1달러에서 0달러로 변했으므로, 현재 잔고 0달러에서 1달러를 가져오는 것은 불가능하기 때문이다. (계좌 잔고가 0달러에서 1달러가 출금되어 -1달러가 되는건 말이 안되니깐.)&lt;/p&gt;
&lt;p&gt;즉, &lt;strong&gt;환전을 입금부터 실행하면 환전이 끝나기전에 다른 트랜잭션에서 입금한 돈에 개입하여 출금해버릴 수 있다.&lt;/strong&gt; 따라서 환전 기능은 반드시 출금이 선행되어야 한다.&lt;/p&gt;
&lt;h3 id=&quot;입금-출금시-통신-방식-http-vs-kafka&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%85%EA%B8%88-%EC%B6%9C%EA%B8%88%EC%8B%9C-%ED%86%B5%EC%8B%A0-%EB%B0%A9%EC%8B%9D-http-vs-kafka&quot; aria-label=&quot;입금 출금시 통신 방식 http vs kafka permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;입금, 출금시 통신 방식 (HTTP vs Kafka)&lt;/h3&gt;
&lt;p&gt;이때, 입출금시 어떠한 통신 방식으로 통신하고 있을까? 크게 HTTP 와 Messaging 방식이 있을 것이다.&lt;/p&gt;
&lt;p&gt;대부분 SAGA 패턴에서는 Messaging 방식으로 구현된다. 비동기 방식이며, 서비스간 결합도가 느슨해지며, 메시지 브로커 레벨에서 재시도와 같은 애러 핸들링을 지원하기 때문에 데이터에 대한 결과적 정합성을 보장하는데 유리하다.&lt;/p&gt;
&lt;p&gt;하지만, 토스뱅크는 HTTP 통신 방식을 택했다. 그 이유는 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; &lt;strong&gt;출금 결과를 반드시 알고 입금으로 넘어가야 하기 때문이다. (By 동기 방식)&lt;/strong&gt; 그리고 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; &lt;strong&gt;유저에게 환전이 즉시 완료되기를 알려줘야 하기 때문이다. 만약 환전 기능이 지연되었다면 유저에게 환전이 지연되었음을 알려줘야 하는데, 이 경우 타임아웃 기능이 필요하다.&lt;/strong&gt; 타임아웃을 비동기 메시징 방식으로 구현하려면 입출금 결과를 다시 메시지로 받고 Polling 하는 등의 구현 복잡도가 높아진다.&lt;/p&gt;
&lt;h3 id=&quot;출금-취소는-messaging&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B6%9C%EA%B8%88-%EC%B7%A8%EC%86%8C%EB%8A%94-messaging&quot; aria-label=&quot;출금 취소는 messaging permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;출금 취소는 Messaging&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 372px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7a85f368cfa14d8e8cc265b26ba2a608/98b6e/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 79.75460122699387%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAAByklEQVR42q1Uy0pCURT1rQMfqBNFhNLSRn6Dz2FNK7O5QVZC9Q8FGdSthLJpfYGWhTSN6EUvi970jl5Go4rV3UduaFFoeWFxNueeu9Zee599RSKRCBVG+R9JJBJIpdIilE0oFouL1pIypMOF+InU7fagu6cXkY5OdHZF0RWNoqqqupiQbHwlFPY0Gg30ej2zptVqkUzN4P4xh8PjU2zvZPH2DoTDbaVblslkMJvNcDqdsNnsSKfTOLu4wNZ2Fssra3jlGSOR9vx5oaDBYAsymQWkePVkMsXiUKgVcrkcdnsNTCYTFAoFO9vcHMT4eAJDwxy4kVGMxeNwuVx5QlKngOM40EOKG5tbLJ6anobeYIDVaoXH44Hf70cgEGC2f2yQQNjX189Izs4vcXV9y+KB2CB7RwRk2Wg0snrm7dfB4XDwcKK21gGVSpUnFFQsFgt8Ph+8XoKXxbQnNIwgiE8kJnFze4f9gyPs7u3j7uEJjY1NpTWFlHU6HcuOMlOr1ZidTePw6ASLi0uYm88g9/yCDv4Kfbs2lEEhhImgZiiVyk9bMb4UlN3K6joDxfX1DX8fPRKgrKm2Wn7V8CuJ/5mw4j8HYYoK8S/C3/ABFevJYrnILIcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7a85f368cfa14d8e8cc265b26ba2a608/98b6e/image-4.png&quot;
        srcset=&quot;/static/7a85f368cfa14d8e8cc265b26ba2a608/222b7/image-4.png 163w,
/static/7a85f368cfa14d8e8cc265b26ba2a608/ff46a/image-4.png 325w,
/static/7a85f368cfa14d8e8cc265b26ba2a608/98b6e/image-4.png 372w&quot;
        sizes=&quot;(max-width: 372px) 100vw, 372px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;반면 보상 트랜잭션의 구현 내용인 출금 취소는 Messaging 방식을 택했다. 그 이유는 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 출금 취소는 마지막 과정이며, &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 유저가 이를 기다릴 필요가 없기 떄문이다. (돈이 실제로 다시 입금되는 것을 동기로 기다릴 필요는 없다.) 그리고 &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 출금 취소 또한 실패할 수 있다. 이미 입금이 실패했기 때문에, 출금된 돈은 반드시 유저에게 돌려줘야한다. 그런데 이런 애러 핸들링을 메시지브로커 원화 계좌 컨슈머에게 위임한다면, 결과적 정합성을 보장하기 유리할 것이다.&lt;/p&gt;
&lt;h2 id=&quot;비정상적인-에러-핸들링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EC%A0%95%EC%83%81%EC%A0%81%EC%9D%B8-%EC%97%90%EB%9F%AC-%ED%95%B8%EB%93%A4%EB%A7%81&quot; aria-label=&quot;비정상적인 에러 핸들링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비정상적인 에러 핸들링&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/305ed0e8f7d54ed6cdda63da6fa7ffc0/69476/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.601226993865026%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABQ0lEQVR42rVSXUvCYBTe95ZLN0FZajg3aYMGMsVduUl/wJ8b9gWr6KbwF0iQGdF90fXTOQPDogsFu3g47/uewznPeZ5XEAQBO4WiKBgMBkiSBHEcI4oi9Pt9pGmK0WiEarVaFIqiuFlDLrQsq4Bt2wW4Sa1Wg+M4ME1zW5Y7Xnn9wmxVVYWu62ApFLrrkgRdlqFRZMj0plGeazRN+7uhRIUcx+Mx7h9muLm9w+n0HHHrEMckxVGlgoCiXyrhZDjE5VWOPL/G9OwC7Xb7t8YCZGLAcTKZ4P3jEy+vb1g8L9E4aGC/XEYQBOh2uzRYREZmPS2WBebzR8qFWCf1Y2XDMOC6HXiejxax40FsSrPZRL1eL/KGsYdOx4Pv+8TO/SazkSlhGGJIKzJ6vR6yLKNh3nYusxYrsDEsPIPPK7PWa/7923wB6oAM7d98wnUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/305ed0e8f7d54ed6cdda63da6fa7ffc0/a6d36/image-5.png&quot;
        srcset=&quot;/static/305ed0e8f7d54ed6cdda63da6fa7ffc0/222b7/image-5.png 163w,
/static/305ed0e8f7d54ed6cdda63da6fa7ffc0/ff46a/image-5.png 325w,
/static/305ed0e8f7d54ed6cdda63da6fa7ffc0/a6d36/image-5.png 650w,
/static/305ed0e8f7d54ed6cdda63da6fa7ffc0/69476/image-5.png 926w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;환전 서버에서 원화 계좌 서버로 출금 요청을 보냈을 때 타임아웃이 발생했다면 어떻게 해야할까? 타임아웃 자체가 나버리면 원화 계좌 서버에서 요청을 정상적으로 처리했는지 안했는지, 즉 성공/실패를 확정할 수 없다.&lt;/p&gt;
&lt;p&gt;이는 원화 계좌 서버로 다시 출금 결과룰 재확인(Retry)하여 그에 따라 조취한다. 재확인 결과로, 만약 출금에 성공했음을 알게 되었다면 출금 취소를 처리하면 된다. 반대로 출금에 실패했다면 요청 자체가 실패한 것이므로, 추가적인 조취 없이 환전을 실패 처리하면 끝이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d92e346844952d1ba2c0f0f94412848e/5b481/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.828220858895705%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABaUlEQVR42qVSy07CUBBtS+mDVJJuSNqSUBGR4kZIoGxwAVXCZ/AVxD3fQgy+4kpjojHG+Cm4AXeyPc7cUCIgJGqT02nuPTM9c2YkWZahqioURUEikRAxmUyCzyVJ+j04OQxDgVqthiiK0O12UalU/laQX6yMVXLUNE0o1HWDLjerlGVlgRXeMpFb5nY5MtYKzRHf/8BbTmCluq4LCDJ7TFHlLjhx7i13YpqmwFrBeACGYWA0usTd/QNeXt9w1u/Dox8cpNPIUWIulcIuxcDzcHF1g+ubWzw+PaPX6y26WypoWRbG43d8zmbg53w4hEV++q6DcqGAoLAHm4oelcuYTKaYTD8EbzAYIJ6FtOpfvR6i1WrTtE+wXyyK8x1W6PvwslnIxNHIjmbzGFE7wmmnA5/uvgnbvAImqTkkNdVqFUEQoFQqodFoIJ/Pb1+bVZXxgvNgMpkMPPLMdV04jiO+bdsWnBhbp/xffAFfiRPsjCsOLwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d92e346844952d1ba2c0f0f94412848e/a6d36/image-6.png&quot;
        srcset=&quot;/static/d92e346844952d1ba2c0f0f94412848e/222b7/image-6.png 163w,
/static/d92e346844952d1ba2c0f0f94412848e/ff46a/image-6.png 325w,
/static/d92e346844952d1ba2c0f0f94412848e/a6d36/image-6.png 650w,
/static/d92e346844952d1ba2c0f0f94412848e/5b481/image-6.png 846w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;입금의 경우도 마찬가지이다. 입금 요청에서 타임아웃이 발생하면 입금 결과를 다시 재확인한다. 그 결과로 입금에 성공했음을 알게 되면 출금과 입금 모두 성공한 것이므로 환전을 성공 처리하면 된다. 반대로 입금에 실패했다면, 앞서 출금했던 내역을 보장 트랜잭션으로 취소한다.&lt;/p&gt;
&lt;h3 id=&quot;재확인조차-못한다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%AC%ED%99%95%EC%9D%B8%EC%A1%B0%EC%B0%A8-%EB%AA%BB%ED%95%9C%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;재확인조차 못한다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;재확인조차 못한다면?&lt;/h3&gt;
&lt;p&gt;만약 상대방 서버(외화 계좌, 원화 계좌) 자체에 장애가 터져거나 네트워크 병목이 발생하여 재확인 조차 못한다면 어떻게 해야할까? 이 해결법은 &lt;a href=&quot;https://haon.blog/article/toss-slash/safely-network-handling-errors/&quot;&gt;확정할 수 없는 모종의 네트워크 에러/지연 문제를 안전하게 서비스하기&lt;/a&gt; 에서도 다룬적이 있다. 바로 카프카와 같은 메시지 큐를 이용하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8204bf70f006049385110130e9ffe508/9cab2/image-7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.89570552147239%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABiElEQVR42q1Rv0tCURh9Kv56g/rUQQd9T1ETeSgqZtCjIExIFxtKQ9v8F3QJs4ygqdqKpmpJ6Qe0BC1BS7RE5uA/YDVWSygtJ+8TyxrUwQsHvvtx7rnnfB9FURRGCq1WC0EQEAwGRXi9XgQCAfA8D5/PB7/fj1AoBLvdLj6QSCT9BdVqNZxOJ2w2G1iWBcdx8Hg8f+4ulwtGo3FYlyOO3I3RL8rAmL2QSqViMeZ24+z8AiflCrZ3dmEymcAwDKwsh/zKGgqlLRTXN0FmTvgOhwOHR8coV06xt38AnU7XEZTJZGJBFkPOZ7OFer0uLoPjWMxEori6ucPl9S3uH55gtVh++G/vH2i2vtBoPMNsNncEu3H0jB4Li0mkUkuIRGYhl8tB3CtVKoxPTCI6F4cwNQ26vUTC12g0SCTmkWzzY7E4FApl/6V0PyKxM5llZLNZpNNpUWjAXH8FSPxekH44HEbj5RWP1RqqtRoMBoPYJ+7/c4dyaGnPrFTaQGG1iFwuD5qmh3M4KnwDtmVAQPteiOcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/8204bf70f006049385110130e9ffe508/a6d36/image-7.png&quot;
        srcset=&quot;/static/8204bf70f006049385110130e9ffe508/222b7/image-7.png 163w,
/static/8204bf70f006049385110130e9ffe508/ff46a/image-7.png 325w,
/static/8204bf70f006049385110130e9ffe508/a6d36/image-7.png 650w,
/static/8204bf70f006049385110130e9ffe508/9cab2/image-7.png 864w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;토스뱅크에는 메시지를 지연시켜서 발행할 수 있는 카프카 메시지 스캐줄러 서버가 존재한다. 일반적인 경우, Producer 가 메시지를 발행하면 그 즉시 메시지 브로커를 통해 컨슈머에게 메시지가 전달된다. 그런데 여기에, 메시지에다 지연시간 값을 넣어서 메시지를 발행하면 별도의 지연 Topic 을 거쳐서 메시지가 카프카 메시지 스캐줄러에게 전달된다. &lt;strong&gt;카프카 메시지 스캐줄러는 메시지를 지연시간만큼 대기시킨 후, 원래의 Topic 에 메시지를 다시 발행하여 컨슈머가 해당 메시지를 가져갈 수 있게 유도한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이때, Producer 와 Consumer 를 모두 환전 서버가 된다면 어떤 효과가 있을까? Kakfa 에 발행한 메시지를 지연 시간만큼 지연된 후 다시 자기 자신에게 되돌려받는 셈이다. 이 방법을 활용하여, 상대 서버가 회복(Recovery) 할 시간을 줄 수 있다. 만약 아직도 회복하지 못한 상태라면, 재요청하는 시간대를 더 늘리면 된다.&lt;/p&gt;
&lt;h3 id=&quot;환전-서버가-다운되었다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%99%98%EC%A0%84-%EC%84%9C%EB%B2%84%EA%B0%80-%EB%8B%A4%EC%9A%B4%EB%90%98%EC%97%88%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;환전 서버가 다운되었다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;환전 서버가 다운되었다면?&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b43a8e0054d21b250acc11edf03ea3fb/38af3/image-8.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.05521472392638%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB3klEQVR42q2Rz04TYRTFO9NpO9O/0IY2Im0ItqBtkQ5a2qZTSFNtFaJGSIwmBqNGMMERDXZjwoKdD+DKnQ+AgqQiiNhofB5xJZHFz68j0gXqwrA4mXu+c3Jyzx2bzWbjSKGqKpFIhGAwSCwWIxqNoijK/weGQiFKpRKpVIpisUjRMNA0zRIlSfor/hF6xJVlWaaru5fw8ROk9RxjlQvkjTLHYv30J3VK5SqF0Qq98RRdPQnC8YylqZr3z4GSJDOUP0c6X2V5dZ0fe3t83fnG+NQtnr94yffdXQszD+okcuNkpxfRCzX8wfDBWQ4FRvuSROODXJ66zsPHdWbn5hk4naVy8QrmoyfMzS8wXCgzMJglc9YQWg7N498PkQ7f0OnScKoeZHv777oEtyuOA97yKA6X2EB4ZAWHyy2gIdmdyIpLeJ2/tm1tGE+NiBpVksOjnMwY1rcnoVN/usTa201erTYYq11l+p7JazGvNTaYmLxpYeXNOssrDW7PmO3KifQIZ4wap/QS8XRehBbp7hticekZW9tN3r3/wPlL15g162yIufnpC5M37grcYbv5mc2tj9w3F34HSuIePjRvALevA4+/Dbe3A18giNffaWktj8UDndbcglfw1pvq9lmVfwIu3DkU7e0QKAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/b43a8e0054d21b250acc11edf03ea3fb/a6d36/image-8.png&quot;
        srcset=&quot;/static/b43a8e0054d21b250acc11edf03ea3fb/222b7/image-8.png 163w,
/static/b43a8e0054d21b250acc11edf03ea3fb/ff46a/image-8.png 325w,
/static/b43a8e0054d21b250acc11edf03ea3fb/a6d36/image-8.png 650w,
/static/b43a8e0054d21b250acc11edf03ea3fb/38af3/image-8.png 894w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;만약 환전 서버에 장애가 터져서, 환전 지연 메시지를 카프카 메시지 스캐줄러에 발행하지 못하고 죽었다면 어떻게 해야할까?&lt;/p&gt;
&lt;p&gt;이런 경우는 Batch 를 통해 재처리 할 수 있다. 오케스트레이션 SAGA 패턴으로 구현된 환전 서버가 환전 트랜잭션의 마지막 상태를 저장하고 있기 떄문에 (어떤 로컬 트랜잭션까지 실행되었는지 알고 있기 떄문에) 트랜잭션이 중단된 구간부터 다시 재개하면 된다.&lt;/p&gt;
&lt;p&gt;예를들어 출금이 성공하고 멈춰버린 환전의 경우에는, 배치가 출금 취소 메시지를 발행한 후에 환전을 실패 처리할 수 있다.&lt;/p&gt;
&lt;p&gt;지금까지의 과정을 요약하면, 입출금 요청에서 애러가 발생했을 때 기본적으로 입출금 결과를 다시 확인하여 재처리하고, 네트워크 병목으로 인해 확인 자체를 못한다면 환전 지연을 통해 계좌 서버에게 회복할 시간을 준다. 그런데 이러한 환전 지연까지 못했을 경우네는 최종적으로 배치를 통해 환전을 재시작할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;원화-계좌-서버가-출금-취소-메시지를-처리하다-애러가-발생했다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%ED%99%94-%EA%B3%84%EC%A2%8C-%EC%84%9C%EB%B2%84%EA%B0%80-%EC%B6%9C%EA%B8%88-%EC%B7%A8%EC%86%8C-%EB%A9%94%EC%8B%9C%EC%A7%80%EB%A5%BC-%EC%B2%98%EB%A6%AC%ED%95%98%EB%8B%A4-%EC%95%A0%EB%9F%AC%EA%B0%80-%EB%B0%9C%EC%83%9D%ED%96%88%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;원화 계좌 서버가 출금 취소 메시지를 처리하다 애러가 발생했다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원화 계좌 서버가 출금 취소 메시지를 처리하다 애러가 발생했다면?&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b1b6c9db30b8bdecfa21692c58d50143/764be/image-9.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.576687116564415%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB7UlEQVR42q1TS08aYRQdnjIDA2hgYynvtwYScEbMpIYFxTQlITSOmKYY0P6AutJFE+mGtqlaJSz8H3bXNB3A+IfEPlan3/fhGFhUXbg4yZ1775xz7r0zHMdxuA8Gg+EW9/Ynk0nE43FEo1Fks1mEQiFEIhGkUimk02mEw2F4PB48RJihVCqhVquhWq1CVVUUi0XU63X2XKlU0Gg0oCgKE5AkCS6X625Cr9eLRCLBXqBOA4EAi6nzYDCITCaDJK0Tt/l8HnOzs+M1EBiNRlgsFgYa35ByeFTQRU8q6Wr6AZxkxBVlFcqzIvK5JQgmExykRyB13/w8npfLKK+twe/3/9+h2Wxm+3I6RZRfvMT59wt8+3GJ4eAS/pkZJMjYPkL4fvcdrq5/YUTQPmhPE+7svEWn8xEH7Q9ELcAU3W43nvieQn29hc03TbxaV1mvvq+FhUXs7e0zSJI8TahpGn7/+YvR6BqyPC46HA7wPM8gCAJzTnMUoijCarXCRFZAMTHhOIjFYlguFJDL5WCz2ViO522Ql1fw6biHL90zHJ10YSGkgt3O6uvE8U+tD60/QLPZuvvK+lGW5AI6h118/trD8WlvinBD3UB/MMRgeIFWa3uakO5Ftz/5i9G8KLrYtXXnOmgvHd9OBCbGftzv8B8in2AEFCxbywAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/b1b6c9db30b8bdecfa21692c58d50143/a6d36/image-9.png&quot;
        srcset=&quot;/static/b1b6c9db30b8bdecfa21692c58d50143/222b7/image-9.png 163w,
/static/b1b6c9db30b8bdecfa21692c58d50143/ff46a/image-9.png 325w,
/static/b1b6c9db30b8bdecfa21692c58d50143/a6d36/image-9.png 650w,
/static/b1b6c9db30b8bdecfa21692c58d50143/764be/image-9.png 806w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;토스뱅크는 위 애러 처리를 위해 &lt;strong&gt;CDL(Consumer Dead Letter)&lt;/strong&gt; 메시지 브로커를 통해 카프카 메시지의 정합성을 보장하고 있다. 원화 계좌 서버가 애러가 발생하면 CDL 메시지 브로커릃 통해 메시지를 DL 서버로 전송한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c8d91d3dfc2a3f0eae7d6b38f5eb182b/42d54/image-10.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.44171779141104%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABl0lEQVR42p1SS0sCYRQd8T0lPlCwFrrwmc0Yo5iok1PgQhRScJP2d2pTfyJrUQkVPYggqHRsY3+ogjrNd2tKe6C0OBy4891zz71nOKPRCI/HA5fLBbfbTexwOIi9Xi+cTidsNhs4jpsM7HGlUkGtViPU63WUSiXiRqNBNVmW4fP54Pf7YbFYxolyCIVCkCQJoigilUpBEASk02niTCZDtWw2C0VRaBvWYzAYwLYzmUzEI4J/gTVNvOp3QX2iDl2MnWRJWUG5uopcXobZbCKXzFmxWESrta6xMplDq9WKSDSOvc45rroDnJxfaoFNIxgMUnCq2sfj0zNubm+1Qeb3Pt2J3W5Hu72Lu24PG5ubVHNpCbPGhCBCSi8iMS9+3M1MDpPJJApaYJFI5MuILsjzPA4OOxgMHrC9tU01lijPTyEam4O4ICEUDlONDWcci8WRy+UQCATGr6wP8s/MYmf/GGfXKo5OLzRnRnLNWO3f4+UV6PbU4aR/igyny+6YlxWUylUtlMLId0VZxlqzSeH86pA9/Oev8ok3Wccm328IT/UAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/c8d91d3dfc2a3f0eae7d6b38f5eb182b/a6d36/image-10.png&quot;
        srcset=&quot;/static/c8d91d3dfc2a3f0eae7d6b38f5eb182b/222b7/image-10.png 163w,
/static/c8d91d3dfc2a3f0eae7d6b38f5eb182b/ff46a/image-10.png 325w,
/static/c8d91d3dfc2a3f0eae7d6b38f5eb182b/a6d36/image-10.png 650w,
/static/c8d91d3dfc2a3f0eae7d6b38f5eb182b/42d54/image-10.png 858w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;그리고 이 DL 서버는 정해진 재시도 횟수와 간격으로 서비스 메시지 브로커를 다시 전달하여 원화 계좌 서버의 출금 취소를 재시도한다.&lt;/p&gt;
&lt;h2 id=&quot;transaction-mesaging&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#transaction-mesaging&quot; aria-label=&quot;transaction mesaging permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Transaction Mesaging&lt;/h2&gt;
&lt;p&gt;SAGA 패턴에서는 로컬 트랜잭션 커밋과 메시지 발행이 원자적으로 함께 이루어져야한다. 즉, 입금 실패로 인한 환전 실패 처리와 출금 취소 메시지 발행은 항상 같이 이루어져야 한다. 그런데, 서비스 몌시지 브로커 장애 등으로 메시지 발행 자체가 안되면 이를 어떻게 보장할 수 있을까?&lt;/p&gt;
&lt;h3 id=&quot;pdl-producer-dead-letter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pdl-producer-dead-letter&quot; aria-label=&quot;pdl producer dead letter permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PDL (Producer Dead Letter)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/edc0cf621f20d418b4917d5d5de3be2c/27b7a/image-11.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.122699386503065%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABvElEQVR42q1STUsCURR1/ERHndFRFNwJo5IfqWCtKmpbGIJBBQUF/YICo1pkkEQtIijoYxN9mLWrNq1aRUXQql0UURYt3VdwenfAyYhs0+Lw3rvvvXPPPfdqNBoN/hUulwuxWAyRSERZ4/E4wuEwUqmUAorRKssyotEokslkfUKLxQK/349QKIRgMIhAIKB8JlCMSBKJhHL2+XzKPcdx9Uj/uWTKVoVOp1NRq4K3WiG53RAdTuj1ejVOb0RRBNnG85b6Cr1mM2S7HXZGnp+dx9bBEc6vb5DJZKDVamFm9w3M64fHMp5fXrFbLH0nNBgMSKe70dvXj5bWNkjsHHE4YGOE/YPDmMwXsLC4jEbWtOofN1O9tr6Bre0djI6NfZVMG0mSUKlU8Pb+gauLSzhtNrpUS+d5HnpGbjKZYGUW0JlUktpaG74p7OzsQjbbg/b2DvbBqnhDHg0MjWBqZg5LKxtobmpW31PX90r7ODw6Ri43/ruHlJHGhGbS6/FgYrqA1c09nJyeKUk59oaU0gjd3t3jqfyCnd3iT8LaDle7TnEzm1WHU4IgiGqsCppjQRBgNBp/V8jVePfHEP/AJwvaQXJMw8F7AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/edc0cf621f20d418b4917d5d5de3be2c/a6d36/image-11.png&quot;
        srcset=&quot;/static/edc0cf621f20d418b4917d5d5de3be2c/222b7/image-11.png 163w,
/static/edc0cf621f20d418b4917d5d5de3be2c/ff46a/image-11.png 325w,
/static/edc0cf621f20d418b4917d5d5de3be2c/a6d36/image-11.png 650w,
/static/edc0cf621f20d418b4917d5d5de3be2c/27b7a/image-11.png 804w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;트랜잭션 메시징을 보장하는 방법에는 여러가지가 있지만, 토스뱅크는 그 중에 PDL 기법을 택하고 있다. 환전 서버가 서비스 메시지 브로커의 장애로 메시지 발행에 실패했을 경우에는 Producer 메시지 브로커로 메시지를 대신 발행하여 DL 서버로 메시지를 전달한다. 그리고 이 DL 서버는 일정 시간이 지난 후 회복된 서비스 메시지 브로커로 메시지를 다시 전달하여 원화계좌 컨슈머가 가져갈 수 있도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;더-학습해볼-주제-및-궁금점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%EC%A3%BC%EC%A0%9C-%EB%B0%8F-%EA%B6%81%EA%B8%88%EC%A0%90&quot; aria-label=&quot;더 학습해볼 주제 및 궁금점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 주제 및 궁금점&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Kafka Message Scheduler 를 통해 지연 시키는 방법과, 지난 Exponential 지연 방식과 어떤 연관이 있을까?&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Next 코어뱅킹, MSA와 MySQL로 여는 평생 무료 환전 시대]]></title><description><![CDATA[왜 모든 은행은 Oracle 만 사용하고 있을까? 기존 토스뱅크의 코어뱅킹 시스템을 아울러, 대부분의 은행은 Oracle…]]></description><link>https://haon.site/article/toss-slash/next-core-banking/</link><guid isPermaLink="false">https://haon.site/article/toss-slash/next-core-banking/</guid><pubDate>Thu, 02 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;왜-모든-은행은-oracle-만-사용하고-있을까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-%EB%AA%A8%EB%93%A0-%EC%9D%80%ED%96%89%EC%9D%80-oracle-%EB%A7%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B3%A0-%EC%9E%88%EC%9D%84%EA%B9%8C&quot; aria-label=&quot;왜 모든 은행은 oracle 만 사용하고 있을까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 모든 은행은 Oracle 만 사용하고 있을까?&lt;/h2&gt;
&lt;p&gt;기존 토스뱅크의 코어뱅킹 시스템을 아울러, 대부분의 은행은 Oracle 데이터베이스를 사용하고 있다. 어떤 장점에서 오라클 데이터베이스를 사용하고 있었을까? &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; &lt;strong&gt;신뢰성과 안정성이 뛰어나다.&lt;/strong&gt; 트랜잭션 관리, 고가용성, 복구 기능, 데이터 무결성 보장등의 측면에서 좋다. &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; &lt;strong&gt;확장성이 좋다.&lt;/strong&gt; 매우 큰 데이터 볼륨을 처리할 수 있는 뛰어난 확장성을 갖는다. &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; &lt;strong&gt;성능도 좋다.&lt;/strong&gt; 복잡한 트랜잭션 처리와 대규모 데이터베이스 쿼리에 효과적이다. &lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; &lt;strong&gt;오랜기간 축적된 노하우가 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;반면 오라클 데이터베이스는 치명적인 단점이 있다. 바로 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; Scale Out 이 제한적이라는 점이여서, 유연한 대처가 어렵고 SPOF 가 발생한다. 또한 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; MySQL 대비 비싼 라이센스 비용을 갖는다. 오라클의 경우 CPU 코어수에 비례하여 비용이 증가한다. (이 때문에 토스뱅크는 SPOF 를 해결하고자 각 마이크로 서버에게 서로 다른 Oracle 데이터베이스를 바라보게 하는 전략을 고려했지만, CPU 코어 수에 비례하는 비용 증가 문제로 인해 택하지 않았다.)&lt;/p&gt;
&lt;p&gt;이 떄문에 MySQL 을 도입했다. MySQL 은 Oracle 대비 확장성에 유리하다. 레플리케이션과 같은 전략을 통해 유연한 Scale Out 이 가능하고, 비용도 Oracle 대비 저렴하다. 또한 MySQL 은 MVCC 기술을 사용하기 떄문에 동시성 제어에도 유리하다. 이 외에도 Oracle 을 사용할 이유가 점점 옅어졌다. IT 기술이 발전함에 따라 트랜잭션 관리 방법이 발전하였다. 기존에는 데이터베이스에서 트랜잭션을 직접 제어해야 했지만, Redis 분산락, Kafka 를 활용한 SAGA 패턴등의 기술을 도입하여 애플리케이션 Layer 에서도 유용하게 트랜잭션 관리를 가능케 했다.&lt;/p&gt;
&lt;h2 id=&quot;msa-환경을-전환중인-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#msa-%ED%99%98%EA%B2%BD%EC%9D%84-%EC%A0%84%ED%99%98%EC%A4%91%EC%9D%B8-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;msa 환경을 전환중인 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MSA 환경을 전환중인 아키텍처&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/016d4bfbcf3bb0eda39746ba111ae6b5/c3fd4/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.21472392638037%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACtklEQVR42mWSW0iTYRjHd3Dq3Kdz7vBt+3berHRpeZhQOo+TOcRZTVKmQVEQHW9KioTuqpuQoiyKotIOFF1EVEaikooYFEF0UXSRdZE3QQRddPfr3UxCunh4P96X7/f/P8/zV0mShKIoeDwe3G43sixTVlZGOBzO3judToLBIOXl5Xi9XiwWCz6fj1AohNVqRaVSra4MKBqNkkwmaWtro6mpiVQqRU9PDy0tLcRiMTo7O+nv7yeRSNDQECUej1NRUZE1kIGo1ep/wIyi3+/H7XLhsMv4fV5sQjlzn/khEAjgEm8+rwfF6RDfClab7X9n/0ooaLRoco3kF8ro9Ga0udIqVV1+IQXFDvTFdorFaS8Ro5C9KDYPJrMbSVSRxYM2J3cZqM3JJ5Xey+DQKY4OnaGyNpoFaYRQ5iwyC/ehCM7SCBWVUarXb2b9uggb19VRurYOqyuM7NuILk/661Ct4+KVGzyfmGJ2/hWp3h3iQY1er88CJZMDOVCNEqpBWRPBHKpFro5hq2nH39CFvSaGfUOz6MSwAszh3Mg1pmbmmFt4TXJbXxaUmWOhZMCqBGnbspPkwEESXf2E0kdQpr9Teu8N/ofv8Y9/wXn7NTrJtDJDHRcuXWdy6iXzArilJy1ENJhMJhGPIKXhWuo70gzsO0Zroo/IrkHOf/nFyOefXPv6i5tLvzk+9Q6VwbwCzOHEydNcvX6L0TsPaO/oRqvVYhPbNBqLMFrdJNP7efjgMQf2D+LZfpjhTz+Y+bjI2IdvzC4u0ffkrQD+dagSLZ89d5mn4y+YmJ6ha2vv8nZzs1vDaHFR1dhNfXw7kU1x7F27ab6/wKHRZ3SPTbLn7gSB4UdoDMYVoIbKqjoamlppbInhUDyrApsZdoHRTp5kJr/IisEo4lXiQmUWURGRUYvIFJQ4RVc6/gBx42XzrUmjKwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/016d4bfbcf3bb0eda39746ba111ae6b5/a6d36/image.png&quot;
        srcset=&quot;/static/016d4bfbcf3bb0eda39746ba111ae6b5/222b7/image.png 163w,
/static/016d4bfbcf3bb0eda39746ba111ae6b5/ff46a/image.png 325w,
/static/016d4bfbcf3bb0eda39746ba111ae6b5/a6d36/image.png 650w,
/static/016d4bfbcf3bb0eda39746ba111ae6b5/c3fd4/image.png 854w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;토스뱅크는 기존의 레거시한 모놀리식 코어뱅킹 서버(원화 예금 서버) 에서 기능을 마이크로 서버로 조금씩 때내고 있다. 대표적으로 지금 이자 받기, 송금, 원장 관리와 같은 도메인들을 새롭게 MSA 로 구현중에 있다. (몰론 데이터베이스는 오라클 DB 를 바라보고 있다.)&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/25e003cf6728802d2d35a35b1ef76a29/914ae/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.50920245398773%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACzklEQVR42q2SSWhTYRSFM9RU0yYvyUvyMvWlTdKYDkmdYkNja4qlaqDQCtqqoILFiiIW0WopjlVBKs6IC6sIVnRhnao4i1VERHFCBF2IoOJGV6Ib4fPviyLSrYvD/Tn3/ueey706nU7Hf4UkSYTDYcrLywmFQqiqSjKZpLq6mkgkouXi8TipVIpYLEYgEKCqqkqr93g8moher/8rGI1GqaurI5vN0tTURE1NDS0tLTQ3twh+OvX19eLdrHENDQ2k02lNbOSf0+kc7dBms4lOCn6fj5KSYlEk41EUXCLKsgO73Y7b7dbekmTFKcv/OhoFkTTbvBQ4ApjtPowmmyDNGMdIGid7wxSFKwmEKpBcQYxmJ4VyETalBKtTzUHwhXZvrpHBmIcrGMcTnoxSMpGN2/oYOD3I9l0HcBTF8ZcmUWMpyibPoHZWK+nGuVRMbRT1UwhVZQiWTxOxXvxNjLhDpzcYkf0xXEJMFli0oovePUdo7+zBHpyAOzQJi7uUTLaNuw8e8+jpS9Zv6dME29rXsXxtLws7ukXTZG5kg96AQ60ksn+IxKU3FO+9SKBvEHX/ZRJDb4kevobVV0Zmdis3hx9y7+ETNmzdTXRCho41vZw7e5XO7j4C438L5om5x6oJum8+Y+gLDHz4zpnPPzj98Rvnv/5k8/3XmHyVZGbO5cqNYW4L0TU9O4mnZtK2tIulqzYJp10EY1NzgsaRpRRVsuDMHU6+esaJly849uI9/c/fcfz1J5ZcfES+t4zaxjlcuzXMneEHrO3Zga90Css6NzNwcpCV67bjj0zMCY5sxuEvwy3GXDG4hdWntrKo/wqLj16g9cR1XPsuI3ljTErPYs+hoxw8cpzWJSuR1Ti1s+fTNK+d6dkFeEOJP4IGCmwKFnEGJk+MMQImb0S4ysGqFFPo8FMgTirf4ibfqjBO8mKRc2dmsigi+jFLbm3LvwDt+JKdYYl6CgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/25e003cf6728802d2d35a35b1ef76a29/a6d36/image-1.png&quot;
        srcset=&quot;/static/25e003cf6728802d2d35a35b1ef76a29/222b7/image-1.png 163w,
/static/25e003cf6728802d2d35a35b1ef76a29/ff46a/image-1.png 325w,
/static/25e003cf6728802d2d35a35b1ef76a29/a6d36/image-1.png 650w,
/static/25e003cf6728802d2d35a35b1ef76a29/914ae/image-1.png 860w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;그리고 외화 예금 서버를 MySQL 과 마이크로 서버로 새롭게 다자인했다. (위 그림에서 왼쪽 부분이 외화 예금 서버이고, 오른쪽은 원화 예금 서버이다.) 전반 오버뷰는 위와 같은데, 지금부터 외부 예금 서버에서 트랜잭션을 어떻게 처리하고 있는지 아키텍처를 더 자세히 뜯어보자.&lt;/p&gt;
&lt;h2 id=&quot;외화예금-트랜잭션-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%B8%ED%99%94%EC%98%88%EA%B8%88-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;외화예금 트랜잭션 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;외화예금 트랜잭션 아키텍처&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5c97e0f1493f2324b283edd9fb7f2d20/38af3/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.239263803680984%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABPUlEQVR42q2S3UvCUBjGdzYHMlvSXHNbbpY6NfxYkaxgamkU5F3QRRDWfxJd1XX9w782k1gSStDFw3mf857363mPJMsyuq5jWRau62IYBrZt4zjOwk7PlCuKgiRJm6GqKq12m4vJhNlsRhRFTKdTRqMRYRgSxzHj8RhN0xYBQohNSSX+FULJodkVthwPtaAjkstUBjnp5BsJF8uAbIdi6VtJqKBuG+QSKHntzx2lhZSfMkjU/DJVt4RRrnJ+fUux5OD6AdObO0zbZ6/i0TsZUnIOMHdNwsEQy2tSD1rM5080ak1qnYi8piPJisogWYS5U6CfPHx+fWc/6NI9PuPl7YN6q0+9EXB0epkkbOB5HoP4Css/pN3pcf/wiOMdEPSH5AvFZOSsDmLdeGKJLJd+41/irvsOWf+qvVhYJvYTE7/Req5KGLgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/5c97e0f1493f2324b283edd9fb7f2d20/a6d36/image-2.png&quot;
        srcset=&quot;/static/5c97e0f1493f2324b283edd9fb7f2d20/222b7/image-2.png 163w,
/static/5c97e0f1493f2324b283edd9fb7f2d20/ff46a/image-2.png 325w,
/static/5c97e0f1493f2324b283edd9fb7f2d20/a6d36/image-2.png 650w,
/static/5c97e0f1493f2324b283edd9fb7f2d20/38af3/image-2.png 894w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;외화 예금 서버에서 사용자에게 빠른 환전 경험을 제공하기 위해 트랜잭션들이 전반적으로 느슨하게 연결되도록 설게했다. 단, 고객의 잔액 관리 부분(외환 서버, 수신 외화예금 서버) 에서 만큼은 서버들간의 트랜잭션을 엄격하게 관리했다. 하지만 이 외의 고객과 관련 없이 은행 내부적으로 회계 처리를 위한 서버들(손익 처리, 회계 처리, 계정계 서버) 간의 트랜잭션을 카프카를 활용하여 비동기적으로 처리했다.&lt;/p&gt;
&lt;h3 id=&quot;금원-거래환전-플로우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%88%EC%9B%90-%EA%B1%B0%EB%9E%98%ED%99%98%EC%A0%84-%ED%94%8C%EB%A1%9C%EC%9A%B0&quot; aria-label=&quot;금원 거래환전 플로우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;금원 거래(환전) 플로우&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f3a8f7e3bf59a0dbb274ab46ac4ee66b/a2b88/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.282208588957054%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB10lEQVR42q2Ty27TQBSGfZ1x7NhR7NixTWKlSZMmjmuJcikEl5KFkSsKYoGsSmXJomqFYBEhNog1rBArHgIhAeIFEA/AAiGegQ2P8DMdt7BBooguRucfnTPfnMuMIAgCTnVZloUoijCZTNDtdrmO45jbXq+HJEngui4PFkXx78BDUJ7nKMsSRbGF7e2byLKMgVaRpilmsyscfmJgEATwPA++7yPw2+h0zkCSpP8puxKKqoEaDohhQzddNJttOE4AVSEss38AUqqhVqMwGh7C0UW0owStteuYvnyP9MVb0O6IB1JKGVzhmqgKFFn+BaFEZX5StcTQa1gZLsFsBti8sct6VmBa3MXi6w88+vwdcrIOkx0YTs+h5YUghGKwksJxQ9i2g07bR3/ABtofV8DxJMbG+hgtfwlPn7/C4skzDC/n6N/Zx6i8D8uPMJ/nuDS/DTfsswszXNsq0WL64eIx9g8eYDm+gOHqrMrYtGwuDkte27iF6dmr6BQ7yD58w+a7LyDLKfdrpg2FaJU2GlBJjWcksgFqugmqW0ctOHoKWt1GI4xZ6RGa5wvce/MJe68/Qh5UQOHkk6+AkqyAaHW2DKhUh0jrENleko6bL+I49rcV/uA75a/3E4zzJ/qjWjEGAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/f3a8f7e3bf59a0dbb274ab46ac4ee66b/a6d36/image-3.png&quot;
        srcset=&quot;/static/f3a8f7e3bf59a0dbb274ab46ac4ee66b/222b7/image-3.png 163w,
/static/f3a8f7e3bf59a0dbb274ab46ac4ee66b/ff46a/image-3.png 325w,
/static/f3a8f7e3bf59a0dbb274ab46ac4ee66b/a6d36/image-3.png 650w,
/static/f3a8f7e3bf59a0dbb274ab46ac4ee66b/a2b88/image-3.png 908w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이런 아키텍처 위에서 금원 거래가 어떻게 이루어지는지 플로우를 알아보자. (즉, 사용자의 원화가 외화로 어떻게 환전되며, 은행의 총 계장 원장 금액이 어떻게 바뀌는지 플로우를 알아보자.)&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 가장 먼저 고객이 환전을 요청할 것이다. &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 그 요청은 외환 서버로 도착하고, 외환 서버는 어떤 종류의 환전인지 확인하고 환율을 확정한다. &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 이어서 수신 원화예금 서버를 호출하여 원화 계좌에서 출금하고, &lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 수신 외화예금 서버를 호출하여 외화 계좌에 돈을 입금한다.&lt;/p&gt;
&lt;p&gt;여기까지 되었다면 고객 계좌의 잔액 변동은 끝이난다. 빠른 환전 경험을 제공하기 위해 여기까지만 서버들간의 트랜잭션을 SAGA 패턴으로 묶어서 안전하게 관리하고 있다.&lt;/p&gt;
&lt;p&gt;이후 은행 계좌를 처리하는 부분은 카프카를 이용해서 트랜잭션을 분리시켰다. &lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; 외환 거래였기 때문에 손익에 대한 처리가 필요하다. 따라서 손익을 위한 카프카에 메시지를 발행하면 손익 서버가 그를 컨슘하여 환율에 따른 손익을 결정한다. &lt;code class=&quot;language-text&quot;&gt;(6)&lt;/code&gt; 이후 은행 회계 처리를 위해 회계 처리 서버가 카프카로 또 컨슘한다. (이 과정에서 회계 처리 서버는 은행의 어떤 계좌에 얼마를 기록할지를 오라클에 기록해둔다.) &lt;code class=&quot;language-text&quot;&gt;(7)&lt;/code&gt; 그렇게 오라클에 기록된 정보를 레기시 코어뱅킹의 Deferred 라는 존재가 주기적으로 조회해서, 은행 계좌 총 원장에 잔액을 변동시킨다.&lt;/p&gt;
&lt;h3 id=&quot;만약-기존-레거시-코어뱅킹으로-환전-기능을-구현했다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%8C%EC%95%BD-%EA%B8%B0%EC%A1%B4-%EB%A0%88%EA%B1%B0%EC%8B%9C-%EC%BD%94%EC%96%B4%EB%B1%85%ED%82%B9%EC%9C%BC%EB%A1%9C-%ED%99%98%EC%A0%84-%EA%B8%B0%EB%8A%A5%EC%9D%84-%EA%B5%AC%ED%98%84%ED%96%88%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;만약 기존 레거시 코어뱅킹으로 환전 기능을 구현했다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;만약 기존 레거시 코어뱅킹으로 환전 기능을 구현했다면?&lt;/h3&gt;
&lt;p&gt;몰론 위 과정들을 기존 레거시 코어뱅킹에서 하나의 로컬 트랜잭션으로 모두 처리할 수는 있었을 것이다. 하지만, 복잡한 은행 비즈니스 로직간의 의존성이 더욱 높아지게 되고, 고객과 관련없는 회계처리 부분도 하나의 트랜잭션으로 함께 묶여서 처리된다. 즉, 모든 것들이 하나의 로컬 트랜잭션으로 묶어 동기적으로 처리되므로 환전 속도가 느려진다.&lt;/p&gt;
&lt;h2 id=&quot;외화예금-api&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%B8%ED%99%94%EC%98%88%EA%B8%88-api&quot; aria-label=&quot;외화예금 api permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;외화예금 API&lt;/h2&gt;
&lt;h3 id=&quot;동시성-처리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%B2%98%EB%A6%AC&quot; aria-label=&quot;동시성 처리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동시성 처리&lt;/h3&gt;
&lt;p&gt;환전 기능에서 외화예금 API 가 있을텐데, 그 중에 입금 API 에는 동시성 처리가 필요했다. (원화 계좌에서 출금된 돈이 외화 계좌로 &quot;입금&quot; 될 때 동시성 처리) 환전을 하는 동시에 똑같은 계좌로 카드 결제가 들어올 수 있었기 때문이다. 즉, 환전을 하는 동시에 얼마든지 타 요소로 인해 잔액 변동이 일어날 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f0a6a3a85516cd861424b2ec7e607c87/d5b59/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.16564417177914%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAABlElEQVR42q1TXUvCUBieZxtzfq7YJq5NzEUyTXR+XKmIjILCCwsqFAq6SKF+RXQVXUc/oquCIPCuftjTzhENzI8ILx7Oed6H85znfc/GcRyHWQQCAYZ52krQg6IoIhgMIhQKMVAuSdK0NtnT+kpDTdNQLBbR7XYxGAwwHA7RarXQ6XTQbrfR7/eZRnkymZx2sNBQURSUy2UUCgU0m014nscMa7UaXNdFqVRiFzqOA9u2wfP88oSyLMM0Tei6zmAYBlRVhWVZSKVSLBWt05VqPC+smvEPmcxu2awIGZuRRYZMJIQRmmQ36yCdyfqHOMgxFXa+ip1cxd/rIIKIeML2eRUxbRu8KC1OOGkhbWoYXBxAEnkYaQfHvWucX97AtPcQjiqoNI7Qu7qFWz9EJLbpdyP8NpyYxTd0nJz14DXyjNfqHl5eR3j7+MR+59RPKOHu4Qnvoy/cPz4jTA0FYV7CsWE0pkBNbE1FOaLA3M7ByuQQjmv+xQSakUHKzrGV8OLyR5lt/59Y/svN1sac/O2zWRPWa/gNlItHbUnIaMsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/f0a6a3a85516cd861424b2ec7e607c87/a6d36/image-4.png&quot;
        srcset=&quot;/static/f0a6a3a85516cd861424b2ec7e607c87/222b7/image-4.png 163w,
/static/f0a6a3a85516cd861424b2ec7e607c87/ff46a/image-4.png 325w,
/static/f0a6a3a85516cd861424b2ec7e607c87/a6d36/image-4.png 650w,
/static/f0a6a3a85516cd861424b2ec7e607c87/e548f/image-4.png 975w,
/static/f0a6a3a85516cd861424b2ec7e607c87/3c492/image-4.png 1300w,
/static/f0a6a3a85516cd861424b2ec7e607c87/d5b59/image-4.png 1370w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;동시성 처리를 위해 애플리케이션 계층에서는 Redis 분산 락을 사용했으며, 실제로 데이터가 수정되는 MySQL 데이터베이스에는 비관적 락을 걸었다. 이때 분산 락은 당연히 DB 보다 빠른 응답 속도를 기대할 수 있기 때문에 다른 트랜잭션에서 접근하더라도 Fail Fast 를 유도할 수 있었기 때문이다.&lt;/p&gt;
&lt;h3 id=&quot;코루틴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BD%94%EB%A3%A8%ED%8B%B4&quot; aria-label=&quot;코루틴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;코루틴&lt;/h3&gt;
&lt;p&gt;그 다음으로는 거래가 가능한지 검증하는 단계이다. 예를들어 계좌를 확인하여 계좌가 해지된 상태는 아닌지, 명의도용된 계좌는 아닌지 등을 검증하는 것이다. 여기서는 레거시 코어뱅킹처럼 하나씩 동기적으로 검증하기엔 검증 요소들이 너무 많아서 느리다. 이를 해결하기 위해 코루틴을 도입하였고, 동시간대에 여러 검증을 수행하도록 했다.&lt;/p&gt;
&lt;h3 id=&quot;그-이후-플로우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B7%B8-%EC%9D%B4%ED%9B%84-%ED%94%8C%EB%A1%9C%EC%9A%B0&quot; aria-label=&quot;그 이후 플로우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그 이후 플로우&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bf51af8f4c7bd9272be4abbe5e8b65eb/6da96/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.601226993865026%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACDklEQVR42qWS7UuTYRTGt+dl25Ph0M10TDfbi3u2Z7lNy7UZNTalMY1aW4QmfbKvYkRGWmCL6IWl9aUSSYug/yCK6EP/gvQH/bqfp5yuEQR9uDicw7muc51z3zabzYYsyzidTguqqlpwuVxWrigKZs8/wyQnEgkqlQr1ep1CoUA+n6dcLjM9PU04HP4rWZIk7HZ7e90smiTDMEgmk0SjUXRdR4/H0WMxvF6v1bhPNGOHSDsOprlcmrV+S2BfxHQicJjoEOcoTp3ndO6MyO2dgqbAcHCIpBFDdXaRyRaJp3JIDo2wcQo9NYmry002O0EulyeVGefL1+9sbb/707EQ+51kxjKcy+l09/TzeGOblXtPcHsGWLq9zqPmFv5AhEajwYvNl1y8VOPT52+83f2ALB5OOtjA1lqrp/eYELcjyQqB4yNCICwaFXyDIUJRA0V1tJz09np4s73L6v31zhvaJZn+QIyhyAkGgjrBkRQ+ET2+EMMjadxev7inJJz30dXtsdxo4lvJYnAqnWZ5+SYzM7O/BM11VafG3OItnr96T/nKIg+evWZ57SmXry+xufWR4uxVevr8rD3coDZ/AyM5ys7OLisrd5i/tsDe3g+azeZhhwpjuRKV6hzx9CSlSo18cZbRiQIXagtEEmM4taOcLVVIprNo2hEG/YO43W7Gx09yd3WNarXafsP/weFX/gn2yS5ykaYfRgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/bf51af8f4c7bd9272be4abbe5e8b65eb/a6d36/image-5.png&quot;
        srcset=&quot;/static/bf51af8f4c7bd9272be4abbe5e8b65eb/222b7/image-5.png 163w,
/static/bf51af8f4c7bd9272be4abbe5e8b65eb/ff46a/image-5.png 325w,
/static/bf51af8f4c7bd9272be4abbe5e8b65eb/a6d36/image-5.png 650w,
/static/bf51af8f4c7bd9272be4abbe5e8b65eb/6da96/image-5.png 922w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;거래 가능한 계좌라는 것이 확인되었다면, 거래 내역을 MySQL 에 저장하고, 계좌의 잔액을 수정하고, DB 커밋을 한다. 그 이후로는 은행 계좌 및 회계 처리를 위한 카프카를 발행하고 있다.&lt;/p&gt;
&lt;h2 id=&quot;24시간-365일-무중단-환전-서비스-구현-방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#24%EC%8B%9C%EA%B0%84-365%EC%9D%BC-%EB%AC%B4%EC%A4%91%EB%8B%A8-%ED%99%98%EC%A0%84-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B5%AC%ED%98%84-%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;24시간 365일 무중단 환전 서비스 구현 방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;24시간, 365일 무중단 환전 서비스 구현 방법&lt;/h2&gt;
&lt;p&gt;기존 은행 시스템은 매일 자정 근방에 거래가 불가능하다. 이는 은행의 회계 시스템 때문이다. 이 회계 시스템과 연관된 것이 바로 수신 외화예금 마이크로 서버에서 잔액대사 기능이다.&lt;/p&gt;
&lt;p&gt;잔액대사란 무엇일까? 앞서 설명했듯이, 금원거래가 발생하면 고객 계좌와 은행 계좌가 바뀐다고 했었다. 그것이 매일 자정마다 잘 처리되었는지 검증하는 행위이다. &lt;strong&gt;즉, 매일 자정을 기준으로 모든 고객 계좌의 잔액의 총 합이 은행의 총 계좌의 잔액의 합과 동일한지 검증한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;일반 은행에서 정확한 전일자 잔액을 계산하기 위해선, 자정 근방에 모든 사용자들의 거래를 막고 모든 고객 계좌 잔액의 총합을 계산하면 된다. 00시 00분 근방에 거래가 없었기 떄문에 정확한 잔액을 산출할 수 있다. 하지만, 고객들이 거래할 수 없다는 치명적인 단점이 존재한다.&lt;/p&gt;
&lt;h3 id=&quot;새로운-방안1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%88%EB%A1%9C%EC%9A%B4-%EB%B0%A9%EC%95%881&quot; aria-label=&quot;새로운 방안1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;새로운 방안1&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e4a7f128d6d818fc57e681595792c1d5/9cab2/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.89570552147239%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB4UlEQVR42q1Ta6vTQBDNe5tXkzQl7W3aNE3SJhX6xBYpVpTWttzrAyyIX/1wFQT/geBPP85u9aJcBIX7YZjZZebMnDO7kiRJeFBrt9soigKLxQJVVWEymWC1WmE+n4uY+zzPMRqNsF6vsd1u4TiOKJZl+T4gL97tdjifzzgcDjidTjgejyLe7/coyxLdbhdxHAvQ2WwG27YvxQR4D7Rer8PzPPi+j0ajAdd1RQG/52dFUf6PMu/MqQ6HQ0FpuVwK+r1eT0zHKWdZJpIVVoNJjaJOB8wPYXgBWhQbtgPZYJB583QwuAOaTKd4TBJsNhtU47HQlmschqGgp6oqVAKukXdaMdyrPkxdF3eqql0A260IFWkzrkoU2QAVTbWYz5D2k4s+PzWS6w3o77/A+PAV6puPsG6/wfr0XcTm+TOMd7eQbBeSFTQRJhn8JEdAPogThP0hvDiFyswLGDfLgfb0muwG6uoF9GevoW9fQXvyEuz5WxjbG8g1C5JeD+DEGcyIKCQF3LSE3c1Io+YfYutBBOZ4MEk3RjV/XQrnrQj+KhRNg0KaKL/0+C0xTvp4NJ2hedWBpOnoJglyWmTST5HS0nLSO4oinvtvz0GnRrUag0YL4WfDMMAYu/PcNBpIeuiv9wN6SSKS8jV4FgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e4a7f128d6d818fc57e681595792c1d5/a6d36/image-6.png&quot;
        srcset=&quot;/static/e4a7f128d6d818fc57e681595792c1d5/222b7/image-6.png 163w,
/static/e4a7f128d6d818fc57e681595792c1d5/ff46a/image-6.png 325w,
/static/e4a7f128d6d818fc57e681595792c1d5/a6d36/image-6.png 650w,
/static/e4a7f128d6d818fc57e681595792c1d5/9cab2/image-6.png 864w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이를 위해 토스뱅크는 새로운 방안을 찾았다. &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 00시 00분에 전일자 스냅샷 테이블을 만든다. 몰론, 이때도 거래가 계속 이루어질 것 이므로 정확한 계산 값이 도출되지 않을 것이다. &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 그래서 00분 이후의 각 사용자의 첫번째 거래 내역을 확인하고, 가져온다. &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 이렇게되면 잔액과 거래내역 금액을 게 되었으므로, 잔액을 역산하여 전일자 스냅샷 테이블을 보정하면 된다.&lt;/p&gt;
&lt;h3 id=&quot;새로운-방안2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%88%EB%A1%9C%EC%9A%B4-%EB%B0%A9%EC%95%882&quot; aria-label=&quot;새로운 방안2 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;새로운 방안2&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/023e9294ab67b0a77cfda8aff5cdb542/b04e4/image-7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.03067484662577%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB9UlEQVR42qWTS4/aMBSFE8dxHpCQhAAVJIxIIBCoaJF4QyUkkJBmw0+Y1UiV2lWldtVFF+2u//nUdkppGWY2XXy6sXNz7HPvjaIoCgSqqkrO62teenfF5QNCyD/xGrF/5llBTdOQZRnW6zX2+72Mx+MRh8NBslqtsNvtZM52u8XpdMJkMnnp1goGgwHG4zGm0ykWiwVGoxHyPJcfCiFd1yWmacK2bTDGnr+hOEUklUolieu6cBxHPpfLZYkQ+tumrlNQfoBCNCkuXP4RFIlpmmKz2cgbClvC8mw2w3A4xHK5xHw+h+d5UkCjOgI/gFdvwHoVI+RRHE74PqG0sNxoNPCGW87zAfJ+H90kQdbrcbqIoqiol8CvQWv3oNQjsP5buO/uQZMhSL0FGiVQTbsQrARV3KVdNOMYUZKieddBzGOc9MC4XUUpiq9t72F++gn2+BX6w2cYH3+APXyB9f4b7A/fQVqdQlB3A5SiVOKkr7mVNt/zYQR1aMw4jwOIYYHovGY8iiYxUcdbXRb+DacC0wthcCFqmE8SqWGglvQRdjIYtSYcz0eLl0OjDI6roxIGPMe+DPaN9l/ga4PpyDNeV95AueYHiPEK/CoXJKi1Y1iV6llQfSJwjRiNoFqFHwRQ+WRQ3tEwDGFZ1u1f739Q1d9/jariF89SMRb1Z0qKAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/023e9294ab67b0a77cfda8aff5cdb542/a6d36/image-7.png&quot;
        srcset=&quot;/static/023e9294ab67b0a77cfda8aff5cdb542/222b7/image-7.png 163w,
/static/023e9294ab67b0a77cfda8aff5cdb542/ff46a/image-7.png 325w,
/static/023e9294ab67b0a77cfda8aff5cdb542/a6d36/image-7.png 650w,
/static/023e9294ab67b0a77cfda8aff5cdb542/b04e4/image-7.png 888w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이 보다 외화예금 기능을 개발하면서 더 좋은 방안을 찾았다. 이 방법은 데이터 보정도 없이 데이터 구조를 바꿔서 새롭게 설계한 방법이다.&lt;/p&gt;
&lt;p&gt;첫번째로 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 일자별 &amp;#x26; 계좌별을 PK 구분자로 하여 잔액만 기록해두는 테이블을 만든다. 그리고, 금원 거래가 발생할 때 마다 일자별을 기준으로 그 계좌에 잔액을 기록해둔다. &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 그리고 전일자 스냅샷 테이블을 만들 때, 앞서 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 엣허 만들어 놓은 테이블에서 일자별을 기준으로 모든 계좌의 잔액을 가져온다.&lt;/p&gt;
&lt;h2 id=&quot;100-테스트-자동화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#100-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9E%90%EB%8F%99%ED%99%94&quot; aria-label=&quot;100 테스트 자동화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;100% 테스트 자동화&lt;/h2&gt;
&lt;p&gt;5명의 팀원들이 어떻게 6개월이라는 짧은 기간만이 외화 예금을 출시할 수 있었을까? 이 비결은 100% 테스트 자동화에 있다. 외화 예금을 만드는 당시 총 3가지 방법으로 테스트를 자동화했다.&lt;/p&gt;
&lt;h3 id=&quot;로컬-환경에서-테스트--단위-테스트-작성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EC%BB%AC-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8--%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9E%91%EC%84%B1&quot; aria-label=&quot;로컬 환경에서 테스트  단위 테스트 작성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로컬 환경에서 테스트 : 단위 테스트 작성&lt;/h3&gt;
&lt;p&gt;로컬에서는 우리가 흔히 알고있는 단위 테스트를 작성했다. 테스트가 단 1개라도 실패하면 빌드가 실패된다.&lt;/p&gt;
&lt;h3 id=&quot;dev-환경에서-테스트--e2e-테스트-활용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dev-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8--e2e-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%99%9C%EC%9A%A9&quot; aria-label=&quot;dev 환경에서 테스트  e2e 테스트 활용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dev 환경에서 테스트 : E2E 테스트 활용&lt;/h3&gt;
&lt;p&gt;그리고 Dev 서버에서는 E2E 서버를 활용하여 업무의 정합성을 검증했다. E2E 서버란 실제로 배포 서버의 타깃이 되는 API 서버를 Dev 환경에서 호출할 수 있도록 코드를 작성할 수 있는 공간이다. 토스뱅크는 E2E 서버에 고객의 외화예금을 거래하는 것 처럼 고객의 시나리오를 코드화하여 작성했다.&lt;/p&gt;
&lt;p&gt;코드 수정 사항이 발생할 때 마다 Trigger 되어, E2E 서버가 자동으로 수신 외화 예금 서버에 API 를 호출할 수 있도록 해놓았다.&lt;/p&gt;
&lt;h3 id=&quot;live-환경에서-테스트--이상거래탐지-서비스-개발-데이터-완결성-검증&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#live-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8--%EC%9D%B4%EC%83%81%EA%B1%B0%EB%9E%98%ED%83%90%EC%A7%80-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B0%9C%EB%B0%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%99%84%EA%B2%B0%EC%84%B1-%EA%B2%80%EC%A6%9D&quot; aria-label=&quot;live 환경에서 테스트  이상거래탐지 서비스 개발 데이터 완결성 검증 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Live 환경에서 테스트 : 이상거래탐지 서비스 개발 (데이터 완결성 검증)&lt;/h3&gt;
&lt;p&gt;마지막으로 운영 환경에서는 이상 거래 탐지 서비스를 개발하여 데이터 완결성까지 검증했다.&lt;/p&gt;
&lt;p&gt;이상거래탐지 서비스란, 운영 환경에서 발생하면 안되는 데이터가 발생했는지 한 번더 검증하는 서비스이다. 이 서비스는 데이터의 중요도에 따라 주기적으로 돌면서 알림을 주도록 설정해놓았다.&lt;/p&gt;
&lt;p&gt;예를들어 해지된 계좌에서 거래가 발생한 건은 없는지, 하루에 이자를 2번받은 잘못된 계좌는 없는지등 처럼 발생하면 안되는 데이터가 발생했는지를 검증한다.&lt;/p&gt;
&lt;h2 id=&quot;전환-성과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%ED%99%98-%EC%84%B1%EA%B3%BC&quot; aria-label=&quot;전환 성과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전환 성과&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;금융권 최초로 MySQL 을 도입, MSA 기반의 안정적인 코어뱅킹 시스템을 구축하였음 : 덕분에 1910만 MAU를 자랑하는 토스 홈에서의 3000TPS 트래픽을 안정적으로 소화 중이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;세상에서 가장 빠른 환전 고객 경험 제공 : 원화 송금 속도인 280ms 보다도 훨씬 빠른 속도인 외화 환전 속도를 30ms 로 빠르게 구현. MySQL 뿐만 아니라 Redis 기반 캐싱, 코루틴 등의 신기술을 활용하여 속도를 최적화&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;24시간, 365일 잠들지 않는 무중단 환전/결제 고객 경험 제공 : 외환 서버 구축과 동시에 은행의 회계 정합성을 위한 원장 보정 작업들도 MSA 로 전환함에따라 고객분들에게 24시간 서비스 제공&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=uWnAVYgCd0k&quot;&gt;https://www.youtube.com/watch?v=uWnAVYgCd0k&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[네트워크 타임아웃 문제를 안전히 처리하기 위한 재시도 전략 (Backoff, Retry Storm)]]></title><description><![CDATA[…]]></description><link>https://haon.site/article/toss-slash/safely-network-handling-errors/</link><guid isPermaLink="false">https://haon.site/article/toss-slash/safely-network-handling-errors/</guid><pubDate>Tue, 31 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;네트워크-지연으로-인해-실패를-확정지을-수-없는-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%A7%80%EC%97%B0%EC%9C%BC%EB%A1%9C-%EC%9D%B8%ED%95%B4-%EC%8B%A4%ED%8C%A8%EB%A5%BC-%ED%99%95%EC%A0%95%EC%A7%80%EC%9D%84-%EC%88%98-%EC%97%86%EB%8A%94-%EC%83%81%ED%99%A9&quot; aria-label=&quot;네트워크 지연으로 인해 실패를 확정지을 수 없는 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;네트워크 지연으로 인해 실패를 확정지을 수 없는 상황&lt;/h2&gt;
&lt;p&gt;클라이언트가 서버로 네트워크를 통해 요청을 보낼 때, 성공적으로 잘 처리되어 응답을 받는 경우가 대부분이겠지만, 일부 요청에서는 에러가 발생할 것이다. 그리고 이 에러 중 대부분의 경우는 응답을 받는 즉시 요청이 정말로 실패했는지 확정지을 수 없겠지만, 일부 에러의 경우 실패를 확정지을 수 없다.&lt;/p&gt;
&lt;p&gt;서드파티에게 요청을 보냈을 때, 서드파티가 요청을 정상적으로 처리하였지만 타임아웃이 되었다면? 아불싸. 네트워크 지연 문제로 인해 우리 서비스는 요청을 성공했다고 처리해야할까, 아니면 실패했다고 처리해야할까? &lt;strong&gt;네트워크 지연으로 인해 타임아웃 되었다면 서드파티가 우리가 보낸 요청을 성공적으로 처리했는지, 실패했는지 알 방법이 없다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;타임아웃-발생&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%83%80%EC%9E%84%EC%95%84%EC%9B%83-%EB%B0%9C%EC%83%9D&quot; aria-label=&quot;타임아웃 발생 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;타임아웃 발생&lt;/h3&gt;
&lt;p&gt;한 가지 상황을 떠올려보자. 클라이언트가 서버에 게시글 작성을 요청했고, 서버는 그 요청을 받아 게시물을 데이터베이스에 저장하기 시작했다. 그런데 데이터베이스에 병목이 발생하여 요청한 DB I/O 작업이 장시간 지연되었다. 클라이언트는 30초 넘게 기다렸지만 서버가 응답을 주지 않아 Read Timeout 에러로 클라이언트는 서버와 연결을 끊었다. 즉, 타임아웃이 발생하였다.&lt;/p&gt;
&lt;p&gt;하지만 타임아웃 발생 이후 데이터베이스 작업이 끝났고, 게시물이 정상적으로 등록되었다. 분명 클라이언트는 Read Timeout 에러를 만났지만, 서버는 요청 처리 속도가 느렸을자라도 성공적으로 작업을 수행하였다. 만약 여기서 무턱대고 클라이언트에게 요청이 실패했다고 응답한다면? 실제 요청은 성공했음에도 클라이언트에게는 요청이 실패했다고 응답하게 되는 아주 모순적인 상황이 발생한다.&lt;/p&gt;
&lt;h3 id=&quot;성공실패를-단언할-수-없는-네트워크-에러-문제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B1%EA%B3%B5%EC%8B%A4%ED%8C%A8%EB%A5%BC-%EB%8B%A8%EC%96%B8%ED%95%A0-%EC%88%98-%EC%97%86%EB%8A%94-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%97%90%EB%9F%AC-%EB%AC%B8%EC%A0%9C&quot; aria-label=&quot;성공실패를 단언할 수 없는 네트워크 에러 문제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;성공/실패를 단언할 수 없는 네트워크 에러 문제&lt;/h3&gt;
&lt;p&gt;이처럼 네트워크 에러는 성공/실패 여부를 즉시 확정지을 수 없는 경우가 존재한다. 이러한 네트워크 에러가 발생한 경우, 아래 3가지 case 로 나누어볼 수 있을 것이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 요청이 서버에 도달한 뒤, 네트워크 에러가 발생했고, 그 요청을 실제로도 진짜 실패했다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 요청이 서버에 도달한 뒤, 네트워크 에러가 발생했지만, 사실 그 요청은 서버에서는 막상 까보니 성공했다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 알고보니 요청이 서버에 도달 조차 하지 못했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;결과를 당장 확정할 수 없는 네트워크 에러가 발생했을 때 별 생각없이 이를 실패했다고 확정 해버린다면, 사용자에게 잘못된 결과를 노출할 수 있다. 또한 모놀리식 환경과 다르게, &lt;strong&gt;MSA 환경에서는 데이터베이스의 트랜잭션을 활용한 롤백이 불가능하다. 따라서 한쪽에 실패한 경우 정합성이 깨질 수 있는 것이다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 왜 MSA 환경에서는 트랜잭션을 활용한 롤백이 불가능한지 추가 학습이 필요하다. 이는 2PC, SAGA 패턴 등으로 해결 가능한것인가? 보상 트랜잭션등에 관련한 학습을 해봐야겠다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;주문과-결제-서버&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A3%BC%EB%AC%B8%EA%B3%BC-%EA%B2%B0%EC%A0%9C-%EC%84%9C%EB%B2%84&quot; aria-label=&quot;주문과 결제 서버 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;주문과 결제 서버&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://hudi.blog/safely-handling-network-errors/&quot;&gt;hudi 님의 기술 블로그 포스팅&lt;/a&gt;을 다수 인용 및 참고하여 작성하였다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이커머스 서비스를 개발하고 있다고 가정해보자. 우리 서비스는 유저로부터 주문 요청을 받는 주문 서버, 주문 서버로부터 결제 요청을 받는 결제 서버를 만들고 있다. 주문 서버는 유저의 주문 내역을 저장하고, 결제 서버는 유저의 결제 내역을 저장한다.&lt;/p&gt;
&lt;p&gt;이때, 주문 서버가 결제 서버로 요청했지만 타임아웃이 발생했다면 어떻게 핸들링 해야할까? 주문 서버는 자신의 주문을 확정해야하는데, 결제가 완료되었는지 알 수 없으니 그럴 수 없다. 단순히 결제 요청이 실패해다고 곧바로 주문을 실패처리한다면, 유저는 주문이 실패했다고 응답을 받았는데, 맙소사. 막상 실제로 결제는 성공하여 돈이 빠져나가는 경우가 발생할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;재시도-retry-를-통해-성공실패를-확정짓기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%AC%EC%8B%9C%EB%8F%84-retry-%EB%A5%BC-%ED%86%B5%ED%95%B4-%EC%84%B1%EA%B3%B5%EC%8B%A4%ED%8C%A8%EB%A5%BC-%ED%99%95%EC%A0%95%EC%A7%93%EA%B8%B0&quot; aria-label=&quot;재시도 retry 를 통해 성공실패를 확정짓기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;재시도 (Retry) 를 통해 성공/실패를 확정짓기&lt;/h3&gt;
&lt;p&gt;일단 주문서버는 결제서버에 재시도를 해볼 수 있을 것이다. 결제 서버가 제대로 응답을 할 때 까지 2번이고, 3번이고, ... 성공할 때 까지 N번의 요청을 재시도하는 것이다. 이때 재시도 과정에서 결제 서버가 정상 응답을 내려주었다면, 주문의 상태도 성공/실패로 확정지을 수 있다.&lt;/p&gt;
&lt;p&gt;그렇다고해서 무턱대고 재시도를 막 구현해도 괜찮을까? &lt;a href=&quot;https://haon.blog/article/toss-slash/broker-issue-concurrency-and-network-latency/&quot;&gt;토스 SLASH 22, 애플 한 주가 고객에게 전달되기 까지&lt;/a&gt; 에서도 다루었듯이, &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 타임아웃의 특성상 짧은 주기로 재요청을 시도하면 네트워크 지연 상황을 더욱 악화시킬 수 있다. (네트워크 병목이 더 심해지기만 한다.) &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 또한 의도치 않게 1번의 주문으로 다건의 결제가 진행되는 금융 사고가 발행할 수 있다. 즉, 1건의 주문에 대해 1건의 결제 데이터가 적재되는 것이 아닌, 다량의 결제 데이터가 적재될 수 있다.&lt;/p&gt;
&lt;p&gt;위 2가지 문제를 어떻게 현명히 해결할 수 있을까?&lt;/p&gt;
&lt;h2 id=&quot;멱등한-api-요청을-통해-안전하게-재시도&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%B1%EB%93%B1%ED%95%9C-api-%EC%9A%94%EC%B2%AD%EC%9D%84-%ED%86%B5%ED%95%B4-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-%EC%9E%AC%EC%8B%9C%EB%8F%84&quot; aria-label=&quot;멱등한 api 요청을 통해 안전하게 재시도 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;멱등한 API 요청을 통해 안전하게 재시도&lt;/h2&gt;
&lt;p&gt;우선 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 를 해결하기 위한 방법을 생각해보자. 멱등성이란 같은 요청을 여러번 수행하더라도 항상 최초 요청과 동일한 결과를 응답받도록 하는 성질을 뜻한다. 예를들어, 결제 서버에 멱등성이 적용된다면 동일한 결제 요청이 도달하더라도 결제는 단 1번만 수행되어야 할 것이다. 그런데, 이 멱등성은 어떻게 구현할 수 있는가? &quot;동일한 요청&quot; 임을 결제 서버가 어떻게 식별하고, 판단할 수 있을까?&lt;/p&gt;
&lt;h3 id=&quot;멱등-키-idenpotency-key-를-통한-동일한-요청-식별&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%B1%EB%93%B1-%ED%82%A4-idenpotency-key-%EB%A5%BC-%ED%86%B5%ED%95%9C-%EB%8F%99%EC%9D%BC%ED%95%9C-%EC%9A%94%EC%B2%AD-%EC%8B%9D%EB%B3%84&quot; aria-label=&quot;멱등 키 idenpotency key 를 통한 동일한 요청 식별 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;멱등 키 (Idenpotency Key) 를 통한 동일한 요청 식별&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4acdea9c26d5a793dd0892705b6d2ba0/d4377/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.73619631901841%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABXklEQVR42m2SiY6DMAxE+f8/5KgolBYK5erBVcCr55WlqALJJGB7PDOJt++7WLiPfVdVJZfLRe73u3RdJ0eP1W7bJt7j8ZA8z+X5fB4Wkr9erzIMwyHYuq5yu93kfD5LXdfiTdMkbdsqKOvr9VImtocd+18F7tBlWbR2HEfx+Pn5fJRJ0zTazCSTyGRk932v/8qy1FrWoii0hzx9PB5SSNAMEIVMMk+YTjGN5An2gBoJVKRpqmxVchAECvh+vzURx7EW4SvMUOCaf3Qg1M7z/A+If7AEjAPgm//f71fBCMzn2wLmBHtyDFZAJNP8O5VCJFMMe1gTDCYYmiSJrr7vq9d6bZhunhl9ViYiA0/DMFRQGJGj0VbXZ1aPFw2YCzh36nQ66cnhKUCuBTC2MPmAc21UssnhxGBqFpBkhaGdvOud+Ucd+SzLVJUCwg42RyeIFIYB4D6A0WOXP4oiBf4D5Wyc+orVnOMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/4acdea9c26d5a793dd0892705b6d2ba0/a6d36/image-2.png&quot;
        srcset=&quot;/static/4acdea9c26d5a793dd0892705b6d2ba0/222b7/image-2.png 163w,
/static/4acdea9c26d5a793dd0892705b6d2ba0/ff46a/image-2.png 325w,
/static/4acdea9c26d5a793dd0892705b6d2ba0/a6d36/image-2.png 650w,
/static/4acdea9c26d5a793dd0892705b6d2ba0/e548f/image-2.png 975w,
/static/4acdea9c26d5a793dd0892705b6d2ba0/d4377/image-2.png 1294w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;주문 서버는 결제 서버에 결제 요청을 보낼 때, Unique 한 ID 를 생성하여 함께 보내면 된다. 이 ID 를 멱등키라고 한다. 유일성을 갖는 식별자를 양측간에 서로 공유함으로써, 중복 결제 요청을 방지할 수 있다.&lt;/p&gt;
&lt;p&gt;결제 서버는 주문 서버로부터 결제 요청을 받음과 동시에, 이전에 동일한 멱등키로 결제가 진행된 적이 있는지 확인하면 된다. 만약 없다면, 즉 처음 받은 멱등키라면 결제를 진행한다. 반대로, 이전에 받아 이미 결제가 수행되었던 멱등키라면, 결제를 수행하지 않고 처음 요청의 결과를 그대로 반환한다.&lt;/p&gt;
&lt;h2 id=&quot;네트워크-병목을-고려하여-retry-하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%B3%91%EB%AA%A9%EC%9D%84-%EA%B3%A0%EB%A0%A4%ED%95%98%EC%97%AC-retry-%ED%95%98%EA%B8%B0&quot; aria-label=&quot;네트워크 병목을 고려하여 retry 하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;네트워크 병목을 고려하여 Retry 하기&lt;/h2&gt;
&lt;p&gt;그리고 앞선 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 을 고려한 해결방법을 떠올려보자. &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 은 짧은 주기로 재시도했을 때 되려 네트워크 병목만 발생하여 실패 확률을 더 높이는 상황이다. 이 상황으로보아, 현재 네트워크 상에서 결제 서버는 굉장히 큰 부하를 받고 있는 상태이다.&lt;/p&gt;
&lt;p&gt;결제 서버 입장에서는 당장 현재 받고있는 트래픽이 낮아져야지 요청 속도가 개선된다. 하지만 &lt;strong&gt;결제 서버는 주문 서버가 짧은 주기로 Retry 해버리면 이전보다 더 큰 부하를 받게되어, 상황이 더 악화되기만 한다.&lt;/strong&gt; 게다가 실제 서비스에서는 주문 서버 뿐만 아니라, 결제 서버에 의존하는 수 많은 서버들이 Retry 요청을 보내게 될 것이다. 이를 &lt;strong&gt;Retry Storm&lt;/strong&gt; 이라고 부른다.&lt;/p&gt;
&lt;h3 id=&quot;지수적-간격으로-재시도-exponential-backoff&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%80%EC%88%98%EC%A0%81-%EA%B0%84%EA%B2%A9%EC%9C%BC%EB%A1%9C-%EC%9E%AC%EC%8B%9C%EB%8F%84-exponential-backoff&quot; aria-label=&quot;지수적 간격으로 재시도 exponential backoff permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;지수적 간격으로 재시도 (Exponential Backoff)&lt;/h3&gt;
&lt;p&gt;이를 위해, 토스증권에서도 지수적(Expoential) 재시도 간격을 두는 전략을 취하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/99da9d1803646a07d365ec322467c47d/21b4d/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.6441717791411%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAABS0lEQVR42oVTCY7DIAzk/59MFalRkxVtLsIZvB5aIpamXSTHBzCxPUZ47wlSLuccDcNwSN/3h0gpqbvdqL1eaVIrhRjJBsbY92SLZVloHMcEtHMwcjBrCFZp+z2QNBsp72gymlZnaWQfWnNMrOtK0zT9AcxS+/zhiyZdzD96GnT4bxlmyYC1IJsQQrLDKxaKfYGezPN8mmGZKZbhXi1Op0zjBxFN0xDKPuthqVHWg7MDAXVLSi3atiWU/Q0wvpiUWtH+AegAvFwupJQ6J6XQIzMKhuOXcg9SapbrsUFmG5g9Yf4NEP2r5zCPiOYBf86YgXs6BfU0CGNMYpmLSSw6LgtDOzPIw+rkl6z+twSembzf6WeZDpHLTBv3zGpDzjrCT/E8cRa21rxnbbKhs79tGwmgpiA7ni97tjMQDmCv67okmAhczvEMBFKz/gXzbfzaIDJr+QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/99da9d1803646a07d365ec322467c47d/a6d36/image.png&quot;
        srcset=&quot;/static/99da9d1803646a07d365ec322467c47d/222b7/image.png 163w,
/static/99da9d1803646a07d365ec322467c47d/ff46a/image.png 325w,
/static/99da9d1803646a07d365ec322467c47d/a6d36/image.png 650w,
/static/99da9d1803646a07d365ec322467c47d/e548f/image.png 975w,
/static/99da9d1803646a07d365ec322467c47d/21b4d/image.png 1280w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;예를들면, 위 그래프처럼 최초 재시도는 1초 뒤에 보낸다. 그 뒤에는 재시도 간격을 지수적으로 늘리는 전략을 취한다. 이후 재시도는 2초 뒤에, 그 다음은 4초 뒤에, 8초 뒤, 16초 뒤, ... 이렇게 &lt;strong&gt;지수적으로 재시도 간격을 늘리는 전략을 취하면 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;짧은 주기로 동일한 간격을 갖는 재시도 요청을 보내는 것 보다, 지수적으로 재시도 주기를 더 늘려가면서 요청하는 것이 부하도 훨씬 적어지고, 응답 속도도 더 빨리질 것이다.&lt;/p&gt;
&lt;p&gt;하지만, &lt;strong&gt;결제 서버로 요청하는 모든 서버가 동일한 지수적 증가 전략을 사용하게 되면, 결국 재시도 시점에 결제 서버가 받는 부하는 여전히 클 것이다.&lt;/strong&gt; (결국엔 모든 서버가 재시도를 보내는 시점 타이밍이 똑같으니깐.) 이를 해결할 수 있는 방법은 아래와 같다.&lt;/p&gt;
&lt;h3 id=&quot;exponential-backoff-with-jitter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#exponential-backoff-with-jitter&quot; aria-label=&quot;exponential backoff with jitter permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Exponential Backoff with Jitter&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f0de7a07bb3ffc07e84ae617fbe5119b/1320e/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.44171779141104%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABO0lEQVR42oWTiW6DMAxA8/9ft63aKlWbRGkptCXckIMEz45IFQbtIiw7IXmxY5sNQsC9LEBJCX5ItLuue8gwDND3vbNJX4sc7lUB1YBzLaFVAjolwVgLrG5bOMQRtE3jYBYXp2lydqi9XQ4IRoDfaybrQHYWRjeO4+gOeNgzrXDfD88cILwkvJRROMaYlUdbOqkLSJtysbYC5nn+0jNLB/ETWsNbEi282xKWpun/QBwflyOcy3zxzptAzrkLeQvkYUcE7a/nB+wlkN6QkrL1ZhrXvzEJuyxGOEGWETwF+qRQFoVW0GBZxOjV+yWCpOKrrP5NSDgYFfStLuErPcFndoL9LYEdvleGGVWz53aG+XDJARvUXihsxOxVWNy8a0Eg3I4GJHbPZCwopZxQh9R17TpG437qJP+PJJz/AiOjXpCSJumuAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/f0de7a07bb3ffc07e84ae617fbe5119b/a6d36/image-1.png&quot;
        srcset=&quot;/static/f0de7a07bb3ffc07e84ae617fbe5119b/222b7/image-1.png 163w,
/static/f0de7a07bb3ffc07e84ae617fbe5119b/ff46a/image-1.png 325w,
/static/f0de7a07bb3ffc07e84ae617fbe5119b/a6d36/image-1.png 650w,
/static/f0de7a07bb3ffc07e84ae617fbe5119b/e548f/image-1.png 975w,
/static/f0de7a07bb3ffc07e84ae617fbe5119b/1320e/image-1.png 1292w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Jitter 란 패킷이 도달하는 시간이 일정하지 않고 불규칙한 현상을 뜻한다. Exponential Backoff with Jitter 전략은, 이 &lt;strong&gt;Jitter 를 의도적으로 발생시켜서 재시도 요청들이 일정한 시각에 몰리는 것을 방지하는 전략이다.&lt;/strong&gt; 즉, 위에서 살펴본 지수적 증가 전략에다 무작위성을 더한 전략이다.&lt;/p&gt;
&lt;p&gt;위 그래프에서도 볼 수 있듯이, 지수적으로 증가하는 간격에다 무작위성을 추가하니 동일한 시간대에 요청이 몰리지 않고 분산됨을 알 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;재시도는-무한정-시도할-수-없다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%AC%EC%8B%9C%EB%8F%84%EB%8A%94-%EB%AC%B4%ED%95%9C%EC%A0%95-%EC%8B%9C%EB%8F%84%ED%95%A0-%EC%88%98-%EC%97%86%EB%8B%A4&quot; aria-label=&quot;재시도는 무한정 시도할 수 없다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;재시도는 무한정 시도할 수 없다.&lt;/h3&gt;
&lt;p&gt;당연하게도, 재시도는 무한정으로 보내면 안된다. 무제한으로 재시도 요청을 보낼수는 없다. 많은 재시도를 하게 된다면, 사용자도 그 만큼 늦게 응답을 받게된다. 또한, 재시도는 그 자체로 리소스 낭비가 될 수 있다. &lt;strong&gt;즉, 최대 재시도 횟수를 정해두고 언젠가는 재시도를 포기해야한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;그렇다면 최악의 경우에 수많은 재시도 끝에도 타임아웃을 만나게 되었다면, 주문 서버는 결국 결제 요청의 성공 여부를 결국 모를 것이다. 하지만 주문 서버는 자신의 주문 상태를 확정지여야 한다. 이를 어떻게 해결해야할까?&lt;/p&gt;
&lt;h2 id=&quot;중복-주문-방지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A4%91%EB%B3%B5-%EC%A3%BC%EB%AC%B8-%EB%B0%A9%EC%A7%80&quot; aria-label=&quot;중복 주문 방지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;중복 주문 방지&lt;/h2&gt;
&lt;p&gt;수 많은 재시도 요청끝에 타임아웃을 만나게 되었다면, 유저에게는 &quot;주문 처리 결과를 알 수 업다&quot; 는 메시지가 노출 될 것이다. 유저는 자신의 주문이 어떻게 처리되었는지 알 방법이 없으니, 재주문을 시도할 것이다. 하지만 재주문을 요청한다면 중복으로 주문이 발생할 가능성이 있다. 따라서 주문 서버는 아래 과정을 통해 중복 요청을 막아야한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 주문 상태 초기화 : 주문 서버는 유저에게 주문 요청을 하자마자, 주문을 &lt;code class=&quot;language-text&quot;&gt;대기&lt;/code&gt; 상태로 저장한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 결제 요청 및 주문 상태 전이 : 주문 서버는 결제 서버에 결제 요청을 보내고, 응답에 따라서 주문의 상태를 &lt;code class=&quot;language-text&quot;&gt;성공&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;실패&lt;/code&gt; 로 변경한다. 하지만 다수의 Retry 에도 불구하고 타임아웃이 발생했다면, 결제의 상태를 확정할 수 없으므로 주문의 상태도 여전히 &lt;code class=&quot;language-text&quot;&gt;대기&lt;/code&gt; 상태이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 중복 주문 방지 : 대기 상태의 주문을 보유한 유저가 새로운 주문을 중복 요청하려는 경우, &quot;이전 주문건이 진행중입니다. 잠시만 기다려주세요.&quot; 이라는 문구를 노출하고 중복 주문 요청을 막는다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;확정되지-않은-결제-요청-확정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%99%95%EC%A0%95%EB%90%98%EC%A7%80-%EC%95%8A%EC%9D%80-%EA%B2%B0%EC%A0%9C-%EC%9A%94%EC%B2%AD-%ED%99%95%EC%A0%95&quot; aria-label=&quot;확정되지 않은 결제 요청 확정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;확정되지 않은 결제 요청 확정&lt;/h2&gt;
&lt;p&gt;우선 이렇게 중복 주문을 방지했다. 이제부터는, 주문 서버가 결제 서버로부터 결제 상태를 알아내어 주문 상태를 &lt;code class=&quot;language-text&quot;&gt;대기&lt;/code&gt; 에서 &lt;code class=&quot;language-text&quot;&gt;성공&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;실패&lt;/code&gt; 로 확정시켜야 한다. 이를 위해 주문 서버가 결제 서버로 부터 결제 상태를 알아낼 수 있는 방법은 크게 2가지이다.&lt;/p&gt;
&lt;p&gt;첫번째는 주기적으로 결제 서버에게 요청을 보내어, 결제 내역 상태를 물어보는 방법인 &lt;strong&gt;풀링(Polling)&lt;/strong&gt; 을 사용한 방법이 있다. 두번째는 결제 서버가 최종 상태로 전이된 결제 내역에 대해 이벤트(메시지) 를 발행하는 방법인 &lt;strong&gt;메시지 큐(Message Queue)&lt;/strong&gt; 를 사용하는 방법이다.&lt;/p&gt;
&lt;h3 id=&quot;풀링polling&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%92%80%EB%A7%81polling&quot; aria-label=&quot;풀링polling permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;풀링(Polling)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c647593921b0ebd69e893dfdf989f255/bc3ae/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.331288343558285%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABqklEQVR42mWR227aQBRF/f8f0udWQpEqJUortUnAQASY+G4wxhgM1Db4bsPq1KVSoo60tM/DnDNn75F4d67Xa6fbKOPTo8KXF4PewODryOaQ5Lc7/HeKouB0OnX9UpJVRGlFLKiamgstSZ4zcwMGhsdU6Oe+xiKMuub6UlMlMeVhTyHg0rJcuowGQ3zfR1K8EtVvUNY1um/jhnNWe41dbBImplCDXaTj7t5YCN6cJ2L5mUIekD4/UfkbSmqsWMNPXCQtaLD2V9QQFsMJmTojd2xS0+RsGR2ZbVI4Jqmoj7MJ+VCmnU4ohwPKYEOdZ2zln8S2jjS2Ir6PHPraQVhtyUQWlmHhrzw812Ml7CydJQvbIc9Ejm1LKDYz7+4IHh8oN34XRXs606Qp0qsd80226IsVfxUN8fmIos2xlibGwkAXryqaIpgRRiK38kT48oTW6+E/3FPdBv77LUnbNNgHYXl7YW6rrLYz1kcDb6+yPmgf+JPv3PzBeTzgMp1SjmSKtfdxoBrUmLu/GQZTnVpTyUV+hSEQ2tWmRSmoHGFb1yjEoOZ1THXL8P3A30lET/k//XreAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/c647593921b0ebd69e893dfdf989f255/a6d36/image-3.png&quot;
        srcset=&quot;/static/c647593921b0ebd69e893dfdf989f255/222b7/image-3.png 163w,
/static/c647593921b0ebd69e893dfdf989f255/ff46a/image-3.png 325w,
/static/c647593921b0ebd69e893dfdf989f255/a6d36/image-3.png 650w,
/static/c647593921b0ebd69e893dfdf989f255/e548f/image-3.png 975w,
/static/c647593921b0ebd69e893dfdf989f255/bc3ae/image-3.png 1268w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;결제 서버가 결제 내역의 상태를 조회할 수 있는 API 를 하나 만들어두고, 주문 서버가 해당 API 를 주기적으로 요청하는 것이다. 주문 서버는 이 API 를 통해 대기 상태로 남아있는 결제건에 대해 상태를 주기적으로 질의한다. 주기적인 질의를 통해 결제 서버가 해당 결제에 대해 성공 또는 실패의 최종 상태로 응답을 한다면, 주문 서버도 해당 결제건과 매핑되어 있는 주문 내역의 상태를 성공 또는 실패로 확정(갱신) 하면 된다.&lt;/p&gt;
&lt;p&gt;하지만 이 풀링(Polling) 의 특성상 앞서 살펴본 Retry Storm 과 마찬가지로 결제 서버에 부하를 줄 수 있다. 결제 상태가 최종 상태로 확정되는 순간에, 주문 서버가 이를 알 수 있도록 하는 방법은 없을까? 이는 바로 메시지 큐를 사용하면 가능해진다.&lt;/p&gt;
&lt;h3 id=&quot;메시지-큐-message-queue&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%94%EC%8B%9C%EC%A7%80-%ED%81%90-message-queue&quot; aria-label=&quot;메시지 큐 message queue permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메시지 큐 (Message Queue)&lt;/h3&gt;
&lt;p&gt;카프카와 같은 메시지 큐를 사용하면, 결제 서버에 부하를 주지 않는다. 결제 서버는 완료된 결제 내역에 대해 성공/실패 여부를 메시지로 발행하고, 주문 서버가 해당 메시지를 컨슘한다. 주문 서버는 컨슘한 메시지의 결제 내역의 상태에 따라, 주문 내역의 상태를 성공 또는 실패로 갱신한다.&lt;/p&gt;
&lt;p&gt;풀링에 비해 부하도 발생하지 않으며, CDL Retry 패턴등을 사용하여 더 안정적으로 정합성을 유지할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;결제-서버에서-결제-요청이-도달조차-하지-못했다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EC%A0%9C-%EC%84%9C%EB%B2%84%EC%97%90%EC%84%9C-%EA%B2%B0%EC%A0%9C-%EC%9A%94%EC%B2%AD%EC%9D%B4-%EB%8F%84%EB%8B%AC%EC%A1%B0%EC%B0%A8-%ED%95%98%EC%A7%80-%EB%AA%BB%ED%96%88%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;결제 서버에서 결제 요청이 도달조차 하지 못했다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결제 서버에서 결제 요청이 도달조차 하지 못했다면?&lt;/h3&gt;
&lt;p&gt;네트워크 지연으로 인해 주문 서버의 요청이 결제 서버에 도달조차 하지 못한 경우도 낮은 확률로 발생할 수 있다. 이 경우 결제 서버는 해당 결제건에 대해 애당초 전혀 모를 것이다. 이런 경우 영영 주문 상태는 최종 상태로 확정되지 못할 것이고, 유저는 영원히 주문을 할 수 없는 상태에 놓인다.&lt;/p&gt;
&lt;p&gt;이런 상황을 막기 위해선, 대기중인 주문이 이미 존재하여 유저의 주문을 막기 전에, 주문 서버는 결제 서버에게 대기중인 주문건에 매핑된 결제건에 대해 알고있는지 물어보야한다. 결제 서버가 모르고 있다면, 결제 과정 자체가 시작되지 않았으므로 주문을 실패로 확정 처리해도 된다.&lt;/p&gt;
&lt;p&gt;몰론 결제 요청이 알 수 없는 이유로 결제 상태 확인 요청보다 늦게 들어오는 경우 또 정합이 깨질 수 있다. 이럴 때에는 주문 서버와 결제 서버 모두가 타임아웃 시간을 합의하고, 결제 요청에 요청을 보낸 타임스탬프를 기록한 뒤, 타임아웃 시간 이후에 요청이 들어온 경우를 항상 실패처리하면 된다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/safely-handling-network-errors/&quot;&gt;https://hudi.blog/safely-handling-network-errors/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=v9rcKpUZw4o&quot;&gt;https://www.youtube.com/watch?v=v9rcKpUZw4o&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=UOWy6zdsD-c&quot;&gt;https://www.youtube.com/watch?v=UOWy6zdsD-c&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tech.kakaopay.com/post/msa-transaction/&quot;&gt;https://tech.kakaopay.com/post/msa-transaction/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jungseob86.tistory.com/12&quot;&gt;https://jungseob86.tistory.com/12&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[트랜잭션 동기화와 추상화 (feat. JDBC Connection)]]></title><description><![CDATA[데이터베이스 레플리케이션을 구축할 때  를 사용하여 라우팅 환경을 구축한 경험이 있다. 이번 포스팅에선 트랜잭션 동기화와 추상화에 대해 다루어보고자 한다. JDBC 에서 트랜잭션 로직이 뒤섞이는 문제 데이터베이스에서 간단히 SELECT, INSERT…]]></description><link>https://haon.site/spring/transaction-synchornization-and-abstraction/</link><guid isPermaLink="false">https://haon.site/spring/transaction-synchornization-and-abstraction/</guid><pubDate>Mon, 30 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;데이터베이스 레플리케이션을 구축할 때 &lt;code class=&quot;language-text&quot;&gt;TransactionaSynchornizationManager&lt;/code&gt; 를 사용하여 라우팅 환경을 구축한 경험이 있다. 이번 포스팅에선 트랜잭션 동기화와 추상화에 대해 다루어보고자 한다.&lt;/p&gt;
&lt;h2 id=&quot;jdbc-에서-트랜잭션-로직이-뒤섞이는-문제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jdbc-%EC%97%90%EC%84%9C-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EB%A1%9C%EC%A7%81%EC%9D%B4-%EB%92%A4%EC%84%9E%EC%9D%B4%EB%8A%94-%EB%AC%B8%EC%A0%9C&quot; aria-label=&quot;jdbc 에서 트랜잭션 로직이 뒤섞이는 문제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JDBC 에서 트랜잭션 로직이 뒤섞이는 문제&lt;/h2&gt;
&lt;p&gt;데이터베이스에서 간단히 &lt;strong&gt;SELECT, INSERT&lt;/strong&gt; 문을 날리면 데이터베이스 쿼리 결과가 실제로 반영된다. 사실 이를 가능케 하는것 &lt;strong&gt;AUTO COMMIT&lt;/strong&gt; 기능이 활성화되었기 때문이다. &lt;strong&gt;AUTO COMMIT&lt;/strong&gt; 이란 데이터베이스 내부적으로 커밋을 자동으로 수행하는 기능이다. 반대로 말해, &lt;strong&gt;AUTO COMMIT&lt;/strong&gt; 이 꺼져있다면 커밋이 수행되지 않아서 데이터베이스에 쿼리가 반영되지 않는다.&lt;/p&gt;
&lt;p&gt;보통 트랜잭션을 처리하면 최소 2개 이상의 여러 쿼리가 한 단위로 묶여서 수행된다. 그런데 만약 AUTO COMMIT 이 활성화되었다면 트랜잭션이 정상 수행될까? 트랜잭션은 ACID 원칙에 따라 원자성(atomic) 을 보장해야한다. 그런데 AUTO COMMIT 이 활성화된다면 단일 쿼리가 별개로 동작하고 커밋되어서 원자성을 갖지 못하게 된다. 따라서 JDBC 환경에선 &lt;strong&gt;트랜잭션의 원자성을 보장하기 위해 AUTO COMMIT 기능을 비활성화하고, 우리가 직접 커밋과 롤백의 시점을 관리하고 구성&lt;/strong&gt;해야한다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Connection&lt;/code&gt; 오브젝트는 &lt;code class=&quot;language-text&quot;&gt;setAutoCommit()&lt;/code&gt; 메소드를 통홰 AUTO COMMIT 의 활성화 여부를 설정할 수 있도록 해준다. 아래와 같이 여행지를 방문하는 상황을 가정해보자. 여기서 가장 중심적으로 살펴봐야 할 부분은 &lt;code class=&quot;language-text&quot;&gt;Connection&lt;/code&gt; 을 외부로부터 파라미터로 전달받는다는 점이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Repository&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TripDao&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findTrip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SELECT * FROM trip WHERE id = ?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; conn &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; conn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prepareStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// id에 해당하는 값을 매개변수로 설정&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;ResultSet&lt;/span&gt; rs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execteQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;place_name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;image_url&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이어서 여행지를 방문했다면 동시에 해당 여행지의 누적 방문 수를 1증가시키는 기능도 함께 동작하도록 해야한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateTrip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;update trip set visited_count = visited_count +1 where id = ?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prepareStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞서 설명했듯이 DAO 내부에서 직접 &lt;code class=&quot;language-text&quot;&gt;Connection&lt;/code&gt; 을 생성하는 것이 아니라 외부로부터 파라미터로 주입받아서 사용한다. 이렇게 구성한 이유는, 트랜잭션을 사용하기 위해선 트랜잭션을 구성하는 여러개의 쿼리가 동일한 커넥션에서 실행되어야하기 때문이다. 아래와 같이 애플리케이션 계층에서 여행지 조회 및 업데이트 쿼리가 동일한 &lt;code class=&quot;language-text&quot;&gt;Connection&lt;/code&gt; 에서 실행될 수 있어야지 트랜잭션 묶음으로 수행되었다고 할 수 있다. 또한 쿼리 실행전에 AUTO COMMIT 을 비활성화하였으며, 모든 쿼리 실행 후 커밋을 수행한 모습을 볼 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TripService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TripDao&lt;/span&gt; tripDao&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findTrip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; connection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            connection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DrivierManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAutoCommit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt; trip &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tripDao&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findTrip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;connection&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            tripDao&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateTrip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;connection&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;rollBack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드는 비즈니스 로직이 직관적으로 눈에 보이지 않는다. 위에서 사용되는 모든 쿼리를 동일한 &lt;code class=&quot;language-text&quot;&gt;Connection&lt;/code&gt; 에서 처리해야 하다보니, 불가피하게 &lt;code class=&quot;language-text&quot;&gt;Connection&lt;/code&gt; 이 애플리케이션 계층에 위치할 수 밖에 없다. 애플리케이션 계층의 주 관심사는 비즈니스 로직인데, 이 때문에 비즈니스 로직에 집중할 수가 없게 되었다. 또한 try-catch-finally 구조로 인해 가독성도 저하된다.&lt;/p&gt;
&lt;p&gt;결국 커넥션을 생성하고, 트랜잭션 경계를 설정하는 코드가 애플리케이션 계층에 위치해있다보니 DAO 에서 본디 수행해야 할 데이터 엑세스 처리 관련 로직이 애플리케이션 계층에도 존재한다.&lt;/p&gt;
&lt;h2 id=&quot;트랜잭션-동기화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EB%8F%99%EA%B8%B0%ED%99%94&quot; aria-label=&quot;트랜잭션 동기화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 동기화&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Connection&lt;/code&gt; 을 파라미터로 넘겨서 공유하는 방식은 많은 단점을 안겨준다. 이를 해결하기 위해 &lt;code class=&quot;language-text&quot;&gt;Connection&lt;/code&gt; 을 계속 파라미처로 전달하는 코드를 재거해보자. 스프링에서는 **트랜잭션 동기화 매니저(Transaction Synchornization Manager)**을 통해 이를 가능케한다. 트랜잭션 동기화 매니저는 트랜잭션을 시작하기 위해 각 쓰레드마다 생성한 &lt;code class=&quot;language-text&quot;&gt;Connection&lt;/code&gt; 객체를 고유한 &lt;strong&gt;쓰레드 로컬(Thread Local)&lt;/strong&gt; 에 저장하고, 필요할 때 마다 매번 쓰레드 풀에서 꺼내 쓰는 역할을 수행한다. 쓰레드 로컬 특성상 각 쓰레드별로 독립적인 &lt;code class=&quot;language-text&quot;&gt;Connection&lt;/code&gt; 을 보관할 수 있기 때문에 멀티 쓰레드 환경에서도 항상 동일한 커넥션 객체를 가져올 수 있게된다.&lt;/p&gt;
&lt;p&gt;트랜잭션 동기화는 다음과 같은 과정을 통해 수행된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 을 만나는 그 즉시 &lt;strong&gt;트랜잭션 추상화 매니저(PlatformTransactionManager)&lt;/strong&gt; 가 &lt;code class=&quot;language-text&quot;&gt;getConnection()&lt;/code&gt; 을 호출하여 트랜잭션을 시작할 준비를 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 이때, 트랜잭션을 시작하기 위해선 Connection이 필요하다. 따라서 트랜잭션 매니저는 내부에서 DataSource 를 사용해서 Connection 을 생성한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; Connection 의 자동 커밋을 비활성화 해준다. 즉, &lt;strong&gt;AUTO COMMIT&lt;/strong&gt; 옵션을 False 로 지정한 뒤에 이제부터 본격적으로 트랜잭션을 시작한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; Connection 을 &lt;strong&gt;트랜잭션 동기화 매니저(Transaction Synchornization Manager)&lt;/strong&gt; 에 보관한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; 트랜잭션 동기화 매니저는 본인이 보유한 쓰레드 로컬에 커넥션을 저장한다. 앞으로 트랜잭션내의 비즈니스 로직을 수행하면서 커넥션이 필요할 때 마다 이 쓰레드 로컬에 저장한 커넥션을 꺼내어 올 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(6)&lt;/code&gt; 비즈니스 로직을 수행하면서 커넥션이 필요할 때 마다 &lt;code class=&quot;language-text&quot;&gt;DataSourceUtils&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;getConnection()&lt;/code&gt; 을 호출하여 트랜잭션 동기화 매니저에 보관된 커넥션을 꺼내어 사용한다. 이로써 하나의 트랜잭션내에 담긴 모든 쿼리를 수행시 동일한 커넥션을 사용하게 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(7)&lt;/code&gt; 비즈니스 로직이 끝나고 트랜잭션을 커밋 또는 롤백할 때도 트랜잭션 매니저에서 커넥션을 꺼내와서 트랜잭션을 커밋 또는 롤백한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;트랜잭션-동기화-적용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EB%8F%99%EA%B8%B0%ED%99%94-%EC%A0%81%EC%9A%A9&quot; aria-label=&quot;트랜잭션 동기화 적용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 동기화 적용&lt;/h3&gt;
&lt;p&gt;스프링에선 &lt;code class=&quot;language-text&quot;&gt;TranscationSynchornizationManager&lt;/code&gt; 라는 클래스를 사용해서 트랜잭션을 동기화한다. 이 클래스를 활용하여 앞선 코드를 개선해보자. 앞서 설명했듯이 커넥션을 생성하기 위해선 &lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 가 필요하다. 이를 위해 &lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 에 대한 기본 설정을 application.yml 에서 진행해주자.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://haon.blog/database/jdbc-connection-pool/&quot;&gt;JDBC 에서 데이터베이스 커넥션 풀 다루기 (feat. JDBC Driver, DataSource, HikariCP)&lt;/a&gt; 에서 다루었듯이, DataSource 는 자바 애플리케이션에서 데이터베이스 커넥션을 얻어오기 위해 사용하는 방법 중 하나이다. 각 서비스에서 사용하는 DB 벤터에 알맞게 DataSource 구현체(HikariCP, DriverManager 등) 이 자동 주입될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yml&quot;&gt;&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;spring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;datasource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; jdbc&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//mysql&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//localhost&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;3306/moheng
    &lt;span class=&quot;token key atrule&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; root
    &lt;span class=&quot;token key atrule&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1234&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;driver-class-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; com.mysql.cj.jdbc.Driver&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;TransactionSynchornizationManager&lt;/code&gt; 를 통해 트랜잭션 동기화 작업을 수행한다. 트랜잭션 동기화의 핵심은 어떤 계층, 어떤 곳에서든 동일한 트랜잭션이라면 항상 동일한 커넥션으로 쿼리를 수행하는 것이다. 실제로 &lt;code class=&quot;language-text&quot;&gt;Connection&lt;/code&gt; 을 일일이 메소드 파라미터로 넘겨주지 않더라도 같은 커넥션내에서 쿼리가 수행되도록 개선되었다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TripService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; dataSource&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TripDao&lt;/span&gt; tripDao&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findTrip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;TransactionSynchornizationManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;initSynchornization&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 트랜잭션 동기화 초기화&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; connection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 트랜잭션 동기화 매니저에 생성된 커넥션 가져오기&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAutoCommit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt; trip &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tripDao&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findTrip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            tripDao&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateTrip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;rollBack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;DataSourceUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;releaseConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;connection&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 커넥션 해제&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;개선 결과, Dao 가 아래처럼 외부로부터 파라미터를 통해 &lt;code class=&quot;language-text&quot;&gt;Connection&lt;/code&gt; 을 전달받지 않는다. 그 대신 &lt;code class=&quot;language-text&quot;&gt;DataSourceUtils&lt;/code&gt; 를 사용하여 커넥션을 꺼내오도록 개선되었다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Repository&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TripDao&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; dataSource&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findTrip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; connection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SELECT * FROM trip WHERE id = ?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; conn &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; conn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prepareStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;ResultSet&lt;/span&gt; rs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execteQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;place_name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;image_url&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateTrip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; connection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;update trip set visited_count = visited_count +1 where id = ?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prepareStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 &lt;code class=&quot;language-text&quot;&gt;TransactionSynchorizationManager&lt;/code&gt; 를 통해 커넥션을 일일이 메소드 파라미터로 전달하는 코드가 제거되었다. 그런데, 아직까진 애플리케이션 계층이 다소 지저분하다는 느낌을 받지 않는가? 아직 TripService 코드에는 커넥션을 통해 직접적으로 트랜잭션 경계를 설정하는 코드가 남아있다.&lt;/p&gt;
&lt;h2 id=&quot;트랜잭션-추상화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%B6%94%EC%83%81%ED%99%94&quot; aria-label=&quot;트랜잭션 추상화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 추상화&lt;/h2&gt;
&lt;p&gt;애플리케이션 계층내에 트랜잭션 경계를 직접 설정하는 코드를 제거하고 싶다는 생각이 든다. 이를위해, 스프링에서는 &lt;strong&gt;트랜잭션 추상화(Transaction Abstraction)&lt;/strong&gt; 방법을 제공하고 있다. 트랜잭션 추상화란 트랜잭션 경계를 지정하는 과정을 추상화한 방법으로, 트랜잭션 추상화 관리자로 &lt;code class=&quot;language-text&quot;&gt;PlatformTransactionManager&lt;/code&gt; 를 제공한다. 이를 사용하여 트랜잭션 경계를 지정하는 로직을 간편히 구성할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PlatformTransactionManager&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionManger&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;TransactionStatus&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getTransaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nullable&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionDefinition&lt;/span&gt; definition&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransacionException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TransactionStatus&lt;/span&gt; status&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rollback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TransactionStatus&lt;/span&gt; status&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;PlatformTransactionManger&lt;/code&gt; 는 위와 같은 3가지 메소드를 제공하고 있다. 유추할 수 있듯이, 커밋과 롤백되는 시점을 지정할 수 있는 방법을 간편하게 추상화하였다. 이때 &lt;code class=&quot;language-text&quot;&gt;TransactionStatus&lt;/code&gt; 는 현재 트랜잭션의 ID와 구분 정보를 담고있는 객체이다.&lt;/p&gt;
&lt;p&gt;트랜잭션 추상화 매니저인 &lt;code class=&quot;language-text&quot;&gt;PlatformTransactionManger&lt;/code&gt; 는 인터페이스이다. 이의 구현 클래스로 &lt;code class=&quot;language-text&quot;&gt;DataSourceTransactionManager&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;JpaTransactionManager&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;HibernateTransactionManager&lt;/code&gt; 등 많은 구현체가 존재한다. 우리는 &lt;code class=&quot;language-text&quot;&gt;PlatformTransactionManger&lt;/code&gt; 을 통해 JPA, JDBC, Hibernate, Jta, ... 등 기술 스택이 변경되는 상황에서도 유연하게 대응할 수 있다.&lt;/p&gt;
&lt;p&gt;만약 앞선 트랜잭션 동기화 코드를 트랜잭션 추상화를 적용하여 개선한다면 어떻게될까? &lt;code class=&quot;language-text&quot;&gt;PlatformTransactionManger&lt;/code&gt; 구현체 중 하나인 &lt;code class=&quot;language-text&quot;&gt;DataSourceTransactionManager&lt;/code&gt; 를 사용하여 코드를 개선해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TripService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; dataSource&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TripDao&lt;/span&gt; tripDao&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findTrip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;PlatformTransactionManager&lt;/span&gt; transactionManager &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceTransactionManger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;TransactionStatus&lt;/span&gt; status &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; transactionManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTransaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DefaultTransactionDefinition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

            &lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt; trip &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tripDao&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findTrip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            tripDao&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateTrip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            transactionManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            transactionManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;rollBack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;어떤가? 훨씬 간편해지지 않았는가? 커넥션을 쓰레드 로컬에서 직접 꺼내오는 로직이 없어졌고, 커넥션을 코드상에서 직접 조작하지 않을 수 있게 되었다. 앞선 코드에서는 커넥션을 꺼내와서 트랜잭션 경계를 장황하게 설정해야 했지만, 이 코드가 더 깔끔히 개선되었다. 또한 트랜잭션 동기화를 위한 코드도 제거되었다. 이를통해 &lt;code class=&quot;language-text&quot;&gt;PlatformTransactionManager&lt;/code&gt; 는 트랜잭션 동기화 로직도 추상화함을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;TransactionStatus&lt;/code&gt; 는 트랜잭션의 4가지 속성 (트랜잭션 전파, 격리 수준, 타임아웃, 읽기/쓰기 전용 여부) 의 상태값을 담고있는 인터페이스이다. 이 구현체로 &lt;code class=&quot;language-text&quot;&gt;DefaultTransactionDefinition&lt;/code&gt; 를 사용했다.&lt;/p&gt;
&lt;h3 id=&quot;aop-를-이용한-선언전-트랜잭션&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#aop-%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%84%A0%EC%96%B8%EC%A0%84-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98&quot; aria-label=&quot;aop 를 이용한 선언전 트랜잭션 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AOP 를 이용한 선언전 트랜잭션&lt;/h3&gt;
&lt;p&gt;사실 &lt;code class=&quot;language-text&quot;&gt;PlatformTransactionManager&lt;/code&gt; 를 우리가 평소에 직접 사용할 일은 거의 없다. 그렇다면 우리는 어떠한 방식으로 트랜잭션 추상화를 누리고 있던것일까? 이 비밀은 바로 &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 어노테이션에 있다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 은 AOP 를 통한 트랜잭션 경계 어노테이션이다. 우리는 이 어노테이션을 사용하면서 내부적으로 &lt;code class=&quot;language-text&quot;&gt;PlatformTransactionManager&lt;/code&gt; 의 기능, 즉 트랜잭션 추상화의 기능을 제공받고 있었던 것이다.&lt;/p&gt;
&lt;h2 id=&quot;abstractroutingdatasource-에서-동적으로-datasource-를-결정하는-원리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#abstractroutingdatasource-%EC%97%90%EC%84%9C-%EB%8F%99%EC%A0%81%EC%9C%BC%EB%A1%9C-datasource-%EB%A5%BC-%EA%B2%B0%EC%A0%95%ED%95%98%EB%8A%94-%EC%9B%90%EB%A6%AC&quot; aria-label=&quot;abstractroutingdatasource 에서 동적으로 datasource 를 결정하는 원리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AbstractRoutingDataSource 에서 동적으로 DataSource 를 결정하는 원리&lt;/h2&gt;
&lt;p&gt;이렇게까지 트랜잭션 동기화와 추상화에 대해 학습해보았다. 이번 포스팅의 가장 큰 목적은 트랜잭션 동기화 매니저에 대해 학습하는 것 이었다. 이 트랜잭션 동기화 매니저는 아래와 같이 &lt;a href=&quot;https://haon.blog/database/replication-mysql-springboot/&quot;&gt;데이터베이스 레플리케이션 환경을 구축&lt;/a&gt;할 때 &lt;code class=&quot;language-text&quot;&gt;AbstractRoutingDataSource&lt;/code&gt; 에서 사용했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//  AbstractRoutingDataSource : Multi DataSource 환경에서 여러 DataSource 를 묶고 분기해줄 때 사용한다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AbstractRoutingDataSource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// determineCurrentLookupKey 메소드 : 여러 datasource 중에서 실제로 사용될 DataSource 를 결정하는 역할&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 현재 트랜잭션의 속성에 따라 targetDataSourceMap 의 조회 Key 를 결정하기위해 AbstractRoutingDataSource 를 상속받아서 determineCurrentLookupKey 를 구현했다.&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;determineCurrentLookupKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; isReadOnly &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionSynchronizationManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isCurrentTransactionReadOnly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;현재 트랜잭션 속성이 ReadOnly인가?:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; isReadOnly&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; isReadOnly &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;slave&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;AbstractRoutingDataSource&lt;/code&gt; 구현체는 &lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 인터페이스의 구현체로, 동적인 DataSource 를 뜻한다. &lt;code class=&quot;language-text&quot;&gt;AbstractRoutingDataSource&lt;/code&gt; 는 어떻게 동적인 DataSource 를 결정할 수 있는것일까?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt; @Transactional&lt;/code&gt; 를 마주한 시점부터 트랜잭션이 시작된다. 트랜잭션 추상화 매니저(PlatformTransactionManger) 가 &lt;code class=&quot;language-text&quot;&gt;getConnection()&lt;/code&gt; 을 호출하면서, 내부적으로 &lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 를 사용하여 커넥션을 생성할 준비를 한다. 또한, 이때 현재 트랜잭션의 4가지 속성도 분석하여 &lt;code class=&quot;language-text&quot;&gt;TransactionStatus&lt;/code&gt; 형태로 트랜잭션 동기화 매니저 (쓰레드 로컬) 에 저장한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 커넥션을 생성하기 위해선 DataSource 정보가 필요할 것이다. 이때, 만약 AbstractRoutingDataSource 를 별도로 설정해놓았다면, AbstractRoutingDataSource 가 동적으로 트랜잭션 속성에 따라 적절한 DataSource 정보를 제공하게 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 이에따라 RoutingDataSource 의 &lt;code class=&quot;language-text&quot;&gt;determineCurrentLookupKey()&lt;/code&gt; 메소드를 호출하게 된다. 이 메소드는 트랜잭션의 읽기 전용 여부를 확인하고 어떤 DataSource 를 제공할지 선택하는 역할을 한다. &lt;code class=&quot;language-text&quot;&gt;TransactionSynchronizationManager&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;isCurrentTransactionReadOnly()&lt;/code&gt; 가 호출된다면, 앞서 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에서 저장해놓았던 &lt;code class=&quot;language-text&quot;&gt;TransactionStatus&lt;/code&gt; 정보를 쓰레드 로컬에서 추출하여 현재 트랜잭션의 ReadOnly 여부를 판단한다. 그를 기반으로 아직 제대로 결정되지 못했던 DataSource 를 확실히 결정짓게된다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;master&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; masterDataSource&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;slave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; slaveDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

   &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt; routingDataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 쿼리 요청을 적절한 서버로 분기할 때 활용됨&lt;/span&gt;
   &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; targetDataSourceMap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;

   &lt;span class=&quot;token comment&quot;&gt;// targetDataSourceMap 객체에 분기할 서버들의 DataSource 빈을 저장&lt;/span&gt;
   targetDataSourceMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; masterDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
   targetDataSourceMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;slave&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; slaveDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

   routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTargetDataSources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;targetDataSourceMap&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (3) =&gt; DataSource 타깃을 설정한다.&lt;/span&gt;
   routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDefaultTargetDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;masterDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (4)&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 이렇게 동적으로 DataSource 를 결정하게 된 RoutingDataSource 를 위처럼 스프링 빈으로 등록해주면, &lt;code class=&quot;language-text&quot;&gt;PlatformTransactionManager&lt;/code&gt; 는 내부적으로 이 DataSource 를 사용해서 커넥션을 을 생성한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; 이후 과정은 앞서 트랜잭션 동기화 과정에서 설명한 내용과 동일하다. 커넥션의 AUTO COMMIT 을 비활성화하고, 이제부터 트랜잭션을 본격적으로 시작하게 될 것이다. 그리고 이 커넥션 객체는 &lt;code class=&quot;language-text&quot;&gt;TransactionSynchornizationManager&lt;/code&gt; 에 보관된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(6)&lt;/code&gt; 현재 트랜잭션내에서 매번 쿼리가 수행될 때 마다 &lt;code class=&quot;language-text&quot;&gt;DataSourceUtils&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;getConnection()&lt;/code&gt; 이 호출될 것이다. 이에따라 쓰레드 로컬에서 추출된 동일한 커넥션을 재사용함으로써 트랜잭션 동기화가 보장된다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Primary&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; determinedDataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;masterDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slaveDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LazyConnectionDataSourceProxy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;determinedDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(7)&lt;/code&gt; 마지막으로 &lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourceProxy&lt;/code&gt; 를 사용하여 커넥션을 획득하는 시점을 &lt;strong&gt;지연(Lazy)&lt;/strong&gt; 시킨다. 만약 이 과정이 없다면 스프링은 기본적으로 &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 을 만나자 마자, 즉 트랜잭션에 진입하자마자 그 즉시 DataSource 를 가져오고 커넥션을 맺는다. 이 떄문에 &lt;code class=&quot;language-text&quot;&gt;RoutingDataSource&lt;/code&gt; 내에서 라우팅할 DataSource 를 결정하는 작업은 DataSource 로 부터 이미 커넥션을 맺은 이후에 뒤늦게 동작한다. 따라서 커넥션을 획득하는 시점을 지연시키는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourceProxy&lt;/code&gt; 를 사용하면 트랜잭션 진입 시점에 실제 커넥션을 리턴하는 대신에 프록시 커넥션 객체를 대신 리턴한다고 했었다. 이를통해 커넥션이 실제로 사용되는 시점까지 커넥션 획득을 지연시켜서, AbstractRoutingDataSource 가 정상적으로 트랜잭션 ReadOnly 상태 값을 읽어올 수 있게된다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;스프링 데이터베이스 1편 : 데이터 접근 핵심 원리, 김영한&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@daehoon12/Spring-DB-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EB%A7%A4%EB%8B%88%EC%A0%80&quot;&gt;https://velog.io/@daehoon12/Spring-DB-트랜잭션-매니저&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jongmin92.github.io/2018/04/08/Spring/toby-5/&quot;&gt;https://jongmin92.github.io/2018/04/08/Spring/toby-5/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL 레플리케이션 복제 동기화 방식 (비동기, 반동기)]]></title><description><![CDATA[…]]></description><link>https://haon.site/database/replication-synchornization-type/</link><guid isPermaLink="false">https://haon.site/database/replication-synchornization-type/</guid><pubDate>Sat, 28 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;소스 서버에서 빌셍힌 이벤트(트랜잭션)은 바이너리 로그에 기록되고, 레플리카 서버는 주기적으로 바이너리 로그 덤프 쓰레드를 사용하여 이벤트를 요청하고, 전송받는다. 즉, 소스 서버에서 레플리카 서버로 데이터를 복제하는데에 소요 시간이 댜소 발생한다. 그럼 궁금증이 하나 생긴다. &lt;strong&gt;과연 복제하는 동안에는 두 서버의 데이터가 다를텐데, 이로 인해 문제가 발생하지는 않을까?&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;비동기-복제-방식-asynchronous-replication&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%B3%B5%EC%A0%9C-%EB%B0%A9%EC%8B%9D-asynchronous-replication&quot; aria-label=&quot;비동기 복제 방식 asynchronous replication permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비동기 복제 방식 (Asynchronous replication)&lt;/h2&gt;
&lt;h3 id=&quot;특징&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B9%EC%A7%95&quot; aria-label=&quot;특징 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;특징&lt;/h3&gt;
&lt;p&gt;위와 같은 문제점은 비동기 복제 방식에서 발생할 수 있다. 비동기 복제 방식이라면 &lt;strong&gt;레플리카로 이벤트가 잘 전달 되었는지, 실제로 이벤트 복제가 잘 이루어졌는지 알 수 없고, 보장하지도 않는다.&lt;/strong&gt; 비동기 방식은 소스 서버에서 쓰기 연산이 수행된 후, 이벤트를 레플리카 서버에서 전송하기만 하고 자기는 곧 바로 커밋하는 셈이다. 즉, 소스 서버에서 발생한 트랜잭션은 레플리카에 동기화되었는지와 별개로 동작하고, 커밋된다.&lt;/p&gt;
&lt;p&gt;MySQL 레플리케이션은 기본적으로 비동기 복제 방식으로 동작한다. 따라서 별다른 설정이 없다면 서버간의 데이터 누락, 정합성 문제가 발생할 수 있다. 그런데, 이렇게 소스 서버가 레플리카 서버에 이벤트 동기화 여부를 보장하지 않는다는 특징은 장점도 있고, 단점도 있다.&lt;/p&gt;
&lt;h3 id=&quot;장점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90&quot; aria-label=&quot;장점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h3&gt;
&lt;p&gt;어떻게 보면 장점이 되기도 한다. 비동기 처리의 특성상 성능이 더 빨라지게 된다. 소스 서버 입장에서는 각 트랜잭션 작업을 수행할 때 레플리카 서버로 전송하는 부분을 기다리지 않고 수행되기 때문에, &lt;strong&gt;트랙잭션 처리 속도가 빨라진다.&lt;/strong&gt; 또한 레플리카 서버에 문제가 발생하더라도 소스 서버가 영향을 받지 않고 독립적으로 동작할 수 있다는 점이다.&lt;/p&gt;
&lt;h3 id=&quot;단점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90&quot; aria-label=&quot;단점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;p&gt;소스 서버에 장애가 발생시 레플리카 서버로 이벤트가 제대로 전달되지 않을 수 있다. 즉, 복제 과정에서 &lt;strong&gt;데이터 누락&lt;/strong&gt;이 발생할 수 있다. 만약 소스 서버에서 장애가 발생하여 FailOver를 위해 레플리카를 소스 서버로 승격시킨다면, 레플리카에서 누락된 데이터를 일일이 수작업으로 찾고 반영해줘야한다.&lt;/p&gt;
&lt;h2 id=&quot;반동기-복제-방식-semi-synchronous-replication&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%98%EB%8F%99%EA%B8%B0-%EB%B3%B5%EC%A0%9C-%EB%B0%A9%EC%8B%9D-semi-synchronous-replication&quot; aria-label=&quot;반동기 복제 방식 semi synchronous replication permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;반동기 복제 방식 (Semi-synchronous replication)&lt;/h2&gt;
&lt;p&gt;비공기 복제 방식보다 데이터 동기화 문제를 더 견고하게 해결해주는 방식이다. &lt;strong&gt;소스 서버의 이벤트가 레플리카 서버에 잘 전달된 것을 확인한 뒤에서야, 소스 서버의 트랜잭션을 마무리하는 방식&lt;/strong&gt;이다. 즉, 소스 서버는 레플리카 서버가 소스 서버로부터 전달받은 이벤트를 릴레이 로그에 기록하고 &lt;code class=&quot;language-text&quot;&gt;응답(ACK)&lt;/code&gt; 을 보내면, 그때서야 트랜잭션을 커밋한다. 앞선 비동기 복제 방식과 달리 소스 서버가 트랜잭션을 커밋하고, 레플리카 서버에 복제하는 2개의 작업 모두가 한 묶음으로 같이 수행된다.&lt;/p&gt;
&lt;p&gt;하지만, 이 방식은 이벤트가 레플리카 서버의 릴레이 로그에 기록되는 것 까지만 보장한다. &lt;strong&gt;즉, 릴레이 로그의 내용이 SQL 쓰레드에 의해 레플리카 서버의 스토리지에 실제로 반영되는 것까지 확인하고 응답하지는 않는다.&lt;/strong&gt; 다시 해석하자면, 동기화 대상을 &quot;전송&quot; 하는 것까지만 보장하고 실제로 스토리지에 &quot;적용&quot; 되는 것 까지는 보장하지 않는다.&lt;/p&gt;
&lt;p&gt;이 방식 또한 어찌보면 당연하게 설계된 방식이다. 만약 각 레플리카 서버의 스토리지에 실제로 잘 적용되는 것 까지 모두 확인하고 응답하는 구조였다면, N개의 레플리카 서버를 배치한 환경이라면 하나의 SQL 문을 N+1번 실행하는 것과 같은 성능 저하를 부를 것이기 때문이다.&lt;/p&gt;
&lt;h3 id=&quot;장점-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90-1&quot; aria-label=&quot;장점 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h3&gt;
&lt;p&gt;복제 과정에서 데이터 정합성이 중요하고, 성능은 다소 떨어져도 되는 상황에 사용하기 적합하다. 레플리카 서버로부터 ACK 응답을 받은 뒤 트랜잭션을 커밋하므로 최소 하나의 이상의 레플리카 서버에는 복제가 성공했음을 보장할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;단점-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90-1&quot; aria-label=&quot;단점 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;p&gt;계속 설명했듯이, 비동기 방식에 비해 느리다. 반동기를 위한 네트워크 통신이 1번 더 발생하고, 레플리카 서버에 문제가 있어서 ACK 응답속도가 늦어질 경우 그만큼 트랜잭션 수행시간이 더 늦어진다. 이 떄문에 소스와 레플리카 서버가 물리적으로 가까이 위치한 경우에 사용하기 좋을 것이다.&lt;/p&gt;
&lt;h3 id=&quot;after_sync-vs-after_commit&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#after_sync-vs-after_commit&quot; aria-label=&quot;after_sync vs after_commit permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AFTER_SYNC vs AFTER_COMMIT&lt;/h3&gt;
&lt;p&gt;반동기 복제 또한 2가지 종류로 나뉜다. 정확히 어떤 시점에 이벤트를 레플리카 서버로 전송하고 응답을 받느냐에 따라 &lt;code class=&quot;language-text&quot;&gt;AFTER_SYNC&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;AFTER_COMMIT&lt;/code&gt; 으로 나뉜다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;AFTER_COMMIT&lt;/code&gt; : 소스 서버의 이벤트(트랜잭션)를 바이너리 로그에 기록하고, &lt;strong&gt;스토리지에 커밋해서 실제로 적용하는 것까지 모두 마친 뒤에 레플리카 서버로 이벤트를 전송&lt;/strong&gt;하는 방식이다. 커밋 이후에 복제를 진행하기 때문에 AFTER COMMIT 반동기 복제라고 부른다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;AFTER_SYNC&lt;/code&gt; : 레플리카 서버의 릴레이 로그에 정상적으로 기록이 되었음을 확인받은 뒤에야 안전하게 스토리지에 커밋하는 방식이다. 이는 동기화 이후에 트랜잭션 커밋을 수행하기 때문에 AFTER SYNC 반동기 복제하고 부른다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;AFTER COMMIT&lt;/h4&gt;
&lt;p&gt;반동기 복제 방식을 지원하기 시작한 초기에, 디폴트로 실행되던 AFTER COMMMIT 방식은 특정 장애 상황이 발생했을 때 문제가 있었다. 만약 소스 서버가 트랜잭션을 커밋한 뒤, 레플리카 서버에 이벤트를 전송하는 도중에 장애가 터졌다고 가정해보자. 문제점은 아래와 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 장애가 터진 소스 서버를 대신하여 레플리카 서버를 새로운 소스 서버로 대체했을 때, 기존 소스 서버에는 정상적으로 반영되었던 데이터 변경이 새롭게 대체된 소스 서버에는 반영되지 않은, &lt;strong&gt;의도하지 않은 롤백이 발생&lt;/strong&gt;할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;팬텀리드(Phantom Read)&lt;/code&gt; 가 발생할 수 있다. 장애가 터진 소스 서버를 즉시 고쳐서 다시 운영 환경에 투입했을 경우, 새로운 소스 서버에는 반영되어 있지 않은 데이터 변경사항이 FailOver 되어 다시 투입된 소스 서버에는 반영되어 있는 문제가 발생할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;흔치 않는 장애 발생 상황이긴 하지만, 팬텀 리드와 같은 데이터 정합성 문제가 일어날 여지가 있는 방식이 AFTER COMMIT 이다.&lt;/p&gt;
&lt;h4&gt;AFTER SYNC&lt;/h4&gt;
&lt;p&gt;위와 같은 AFTER COMMIT 의 데이터 정합성 문제를 해결하기 위해, MySQL 8.0 부터는 디폴트로 &lt;strong&gt;레플리카 서버의 릴레이 로그에 정상적으로 기록이 되었음을 확인받은 뒤에야 스토리지에 커밋하는 방식&lt;/strong&gt;을 채택하게 되었다. 앞서 설명했듯이, 이는 동기화 이후에 트랜잭션 커밋을 수행하므로 AFTER SYNC 반동기 복제라고 한다.&lt;/p&gt;
&lt;p&gt;AFTER SYNC 방식에서 앞선 장애 상황이 터졌을 때, 레플리카 서버에는 반영되지 않았지만 소스 서버에만 반영되는 일은 발생하지 않는다. 따라서 팬텀 리드가 발생하지 않는다.&lt;/p&gt;
&lt;h3 id=&quot;어떤-동기화-복제-방식을-선택해야-할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%96%A4-%EB%8F%99%EA%B8%B0%ED%99%94-%EB%B3%B5%EC%A0%9C-%EB%B0%A9%EC%8B%9D%EC%9D%84-%EC%84%A0%ED%83%9D%ED%95%B4%EC%95%BC-%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;어떤 동기화 복제 방식을 선택해야 할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어떤 동기화 복제 방식을 선택해야 할까?&lt;/h3&gt;
&lt;p&gt;데이터 정합성을 위해선 확실히 비동기 방식에 비해 반동기 방식이 유리해보인다. 하지만, 성능은 더 느릴 것이다. 과연 데이터 정합성 보장을 위해 성능을 포기하면서까지 AFTER SYNC 방식을 택하는 것이 좋을까?&lt;/p&gt;
&lt;p&gt;사실상 데이터의 레플리케이션 작업은 생각보다 매우 짧은 시간에 이루어진다. 보통의 경우 약 200ms ~ 300ms 라는 짧은 시간안에 데이터를 복제해서, 데이터 불일치로 생기는 불편함을 느낄 일이 흔히 발생하지 않는다.&lt;/p&gt;
&lt;p&gt;어떤 동기화 복제 방식을 택할지는 서비스 특성에 따라 다를 것이다. 만약 변경된 데이터를 정말 즉각적으로 읽어야하는 민감한 서비스라면, 아무래도 반동기 방식이 더 유리할 것이다. 상황에 따라 얼만큼을 성능을 고려하고, 데이터 정합성을 고려할지 생각해보자.&lt;/p&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;비동기 복제 방식 : 트랜잭션 처리 속도가 빠르지만, 레플리케이션의 성공을 반드시 보장하지 않는다.&lt;/li&gt;
&lt;li&gt;반동기 복제 방식 : 트랜잭션 처리 속도가 느리지만, 레플리카 서버로의 전송을 보장해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@backfox/%EB%AC%B4%EB%87%BD%EC%9D%B4%EC%99%80-%EC%95%8C%EC%95%84%EB%B3%B4%EB%8A%94-%EB%8C%80%EA%B7%9C%EB%AA%A8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B4%80%EB%A6%AC-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B3%B5%EC%A0%9C%ED%95%98%EA%B8%B0%EB%A6%AC%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-f4pota6h&quot;&gt;https://velog.io/@backfox/무뇽이와-알아보는-대규모-데이터-관리-데이터베이스-복제하기리플리케이션-f4pota6h&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Real MySQL 8.0 - 백은빈, 이성욱&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Redis 분산 락(Distribution Lock)을 구현하여 동시성 해결하기]]></title><description><![CDATA[분산 락(Distribution Lock) 스프링 웹 애플리케이션은 기본적으로 멀티 쓰레드 환경에서 구동된다. 이를 위해 여러 쓰레드가 함께 접근할 수 있는 공유 자원에 대한 경쟁 상태(Race Condition…]]></description><link>https://haon.site/database/redis-distribution-lock/</link><guid isPermaLink="false">https://haon.site/database/redis-distribution-lock/</guid><pubDate>Sat, 21 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;분산-락distribution-lock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%84%EC%82%B0-%EB%9D%BDdistribution-lock&quot; aria-label=&quot;분산 락distribution lock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;분산 락(Distribution Lock)&lt;/h2&gt;
&lt;p&gt;스프링 웹 애플리케이션은 기본적으로 멀티 쓰레드 환경에서 구동된다. 이를 위해 여러 쓰레드가 함께 접근할 수 있는 공유 자원에 대한 &lt;strong&gt;경쟁 상태(Race Condition)&lt;/strong&gt; 가 발생할 수 있고, &lt;strong&gt;임계영역(Critical Section)&lt;/strong&gt; 에 대한 동시성 제어 메커니즘을 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;특히나, 자바 진영에서는 &lt;strong&gt;모니터 기반의 상호 배제(mutual exclusion)&lt;/strong&gt; 기법을 제공하는 &lt;code class=&quot;language-text&quot;&gt;synchornized&lt;/code&gt; 키워드를 제공한다. 하지만, 이 키워드를 사용하면 임계영역에 대한 동시 접근을 제어할 수 있지만, 문제는 단일 서버에서만 상호 배제를 보장한다는 점이다. 우리는 이를 통해, 다중화된 분산 환경에서 여러 서버의 동시성 문제를 다루기 위한 락 메커니즘이 필요함을 느낄 수 있다.&lt;/p&gt;
&lt;p&gt;분산 락(Distribution Lock) 은 다중화된 분산 환경에서 &lt;strong&gt;상호 배제(mutual exclusion)&lt;/strong&gt; 기반의 락 메커니즘을 제공한다. 즉, 분산된 여러 서버들은 모두 동일한 락 제공처로부터 &lt;strong&gt;락을 획득하고(acquire), 해제(release)할 수 있다.&lt;/strong&gt; 이떄 분산 환경에서 제공되는 락 타입을 분산 락이라고 하며, 락 제공처는 가장 많이 사용되는 것이 Redis, Zookeeper 등이 있다. 다시 정리하자며느 분산 환경에서 여러대의 서버들은 락을 획득하기 위해 Redis 와 같은 락 제공처를 바라보며, 임계영역에 접근할 수 있는지 확인하고, 대기하고, 수행한다.&lt;/p&gt;
&lt;p&gt;이번 포스팅에서는 Redis 를 사용하여 간단히 분산 락을 구현해보도록 한다. RedLock 알고리즘에 대해서도 다루어볼까 하는데, 이는 다음 포스팅에서 천천히 다루어볼까 한다.&lt;/p&gt;
&lt;h3 id=&quot;동시성-이슈-발생-상황-가정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88-%EB%B0%9C%EC%83%9D-%EC%83%81%ED%99%A9-%EA%B0%80%EC%A0%95&quot; aria-label=&quot;동시성 이슈 발생 상황 가정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동시성 이슈 발생 상황 가정&lt;/h3&gt;
&lt;p&gt;동시성이 터질만한 애플리케이션 요구사항을 하나 만들어 볼 것이다. 우리는 대학교 수강신청 사이트에서 동시성 이슈가 터지는 상황을 하나 가정해보고, 이를 Redis 를 활용한 2가지 방법의 분산 락으로 해결해볼 것이다. 그리고 인프라 아키텍처는 간딘히 아래와 같이 구성했다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;스프링부트 애플리케이션 인스턴스 2대를 띄웠다.&lt;/li&gt;
&lt;li&gt;MySQL 서버에 수업에 대한 더미 데이터를 미리 띄웠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;course&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#course&quot; aria-label=&quot;course permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Course&lt;/h3&gt;
&lt;p&gt;수업에 대한 엔티티이다. 수업명, 수강신청 제한 수, 현재까지의 수강신청 인원 수로 구성했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Course&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GeneratedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;starategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenerationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; courseName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; countLimit&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; currentCount&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isOverFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; currentCount &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; countLimit&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;coursecontroller&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#coursecontroller&quot; aria-label=&quot;coursecontroller permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CourseController&lt;/h3&gt;
&lt;p&gt;컨트롤러는 아래와 같이 간단히 구성했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/course&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt; courseService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt; courseService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;courseService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ResponseBody&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@PostMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/registration/{courseIdx}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerCourse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@PathVariable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;courseIdx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt; registerRes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerCourse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;created&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;URI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/course/registration/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;registerRes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;courserepository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#courserepository&quot; aria-label=&quot;courserepository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CourseRepository&lt;/h3&gt;
&lt;p&gt;레포지토리는 아래와 같이 구성했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@EnableJpaRepositories&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseRespository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JpaRepository&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CourseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;CourseEntity&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;courseservice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#courseservice&quot; aria-label=&quot;courseservice permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CourseService&lt;/h3&gt;
&lt;p&gt;수강 신청의 핵심 로직을 담당하는 서비스 레이어이다. &lt;code class=&quot;language-text&quot;&gt;isOverFlow()&lt;/code&gt; 를 통해 수강신청 인원이 초과했는지를 검증하도록 간단히 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseRespository&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisLockRepository&lt;/span&gt; redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CourseRespository&lt;/span&gt;  courseRespository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisLockRepository&lt;/span&gt; redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;courseRespository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisLockRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerCourse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;CourseEntity&lt;/span&gt; courseEntity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findCourseEntityByCourseIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isOverFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;마감 되었습니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Jmeter 를 사용하여 동시간대에 100개의 요청을 보내도록 한다. 그러면 분명히 100개의 쓰레드가 요청했음에도 불구하고, currentCount 값이 100이 아닌 19밖에 증가히지 않았다. 즉, 동시성 이슈가 발생했다. 앞서 설명했듯이, 우리의 환경은 단일 서버가 아닌 분산 환경이므로 &lt;code class=&quot;language-text&quot;&gt;synchornized&lt;/code&gt; 으로도 이를 해결할 수 없다. (사실 JPA 락 메커니즘을 사용하는 것을 고려해볼 수 있지만, 현재 포스팅에선 고려 대상에서 일단 제외한다.)&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/890a17101b660926e9d6dbf640522c05/c2d9c/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.355828220858896%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABPUlEQVR42nWPyW7CMBRF8zF0IInjDLYzOJMzFRK6oUJqoaigqlI3/f/lrQkC2qIujvXu8J5kQ3VztLMeD8MCqumweHrGarPH8mWH5XqPQeu2H8Z81i9QVA3SsoLMS8hCXWGMQZYjlhlEkiBXNVpNWlTIVIOi7RDlKQpV6UxTKn20hogleBSfEXGCUHuGEAxhyMGYj8B34XsUnkbwAC4lcOwpKLHAAl97DFEUjj0WeOBac907QB0Cy7JgeEwiiAqwuIIncqRqjrJ5BE9qeDwD8WI4fqJzhSAskVY9QllfvKg8IlLYtj7oFDtM+Aa34ZGg/kA8fGEq30Z9H21xF72O84SvQct38O4TN+KycxNuQeTqeJDox7bMM5RSBIzBdSks0/yVHSDE1h1Hz9Mrf/yyqZcOw4mDPvHT/9v5L/sGbDbm+VoRruMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/890a17101b660926e9d6dbf640522c05/a6d36/image.png&quot;
        srcset=&quot;/static/890a17101b660926e9d6dbf640522c05/222b7/image.png 163w,
/static/890a17101b660926e9d6dbf640522c05/ff46a/image.png 325w,
/static/890a17101b660926e9d6dbf640522c05/a6d36/image.png 650w,
/static/890a17101b660926e9d6dbf640522c05/e548f/image.png 975w,
/static/890a17101b660926e9d6dbf640522c05/3c492/image.png 1300w,
/static/890a17101b660926e9d6dbf640522c05/c2d9c/image.png 1326w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이를 Redis 에서 제공하는 분산 락으로 해결해보자. 분산 락 구현방식에는 크게 2가지가 존재한다.&lt;/p&gt;
&lt;h2 id=&quot;redis-setnx-를-뢀용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-setnx-%EB%A5%BC-%EB%A2%80%EC%9A%A9&quot; aria-label=&quot;redis setnx 를 뢀용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis SETNX 를 뢀용&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 Redis 2.6.12 버전 이전에는 &lt;code class=&quot;language-text&quot;&gt;SETNX&lt;/code&gt; 명령어가 제공되었지만, 2.6.12 버전부터는 deprecated 되었다고 한다. SET 명령어에 NX 옵션을 전달하는 방향으로 수정하였다. 우선, &lt;code class=&quot;language-text&quot;&gt;SETNX&lt;/code&gt; 명령을 기준으로 설명한다. 이후 포스팅에서 2.6.12 이후 버전의 RedLock 알고리즘에 대해 다루어보고자 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Redis 에는 &lt;code class=&quot;language-text&quot;&gt;SETNX&lt;/code&gt; 라는 명령어가 존재한다. &quot;&lt;strong&gt;SET&lt;/strong&gt; if &lt;strong&gt;N&lt;/strong&gt;ot e&lt;strong&gt;X&lt;/strong&gt;ists&quot; 의 줄임말이다. 말 그대로 특정 key 에 value 가 존재하지 않을때만 값을 설정할 수 있다는 뜻이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token number&quot;&gt;127.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;:&lt;span class=&quot;token number&quot;&gt;6379&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; setnx &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;lock&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;127.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;:&lt;span class=&quot;token number&quot;&gt;6379&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; setnx &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;lock&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;127.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;:&lt;span class=&quot;token number&quot;&gt;6379&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; del &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;127.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;:&lt;span class=&quot;token number&quot;&gt;6379&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; setnx &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;lock&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위는 &lt;code class=&quot;language-text&quot;&gt;SETNX&lt;/code&gt; 명령어를 사용한 모습이다. Key 1 에 lock 이라는 value 를 설정하는 장면이다. 최초에는 key 에 아무값도 설정되어 있지 않기 때문에 1을 반환하며 성공한다. 그런데, 이후 동일한 명령어를 또 실행하는데, 이미 해당 key 에 대한 value 가 존재하기 때문에 0을 반환하며 실패하였다. 이후 &lt;code class=&quot;language-text&quot;&gt;DEL&lt;/code&gt; 명령어를 통해 key 1의 데이터를 가져오고, 동일하게 &lt;code class=&quot;language-text&quot;&gt;SETNX&lt;/code&gt; 명령어를 실행하니 다시 1을 반환하며 성공한다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;SETNX&lt;/code&gt; 명령을 활용하면 &lt;code class=&quot;language-text&quot;&gt;스핀 락(Spin Lock)&lt;/code&gt; 을 구현하여 동시성을 해결할 수 있다. 즉, Redis 서버에 지속적으로 &lt;code class=&quot;language-text&quot;&gt;SETNX&lt;/code&gt; 명령어를 보내서 임계 영역 진입 여부를 확인한다. 이를 위해 아래와 같이 의존성을 추가하고, 스핀 락을 구현해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;implementation &apos;org.springframework.boot:spring-boot-starter-cache&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;redisrepository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redisrepository&quot; aria-label=&quot;redisrepository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedisRepository&lt;/h3&gt;
&lt;p&gt;Redis 분산 락을 얻고 해제하는 레포지토리 컴포넌트를 간단히 생성해주었다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisLockRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisLockRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opsForValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setIfAbsent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lock&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ofMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;RedisTemplate&lt;/code&gt; 을 주입받아서 락을 관리하는 &lt;code class=&quot;language-text&quot;&gt;RedisLockRepository&lt;/code&gt; 를 구현한다. &lt;code class=&quot;language-text&quot;&gt;lock()&lt;/code&gt; 메소드는 이름 그대로 락을 획득하는 것으로, &lt;code class=&quot;language-text&quot;&gt;setIfAbsent()&lt;/code&gt; 를 사용하여 &lt;code class=&quot;language-text&quot;&gt;SETNX&lt;/code&gt; 명령어를 실행시킬 수 있다. 이때, key 는 Course 엔티티에 대한 PK 값으로, value 는 &lt;code class=&quot;language-text&quot;&gt;lock&lt;/code&gt; 으로 설정한다. 3번째 파라미터는 타임아웃 설정이다. 즉, 락을 3초 이상 획득하지 못한 쓰레드는 타임아웃된다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;unlock()&lt;/code&gt; 메소드는 key 에 대해 &lt;code class=&quot;language-text&quot;&gt;DEL&lt;/code&gt; 명령어를 실행한다. 이를 통해 락을 해제할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;courseservice-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#courseservice-1&quot; aria-label=&quot;courseservice 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CourseService&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseRespository&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisLockRepository&lt;/span&gt; redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CourseRespository&lt;/span&gt;  courseRespository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisLockRepository&lt;/span&gt; redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;courseRespository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisLockRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerCourse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;CourseEntity&lt;/span&gt; courseEntity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findCourseEntityByCourseIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isOverFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;마감 되었습니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;registerCourse()&lt;/code&gt; 에 스핀 락을 적용하였다. while 문을 활용해서 락을 획득할 때 까지 무한 반복과 대기를 한다. Redis 서버에 부하를 덜기위해 100ms 를 쉬어주고, 임계영역에 진입후 수강신청에 대한 로직을 처리후 finally 블럭으로 락을 해제해준다. 이때 락을 해제해주지 않으면 다른 쓰레드에서 임계영역에 진입하므로 주의하자.&lt;/p&gt;
&lt;p&gt;위와같은 스핀 락 방식 과연 적합한 구현 방식일까? 몰론 기본적으로 제공되는 Redis Client 인 Lettuce 만으로도 금방 락 메커니즘을 구현할 수 있기 때문에, 구현 난이도가 낮을 것이다. 하지만, 스핀 락 방식으로 동작하므로 Redis 서버와 스프링 애플리케이션 서버 모두에 심한 부하가 생길 것이다.&lt;/p&gt;
&lt;h3 id=&quot;redis-의-pubsub-기반-메시지브로커-활용-redisson-클라이언트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-%EC%9D%98-pubsub-%EA%B8%B0%EB%B0%98-%EB%A9%94%EC%8B%9C%EC%A7%80%EB%B8%8C%EB%A1%9C%EC%BB%A4-%ED%99%9C%EC%9A%A9-redisson-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8&quot; aria-label=&quot;redis 의 pubsub 기반 메시지브로커 활용 redisson 클라이언트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis 의 Pub/Sub 기반 메시지브로커 활용 (Redisson 클라이언트)&lt;/h3&gt;
&lt;p&gt;스핀 락으로 인해 발생하는 심한 부하는 사실상 서비스에 도입하기란 무모하다. 이번에는 스핀락처럼 부하가 발생하는 기능이 아닌, 더 효율적인 방법으로 구현해보자. Redis 가 제공하는 Pub/Sub 메시지브로커를 사용하여 분산 락을 구현해보자. Pub/Sub 과 관련한 내용은 &lt;a href=&quot;https://haon.blog/spring/redis-pub-sub-local-cache-synchornization/&quot;&gt;Redis Pub/Sub 을 사용한 분산 환경에서 로컬 캐시 동기화&lt;/a&gt; 에서도 다룬적이 있다.&lt;/p&gt;
&lt;p&gt;Redis 의 Pub/Sub 의 활용하면, 락을 해제할 주체자(Publisher) 가 다른 클라이언트(Subscriber) 에게 락 획득 가능 메시지를 브로드케스트 한다. 즉, 락이 해제될 때 마다 Subscriber 들에게 락 획득을 이제 시도해도 된다라는 메시지를 발행한다. 이를 통해, 앞선 &lt;code class=&quot;language-text&quot;&gt;SETNX&lt;/code&gt; 명령어 기반 스핀 락 방식과 달리 락 획득 여부를 체크하지 않아도 된다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Redisson&lt;/code&gt; 이라는 라이브러리는 Redis 의 Pub/Sub 을 활용하여 분산 락 메커니즘을 구현했다. 이 라이브러리는 타임아웃 기능 또한 제공하여, 일정 시간동안 락을 획득하지 못하면 타임아웃 되도록 처리할 수 있도록 한다. &lt;code class=&quot;language-text&quot;&gt;Redisson&lt;/code&gt; 을 사용하기 위해 아래와 같이 의존성을 추가해주자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;implementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisson&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;redisson&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.17&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.7&lt;/span&gt;&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;courseservice-2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#courseservice-2&quot; aria-label=&quot;courseservice 2 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CourseService&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;RedissonClient&lt;/code&gt; 을 아래와 같이 주입받자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseRespository&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedissonClient&lt;/span&gt; redissonClient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;courseRespository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redissonClient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redissonClient&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 아래처럼 &lt;code class=&quot;language-text&quot;&gt;redissonClient.getLock()&lt;/code&gt; 을 통해 락을 획득하고, &lt;code class=&quot;language-text&quot;&gt;tryLock()&lt;/code&gt; 을 통해 락 획득을 시도한다. 락 획득에 성공하면 임계영역에 진입하여 비즈니스 로직을 수행하고, finally 블럭에서 &lt;code class=&quot;language-text&quot;&gt;unlock()&lt;/code&gt; 한다. &lt;strong&gt;락 획득을 실패하였으면, 끊임없이 레디스 서버에 재확인하는것이 아니라 대기 상태로 들어가 메시지가 발행되기를 기다린다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerCourse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;RLock&lt;/span&gt; lock &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redissonClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; available &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tryLock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TimeUnit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SECONDS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; newCourseIdx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toIntExact&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;available&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Lock 획득 실패!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// 비즈니스 로직 (임계영역)&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;CourseEntity&lt;/span&gt; courseEntity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findCourseEntityByCourseIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newCourseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isOverFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;마감 되었습니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        lock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;jpa-락-메커니즘-vs-분산-락&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jpa-%EB%9D%BD-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98-vs-%EB%B6%84%EC%82%B0-%EB%9D%BD&quot; aria-label=&quot;jpa 락 메커니즘 vs 분산 락 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JPA 락 메커니즘 vs 분산 락&lt;/h2&gt;
&lt;p&gt;JPA 에서 제공하는 낙관적 락, 비관적 락의 주 관심사는 특정 엔티티에 대한 동시성 제어이다. 즉, 동시성을 제어히기 위한 엔티티가 존재해야한다. 반면, 분산 락은 &lt;code class=&quot;language-text&quot;&gt;synchornized&lt;/code&gt; 와 같이 임계영역에 대한 접근 제어이다. 엔티티 접근 제어가 아닌, 여러 엔티티에 대한 생성, 수정, 삽입, 삭제 그 모든 행위에 대해 접근을 제어할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;우리-서비스에서-비관적-락을-사용하는데-문제가-되진-않을까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%B0%EB%A6%AC-%EC%84%9C%EB%B9%84%EC%8A%A4%EC%97%90%EC%84%9C-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BD%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EB%8D%B0-%EB%AC%B8%EC%A0%9C%EA%B0%80-%EB%90%98%EC%A7%84-%EC%95%8A%EC%9D%84%EA%B9%8C&quot; aria-label=&quot;우리 서비스에서 비관적 락을 사용하는데 문제가 되진 않을까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;우리 서비스에서 비관적 락을 사용하는데, 문제가 되진 않을까?&lt;/h3&gt;
&lt;p&gt;가장 고민이 많았던 요소 중 하나는, 우리 모행 서비스에는 어떠한 동시성 제어 락 메커니즘을 취하는가이다. &lt;a href=&quot;https://haon.blog/database/replication-mysql-springboot/&quot;&gt;MySQL 8.0 레플리케이션과 스프링부트 DataSource 라우팅을 통한 부하 분산&lt;/a&gt; 에서도 다루었듯이, 해당 포스팅에서 다룬 내용과 거의 유사한 구조로 데이터베이스 레플리케이션 구조를 취하고 있다. 이러한 상황에서 우리 서비스는 JPA 비관적 락을 사용하는데, 다중화된 데이터베이스 환경에서 ROW 단위의 락을 걸어도 괜찮은가이다.&lt;/p&gt;
&lt;p&gt;하지만, 이는 다행히 문제가 되지 않는다. JPA 비관적 락은 &lt;code class=&quot;language-text&quot;&gt;PESSIMISTIC_WRITE&lt;/code&gt; 옵션 기준으로 쓰기 락, 베타 락을 설정한다. &lt;code class=&quot;language-text&quot;&gt;PESSIMISTIC_WRITE&lt;/code&gt; &lt;strong&gt;데이터에 변경 가능성이 있는 연산이므로, 쓰기 트랜잭션으로 간주된다는 것이다.&lt;/strong&gt; 따라서 이 &lt;code class=&quot;language-text&quot;&gt;FOR UPDATE&lt;/code&gt; 절을 사용하는 쿼리는 Master 서버로 전송된다. 우리는 &lt;code class=&quot;language-text&quot;&gt;AbstractRoutingDataSource&lt;/code&gt; 를 통해 쓰기 연산에 대해선 Master 서버로 라우팅하도록 되어있는데, 현재 Master 서버는 단 1대밖에 존재하지 않으며, Slave 서버로 전송되지 않는다. 따라서 쓰기 연산에 대한 데이터 정합성에 문제가 되지 않는다.&lt;/p&gt;
&lt;p&gt;만약 우리 서비스가 더 거대해져서 Master 서버가 2대 이상으로 커진다면, Master 서버간에 락을 공유할 방법이 없다. 즉, 각 Master 서버는 중앙화된 락 제공처를 사용하지 않으므로, 락이 각기 별개로 관리된다. 이 문제는 현재 포스팅에서 다룬 Redis 분산 락을 통해 동시성 문제를 해결해볼 수 있을 것이다. 2대 이상의 다중화된 데이터베이스 환경이라면 ROW 단위의 락 설정에 대해 잘 고려해야한다.&lt;/p&gt;
&lt;h3 id=&quot;mysql-네임드-락-vs-redis-분산-락&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mysql-%EB%84%A4%EC%9E%84%EB%93%9C-%EB%9D%BD-vs-redis-%EB%B6%84%EC%82%B0-%EB%9D%BD&quot; aria-label=&quot;mysql 네임드 락 vs redis 분산 락 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MySQL 네임드 락 vs Redis 분산 락&lt;/h3&gt;
&lt;p&gt;MySQL 의 네임드 락의 경우 &lt;code class=&quot;language-text&quot;&gt;GET_LOCK(문자열, Timeout)&lt;/code&gt; 을 통해 락을 획득하고, &lt;code class=&quot;language-text&quot;&gt;RELEASE_LOCK(문자열)&lt;/code&gt; 을 통해 락읗 해제하는 방식으로 구현할 수 있다. 또한 &lt;code class=&quot;language-text&quot;&gt;GET_LOCK&lt;/code&gt; 을 얻을 때 타임아웃 시간을 지정하게 되는데, 타임아웃 시간 내에서 동시 요청에 의한 작업들은 누락되지 않고, 정상적으로 처리되는 부분이 있다.&lt;/p&gt;
&lt;p&gt;네임드 락의 장점으로는 추가적인 리소스가 필요하지 않는다. 다른 소프트웨어를 사용하지 않고도 기존의 DB 인 MySQL 로 분산락을 구현할 수 있는 장점이 있다. 하지만 단점으로는 락에 대한 정보가 테이블에 따로 저장되어 무거워질 수 있고, 실제 DB 에 락으로 인한 커넥션 대기가 발생하기때문에 성능상 단점이 있다.&lt;/p&gt;
&lt;p&gt;그에 비해 Redis 메세지 브로커를 이용한 분산락은 락에 대한 정보는 휘발성이 있고, 메모리에서 락을 획득하고 해제하기때문에 가볍다는 장점이 있다.&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;만약 우리 모행 서비스가 분산된 환경이라면, 상황에 따라 Redis 분산 락을 적극 고려해볼 것이다. 하지만, 지금 모행 서비스는 단일 서버인 점을 감안한다면 Redis 분산 락을 도입할 이유가 없다. 또한, JPA 의 비관적 락 만으로도 엔티티에 대한 충분히 동시성을 해결할 수 있다. ROW 단위에 대해서만 락을 거는 것에 비해 상호 배제 기반의 분산 락을 도입하여 불필요하게 성능 저하를 일으킬 이유는 없다.&lt;/p&gt;
&lt;p&gt;다음 포스팅에선 Redis 의 RedLock 알고리즘에 대해 간단히 학습하도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dkswnkk.tistory.com/681&quot;&gt;https://dkswnkk.tistory.com/681&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hyos-dev-log.tistory.com/34&quot;&gt;https://hyos-dev-log.tistory.com/34&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hyperconnect.github.io/2019/11/15/redis-distributed-lock-1.html&quot;&gt;https://hyperconnect.github.io/2019/11/15/redis-distributed-lock-1.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://techblog.woowahan.com/2631/&quot;&gt;https://techblog.woowahan.com/2631/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[하모니 팀의 Jenkins와 Nginx를 활용한 Blue/Green 무중단 배포 도입기]]></title><description><![CDATA[💡 현재 포스트는 하모니 팀 기술 블로그 에도 게시된 글 입니다. 기존 배포 방식에서 Downtime…]]></description><link>https://haon.site/deployment/blue-green/</link><guid isPermaLink="false">https://haon.site/deployment/blue-green/</guid><pubDate>Sat, 21 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/blue-green/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 에도 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;기존-배포-방식에서-downtime이-발생하는-문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EC%A1%B4-%EB%B0%B0%ED%8F%AC-%EB%B0%A9%EC%8B%9D%EC%97%90%EC%84%9C-downtime%EC%9D%B4-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;기존 배포 방식에서 downtime이 발생하는 문제점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기존 배포 방식에서 Downtime이 발생하는 문제점&lt;/h2&gt;
&lt;p&gt;기존의 모행 배포 파이프라인 스크립트는 아래와 같다. 우선 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 구동중인 애플리케이션을 확인하고 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 구동중인 애플리케이션이 존재한다면 종료시킨다. &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 그리고 이후 jar 파일을 실행하여, 신버전 스프링부트 애플리케이션이 구동된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;z &lt;span class=&quot;token string&quot;&gt;&quot;$CURRENT_PID&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
        echo &lt;span class=&quot;token string&quot;&gt;&quot;구동중인 애플리케이션이 없으므로 종료하지 않습니다.&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
        echo &lt;span class=&quot;token string&quot;&gt;&quot;구동중인 애플리케이션을 종료합니다.&quot;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt; $CURRENT_PID
fi

sudo &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;E nohup java &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;jar &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;home&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ubuntu&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;myproject&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;deploy&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;core&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;SNAPSHOT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jar &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 문제점이 있다. 과정 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 에서 &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 으로 넘어갈 때, 우리 서비스가 일시적으로 중단되어 이용 불가능한 상태가 된다. 즉, 구버전 프로세스를 종료하고 새로운 프로세스를 실행하는 과정에서 서비스가 중단된다. 실제로 우리 서비스 다운타임을 측정한 결과, 약 7초정도의 다운타임이 발생하는 것을 확인할 수 있었다.&lt;/p&gt;
&lt;h2 id=&quot;무중단-배포-전략은-왜-필요할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC-%EC%A0%84%EB%9E%B5%EC%9D%80-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;무중단 배포 전략은 왜 필요할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;무중단 배포 전략은 왜 필요할까?&lt;/h2&gt;
&lt;p&gt;서비스가 새로운 버전이 배포될 때 마다
일시적으로 사용할 수 없다면 누가 해당 서비스를 마음놓고 사용할까? 무엇보다 우리 팀처럼 애자일하게 자주 배포하는 환경에서는 더욱이 서비스 중단이 자주 발생한다. &lt;strong&gt;간혈적인 서비스 중단은 유저 이탈로 이어지게 될 것이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;따라서 24시간 서비스를 중단 없이 사용할 수 있도록 배포 환경을 구축해야한다. 이렇게 일시적으로 서비스를 이용 불가능한 시간을 &lt;strong&gt;다운타임(Downtime)&lt;/strong&gt; 이라고 하며, 다운타임이 발생하지 않도록 배포하는 전략을 &lt;strong&gt;무중단 배포(Zero-Downtime Deployment)&lt;/strong&gt; 전략이라고 한다. 지금부터 우리 모행 서비스에 어떻게 무중단 배포 전략을 구현했으며, 기존에 발생한 다운타임을 제거했는지 소개해보고자 한다.&lt;/p&gt;
&lt;h2 id=&quot;우리-서비스에-어떤-무중단-배포-전략을-취했을까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%B0%EB%A6%AC-%EC%84%9C%EB%B9%84%EC%8A%A4%EC%97%90-%EC%96%B4%EB%96%A4-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC-%EC%A0%84%EB%9E%B5%EC%9D%84-%EC%B7%A8%ED%96%88%EC%9D%84%EA%B9%8C&quot; aria-label=&quot;우리 서비스에 어떤 무중단 배포 전략을 취했을까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;우리 서비스에 어떤 무중단 배포 전략을 취했을까?&lt;/h2&gt;
&lt;h3 id=&quot;롤링rolling-배포&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%A4%EB%A7%81rolling-%EB%B0%B0%ED%8F%AC&quot; aria-label=&quot;롤링rolling 배포 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;롤링(Rolling) 배포&lt;/h3&gt;
&lt;p&gt;롤링(Rolling) 배포 전략이란, 각 서버에서 구동중인 애플리케이션을 점진적으로 구버전에서 새로운 버전으로 차근차근 하나씩 전환하며 바꾸는 방식을 뜻한다.&lt;/p&gt;
&lt;p&gt;롤링 배포 전략의 경우, EC2 인스턴스 개수가 제한된 환경에서 사용하기 좋은 전략이다. 다만, 배포가 진행중인 시점에는 유저의 트래픽이 구버전과 신버전으로 동시에 향하게 되므로 &lt;strong&gt;정합성 이슈&lt;/strong&gt;가 발생할 수 있으므로 제외하였다.&lt;/p&gt;
&lt;h3 id=&quot;카나리canary-배포&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B9%B4%EB%82%98%EB%A6%ACcanary-%EB%B0%B0%ED%8F%AC&quot; aria-label=&quot;카나리canary 배포 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;카나리(Canary) 배포&lt;/h3&gt;
&lt;p&gt;다음으로 카나리(Canary) 배포 전략이란, 소수 인원에 대해서만 트래픽을 새로운 버전에 옮겨둔 상태에서 서비스를 운영하는 전략이다. 새로운 버전에 이상이 없다고 판단하였을 경우에 신버전에 대해 트래픽 유입율을 점진적으로 올려간다. A/B 테스트를 하기에 적합하다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;대규모 트래픽이 유입되고, 팀 규모가 큰 서비스들에선 카나리 배포 전략을 취하는 경우가 많다.&lt;/strong&gt; 다만, 우리 서비스는 카나리 배포 전략을 도입할 만큼 서비스 규모와 팀의 규모가 아직은 크지 않다. 카나리 배포의 취지는, 신버전에서 발생할 오류를 조기에 감지하고, 문제가 발생하면 빠르게 롤백하는 방식이다. 다만, 우리는 &lt;strong&gt;트래픽을 점진적으로 옮겨가며 배포하며 사전 오류를 감지할 즉시 대응할 정도로 민감하지 않아, 이 또한 제외했다.&lt;/strong&gt; 또한 팀의 규모가 크지않다. QA, CS 담담자등이 존재하지 않기에, 사전 테스팅 및 즉각 오류 감지에 대해 관리할 정도로 규모가 있지 않기에, 되려 카나리 배포 도입 근거가 옅어지기에 제외하였다. 다만, 향후 서비스와 팀의 규모가 커진다면 카니리 배포로 전환하는 하는 것이 매우 좋아보인다.&lt;/p&gt;
&lt;h3 id=&quot;블루그린bluegreen-배포&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%94%EB%A3%A8%EA%B7%B8%EB%A6%B0bluegreen-%EB%B0%B0%ED%8F%AC&quot; aria-label=&quot;블루그린bluegreen 배포 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;블루그린(Blue/Green) 배포&lt;/h3&gt;
&lt;p&gt;블루그린(Blue/Green) 배포 전략이란, 현재 운영중인 서비스를 종료하지 않고 새로운 운영 환경을 만든 뒤 트래픽을 한 번에 신버전으로 옮기는 방식이다. 마치 빅뱅 배포 방식이라고 보면 된다.&lt;/p&gt;
&lt;p&gt;우리 팀은 블루그린 배포 전략을 취했다. 블루그린 배포는 어떠한 장점이 있을까? 우선 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 블루그린 배포는 롤링 배포와 달리 한번에 트래픽을 모두 새로운 버전으로 옮기기 때문에 &lt;strong&gt;호환성 문제가 발생하지 않는다.&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 또한, 카나리 배포에 비해 우리 서비스처럼 아직 &lt;strong&gt;서비스 규모가 크지 않은 경우에 구성하기 매우 편리하고 적합&lt;/strong&gt;한 배포 전략이다. &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 무엇보다 포트(port) 를 스위칭하는 배포 전략을 취한다면, 서버를 증설하지 않고도 충분히 무중단 배포가 가능하다. 다시말해, 상대적으로 &lt;strong&gt;저렴한 서버 비용&lt;/strong&gt;으로 무중단 배포를 충분히 구현할 수 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;다만, 향후 서비스와 팀의 규모가 확장된다면 카나리 배포 전략을 고려해보는 것도 좋은 선택안 일 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;bluegreen-배포-파이프라인-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bluegreen-%EB%B0%B0%ED%8F%AC-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;bluegreen 배포 파이프라인 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Blue/Green 배포 파이프라인 구현&lt;/h2&gt;
&lt;p&gt;지금부터, 우리 서비스에서 구현한 블루그린 배포 전략 파이프라인 스크립트를 구현해보겠다. 그를 통해 무중단 배포가 어떻게 동작하는지 소개해볼까 한다. 우리 팀은 8080 포트와 8081 포트를 번갈아 스위칭하는 방식으로 무중단 배포 전략을 사용하였으며, 내부 동작 과정을 정리하면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b2a46752b8075aafadc2ea7032c8e1d9/9685e/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.282208588957054%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACQ0lEQVR42qVTzY7SUBjtA5r4CK7c+g5uZ+PKGWMyiwmiRoYYw8TJMGDEaIUAHajAAKWl0FJoS8tfoRQKx3uvMDJRV57m9C/fPTnfud/l8Bdst1tGis1mg/V6zRiGIftmNeTahITrDcJ1yEjBsYWEa1L8vtSE2Hf3suzueR46nQ5aioxGowFZljGfe7AtG2JDQNduoWvKULQWgmBFBHfLqf6TeAbH1zzklgSJLHRdF7PZDI41hFJrQigW0Ww2YVkm7IGDOH+KuHCCovIFak/GYrEA11YUCIKAXDYLlbzbuobLsxcw1DZzZQ1MZKUbRMWPaHfa0DWdCFrMudpREYlGcB4/Z45pHFyjUUc6lUIymcRtvY5b8Qciz0/g9HowzAFMzcBLPoZH10/x9eY7ykIJLbnFWu92NKz8EP7cR7AMWO7clLRUKovwl0tWVK1WURJFlAlt28bcm+NN7C2Ojp+hmC9gGQSYTCawzSHylW+oGKSzYR11VYQ388BJkoRX0dfoEUc0rzpxGYvFUKlUoOs6fN/HlAiMR2OMRiOMx2MiuoTZt5DOX+D06giJzDs01dtfGeqahnQ6RdwpdyNCRfbPfr+PfKGAbC6HHCHP8+zfknTk2C4+pT4jkbgg2fZ+j83IdYigBNM0783ifmxoFzQKusO1Wo05Oaw5BBcafVydfYAxWe2K8IfoIRaLOWt7Op3CIUZoDNRIQLJlgtO2jAcPHyNTs3cnY4t/nR4KKjYYDNjG0PFxHAeGYdy5ZoMdbO47+x/8BLxCecfe7g29AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/b2a46752b8075aafadc2ea7032c8e1d9/a6d36/image.png&quot;
        srcset=&quot;/static/b2a46752b8075aafadc2ea7032c8e1d9/222b7/image.png 163w,
/static/b2a46752b8075aafadc2ea7032c8e1d9/ff46a/image.png 325w,
/static/b2a46752b8075aafadc2ea7032c8e1d9/a6d36/image.png 650w,
/static/b2a46752b8075aafadc2ea7032c8e1d9/e548f/image.png 975w,
/static/b2a46752b8075aafadc2ea7032c8e1d9/3c492/image.png 1300w,
/static/b2a46752b8075aafadc2ea7032c8e1d9/9685e/image.png 1336w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 깃허브가 Jenkins에 Webhook 요청을 전송한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; Jenkins는 새로운 버전 코드를 pull 해오고 jar 파일을 빌드한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; Jenkins는 구버전 프로세스가 8080 포트로 실행되고 있는지, 또는 8081 포트로 실행되고 있는지 확인한다. 이를 위해, Blue 서버에 Health Check 를 시도한다. Blue 가 살아있다면 신버전을 Green 에 배포하면 되고, 살아있지 않다면 Blue 에 배포한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; (구버전 프로세스가 8080 포트로 실행되고 있다는 가정하에) Green 서버인 8081 포트로 신버전 프로세스를 배포한다. Green 서버에 맨 처음 빌드해둔 jar 파일을 전송하고, 신버전 애플리케이션을 구동시킨다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; 지속적으로 8081 포트로 헬스체크를 시도하여, 신버전 애플리케이션이 제대로 구동되었다면 &lt;code class=&quot;language-text&quot;&gt;(6)&lt;/code&gt; 으로 넘어간다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(6)&lt;/code&gt; Nginx 의 리버스 프록시 설정을 변경하여 8080 으로 프록싱하던 설정을 8081로 변경하고 Reload 한다. 즉, Nginx 프록시 방향을 Blue 에서 Green 으로 스위칭한다. 이렇게 되면 앞으로 모든 클라이언트의 트래픽이 신버전 애플리케이션으로 향한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(8)&lt;/code&gt; 곧바로 8080 포트로 구동중이던 구버전 프로세스 (Blue 서버) 를 kill 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;nginx-리버스-프록시-환경-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EB%A6%AC%EB%B2%84%EC%8A%A4-%ED%94%84%EB%A1%9D%EC%8B%9C-%ED%99%98%EA%B2%BD-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;nginx 리버스 프록시 환경 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 리버스 프록시 환경 설정&lt;/h2&gt;
&lt;p&gt;본격적인 젠킨스 파이프라인 구성에 앞서, Nginx 에 대한 설정이 필요하다. 이를 간략히 살펴보자.&lt;/p&gt;
&lt;h3 id=&quot;sites-enabled&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sites-enabled&quot; aria-label=&quot;sites enabled permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;sites-enabled&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;server {
    listen &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    include &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;d&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;service&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;inc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    location &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; {
        proxy_pass $Service_url&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header X&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Real&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;IP $remote_addr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header X&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Forwarded&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;For&lt;/span&gt; $proxy_add_x_forwarded_for&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header Host $http_host&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 설정을 보면 &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; 라는 지시어를 볼 수 있다. 이는 외부에서 설정파일을 불러올 수 있는 Nginx의 기능이다. 또한 &lt;code class=&quot;language-text&quot;&gt;$service_url&lt;/code&gt; 이라는 URL 로 리버스 프록시 요청을 보내는 것을 볼 수 있다. 이 변수에는 service-url.inc 파일로부터 변수 값이 할당된다.&lt;/p&gt;
&lt;h3 id=&quot;service-urlinc&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#service-urlinc&quot; aria-label=&quot;service urlinc permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;service-url.inc&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; $service_url https:&lt;span class=&quot;token comment&quot;&gt;//111.111.111:8080;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;기본적으로 Blue 인스턴스의 포트번호 기반으로 초기 변수값을 설정해두었으나, Green 서버로 설정해도 상관없을 것이다. 젠킨스가 이 파일을 직접 수정하여 리버스 프록시 방향을 바꿔줄 것이다.&lt;/p&gt;
&lt;h2 id=&quot;jenkins-무중단-배포-스크립트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jenkins-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8&quot; aria-label=&quot;jenkins 무중단 배포 스크립트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jenkins 무중단 배포 스크립트&lt;/h2&gt;
&lt;p&gt;아래는 새롭게 개선한 젠킨스 무중단 배포 스크립트이다. stage &lt;code class=&quot;language-text&quot;&gt;Github&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;Build&lt;/code&gt; 는 깃허브에서 신규 버전의 소스 코드를 가져와 애플리케이션을 빌드하는 과정이다. 핵심은 stage &lt;code class=&quot;language-text&quot;&gt;Deployment&lt;/code&gt; 의 쉘 스크립트 내용이다. 이를 자세히 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;pipeline {
    agent &lt;span class=&quot;token keyword&quot;&gt;any&lt;/span&gt;
    stages {
        stage&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Github&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; {
            steps {
                git branch: &lt;span class=&quot;token string&quot;&gt;&apos;develop-backend&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; url: &lt;span class=&quot;token string&quot;&gt;&apos;https://github.com/kakaotech-25/moheng.git&apos;&lt;/span&gt;
            }
        }
        stage&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Build&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; {
            steps {
                sh &lt;span class=&quot;token string&quot;&gt;&quot;./gradlew bootJar&quot;&lt;/span&gt;
            }
        }

        stage&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Deployment&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; {
            steps {
                sshagent &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;credentials: &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;key-jenkins&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; {
                    sh &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&apos;#!/bin/bash
                        if curl -s &quot;http://${BACKEND_PUBLIC_DEV_IP}:${BLUE_PORT}&quot; &gt; /dev/null
                        then
                            deployment_target_port=${GREEN_PORT}
                        else
                            deployment_target_port=${BLUE_PORT}
                        fi

                        scp -o StrictHostKeyChecking=no ./build/libs/core-0.0.1-SNAPSHOT.jar root@${BACKEND_DEV_IP}:/home/ubuntu
                        ssh root@${BACKEND_DEV_IP} &quot;nohup java -jar -Dserver.port=${deployment_target_port} /home/ubuntu/core-0.0.1-SNAPSHOT.jar &gt; /dev/null &amp;amp;&quot; &amp;amp;


                        for retry_count in \$(seq 5)
                        do
                          if curl -s &quot;http://${BACKEND_DEV_IP}:${deployment_target_port}
                          then
                              echo &quot;✅ Health Checking 에 성공했습니다! 포트 번호: ${deployment_target_port}&quot;
                              break
                          fi

                          if [ $retry_count -eq 10 ]
                          then
                            echo &quot;❌ Health checking 에 실패했습니다.&quot;
                            exit 1
                          fi

                          echo &quot;🏥 10초후에 다시 Health Checking 이 시도될 예정입니다.&quot;
                          sleep 10
                        done

                        ssh root@${nginx_ip} &quot;echo &apos;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; \\\$service_url http:&lt;span class=&quot;token comment&quot;&gt;//${BACKEND_DEV_IP}:${deployment_target_port};&apos; &gt; /etc/nginx/conf.d/service-url.inc &amp;amp;&amp;amp; service nginx reload&quot;&lt;/span&gt;
                        echo &lt;span class=&quot;token string&quot;&gt;&quot;Switch the reverse proxy direction of nginx to ${deployment_target_port} 🔄&quot;&lt;/span&gt;

                        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${deployment_target_ip}&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${blue_port}&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                        &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
                            ssh root&lt;span class=&quot;token variable&quot;&gt;@$&lt;/span&gt;{BACKEND_DEV_IP} &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k ${GREEN_PORT}/tcp&quot;&lt;/span&gt;
                        &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
                            ssh root&lt;span class=&quot;token variable&quot;&gt;@$&lt;/span&gt;{BACKEND_DEV_IP} &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k ${BLUE_PORT}/tcp&quot;&lt;/span&gt;
                        fi
                        echo &lt;span class=&quot;token string&quot;&gt;&quot; ✅ 구버전 프로세스를 종료하고, 신버전 프로세스로 교체합니다.&quot;&lt;/span&gt;
                    &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&apos;
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;1-blue-서버-health-check-및-배포할-포트번호-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-blue-%EC%84%9C%EB%B2%84-health-check-%EB%B0%8F-%EB%B0%B0%ED%8F%AC%ED%95%A0-%ED%8F%AC%ED%8A%B8%EB%B2%88%ED%98%B8-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;1 blue 서버 health check 및 배포할 포트번호 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Blue 서버 Health Check 및 배포할 포트번호 설정&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; curl &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;s &lt;span class=&quot;token string&quot;&gt;&quot;http://${BACKEND_DEV_IP}:${BLUE_PORT}&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;dev&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;null&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    deployment_target_port&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;${GREEN_PORT}
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
    deployment_target_ip&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;${BLUE_PORT}
fi&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Blue 서버에서 애플리케이션이 구동중인지 &lt;code class=&quot;language-text&quot;&gt;curl&lt;/code&gt; 로 헬스체킹한다. &lt;code class=&quot;language-text&quot;&gt;-s&lt;/code&gt; 옵션은 slient 옵션으로, 프로그래스 바가 출력되지 않도록 한다. &lt;code class=&quot;language-text&quot;&gt;/dev/null&lt;/code&gt; 은 리눅스에서 null device를 나타내며, 이곳으로 쓰여진 데이터를 마치 블랙홀처럼 공허로 버려진다. 리다이렉션 &quot;&gt;&quot; 을 통해 &lt;code class=&quot;language-text&quot;&gt;curl&lt;/code&gt; 의 표준 출력을 버리기 위해 사용한다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;curl&lt;/code&gt; 을 통한 헬스체킹 결과에 따라, Blue 가 살아있다면 &lt;code class=&quot;language-text&quot;&gt;deployment_target_port&lt;/code&gt; 를 &lt;code class=&quot;language-text&quot;&gt;${GREEN_PORT}&lt;/code&gt; 로 설정하고, 죽어있다면 &lt;code class=&quot;language-text&quot;&gt;${BLUE_PORT}&lt;/code&gt; 로 설정한다.&lt;/p&gt;
&lt;h3 id=&quot;2-jar-파일-전송-및-새로운-포트-번호로-애플리케이션-실행&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-jar-%ED%8C%8C%EC%9D%BC-%EC%A0%84%EC%86%A1-%EB%B0%8F-%EC%83%88%EB%A1%9C%EC%9A%B4-%ED%8F%AC%ED%8A%B8-%EB%B2%88%ED%98%B8%EB%A1%9C-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%8B%A4%ED%96%89&quot; aria-label=&quot;2 jar 파일 전송 및 새로운 포트 번호로 애플리케이션 실행 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. jar 파일 전송 및 새로운 포트 번호로 애플리케이션 실행&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;scp &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;o &lt;span class=&quot;token class-name&quot;&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;no &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;/build&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;libs&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;core&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SNAPSHOT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jar root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BACKEND_DEV_IP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;home&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ubuntu
ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BACKEND_DEV_IP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nohup java -jar -Dserver.port=${deployment_target_port} /home/ubuntu/core-0.0.1-SNAPSHOT.jar &gt; /dev/null &amp;amp;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;scp&lt;/code&gt; 명령으로 &lt;code class=&quot;language-text&quot;&gt;$BACKEND_DEV_IP&lt;/code&gt; 인스턴스에 jar 파일을 전송하고, &lt;code class=&quot;language-text&quot;&gt;ssh&lt;/code&gt; 명령으로 해당 파일을 실행하여 애플리케이션을 구동한다. 이때, 구동할 애플리케이션 포트 번호로 &lt;code class=&quot;language-text&quot;&gt;${deployment_target_port}&lt;/code&gt; 를 지정한다.&lt;/p&gt;
&lt;h3 id=&quot;3-주기적인-health-check-로-새로운-포트에-애플리케이션이-잘-떴는지-확인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%EC%A3%BC%EA%B8%B0%EC%A0%81%EC%9D%B8-health-check-%EB%A1%9C-%EC%83%88%EB%A1%9C%EC%9A%B4-%ED%8F%AC%ED%8A%B8%EC%97%90-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%B4-%EC%9E%98-%EB%96%B4%EB%8A%94%EC%A7%80-%ED%99%95%EC%9D%B8&quot; aria-label=&quot;3 주기적인 health check 로 새로운 포트에 애플리케이션이 잘 떴는지 확인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 주기적인 Health Check 로 새로운 포트에 애플리케이션이 잘 떴는지 확인&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; retry_count &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; \$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;seq &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; curl &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;s &lt;span class=&quot;token string&quot;&gt;&quot;http://${BACKEND_DEV_IP}:${deployment_target_port}&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;dev&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    echo &lt;span class=&quot;token string&quot;&gt;&quot;✅ Health Checking 에 성공했습니다!&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;
  fi

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; $retry_count &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;eq &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    echo &lt;span class=&quot;token string&quot;&gt;&quot;❌ Health checking 에 실패했습니다.&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
  fi

  echo &lt;span class=&quot;token string&quot;&gt;&quot;🏥 10초후에 다시 Health Checking 이 시도될 예정입니다.&quot;&lt;/span&gt;
  sleep &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
done&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;최대 10번, 10초 간격으로 &lt;code class=&quot;language-text&quot;&gt;${deployment_target_port}&lt;/code&gt; 포트 번호를 가진 새로운 인스턴스에 &lt;code class=&quot;language-text&quot;&gt;curl&lt;/code&gt; 을 통해 Health Check 를 시도한다. 10번안에 Health Check 에 성공했다면, 다음 과정으로 넘어간다. 반대로 실패했다면 &lt;code class=&quot;language-text&quot;&gt;exit&lt;/code&gt; 로 스크립트 실행을 중단한다.&lt;/p&gt;
&lt;h3 id=&quot;4-nginx-리버스-프록시-방향-변경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-nginx-%EB%A6%AC%EB%B2%84%EC%8A%A4-%ED%94%84%EB%A1%9D%EC%8B%9C-%EB%B0%A9%ED%96%A5-%EB%B3%80%EA%B2%BD&quot; aria-label=&quot;4 nginx 리버스 프록시 방향 변경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Nginx 리버스 프록시 방향 변경&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;ssh root&lt;span class=&quot;token variable&quot;&gt;@$&lt;/span&gt;{nginx_ip} &lt;span class=&quot;token string&quot;&gt;&quot;echo &apos;set \\\$service_url http://${BACKEND_DEV_IP}:${deployment_target_port};&apos; &gt; /etc/nginx/conf.d/service-url.inc &amp;amp;&amp;amp; service nginx reload&quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&quot;Switch the reverse proxy direction of nginx to ${deployment_target_port} 🔄&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞선 헬스체킹에 성공했다면, &lt;code class=&quot;language-text&quot;&gt;$service_url&lt;/code&gt; 에 새로운 포트로 구동된 주소값을 할당하여 Nginx 가 새로운 프로세스를 바라볼 수 있도록 한다. 이후 Nginx 설정을 Reload 하여 설정을 반영한다.&lt;/p&gt;
&lt;h3 id=&quot;5-구버전-프로세스-kill&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-%EA%B5%AC%EB%B2%84%EC%A0%84-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-kill&quot; aria-label=&quot;5 구버전 프로세스 kill permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. 구버전 프로세스 Kill&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${deployment_target_port}&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${blue_port}&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    ssh root&lt;span class=&quot;token variable&quot;&gt;@$&lt;/span&gt;{BACKEND_DEV_IP} &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k ${GREEN_PORT}/tcp&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
    ssh root&lt;span class=&quot;token variable&quot;&gt;@$&lt;/span&gt;{BACKEND_DEV_IP} &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k ${BLUE_PORT}/tcp&quot;&lt;/span&gt;
fi

echo &lt;span class=&quot;token string&quot;&gt;&quot; ✅ 구버전 프로세스를 종료하고, 신버전 프로세스로 교체합니다.&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;배포된 서버가 &lt;code class=&quot;language-text&quot;&gt;$BLUE_PORT&lt;/code&gt; 라면 구버전인 &lt;code class=&quot;language-text&quot;&gt;$GREEN_PORT&lt;/code&gt; 서버의 프로세스를 Kill 한다. (그 반대라면 &lt;code class=&quot;language-text&quot;&gt;$BLUE_PORT&lt;/code&gt; 서버의 프로세스를 죽인다.) &lt;code class=&quot;language-text&quot;&gt;fuser&lt;/code&gt; 를 사용하면 특정 포트를 점유하고 있는 프로세스를 죽일 수 있다. &lt;code class=&quot;language-text&quot;&gt;-k&lt;/code&gt; 옵션을 붙여야 프로세스를 죽일 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;추가-개선점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B6%94%EA%B0%80-%EA%B0%9C%EC%84%A0%EC%A0%90&quot; aria-label=&quot;추가 개선점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;추가 개선점&lt;/h2&gt;
&lt;p&gt;이렇게 블루그린을 통해 우리 팀의 무중단 배포 전략을 직접 구현해보았다. &lt;strong&gt;기존의 7초 가량의 다운타임이 약 0.03초로 크게 감소&lt;/strong&gt;했다.&lt;/p&gt;
&lt;p&gt;다만, 이때 0.03초의 다운타임은 왜 발생하는 것인지, 완벽히 제거하는 방법이 없는지와 관련한 궁금증이 생길 것이다. 이는 향후 별도의 포스팅에서 다루어볼까 한다. 이만 포스팅을 마쳐본다 😎&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jenkins.io/doc/book/using/using-credentials/&quot;&gt;https://www.jenkins.io/doc/book/using/using-credentials/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nginxstore.com/blog/ci-cd/jenkins-%EB%A5%BC-%ED%86%B5%ED%95%9C-nginx-%EA%B5%AC%EC%84%B1-%EB%B3%80%EA%B2%BD-%EB%B0%8F-%EB%B0%B0%ED%8F%AC-%EC%9E%90%EB%8F%99%ED%99%94-%EA%B5%AC%ED%98%84/&quot;&gt;https://nginxstore.com/blog/ci-cd/jenkins-%EB%A5%BC-%ED%86%B5%ED%95%9C-nginx-%EA%B5%AC%EC%84%B1-%EB%B3%80%EA%B2%BD-%EB%B0%8F-%EB%B0%B0%ED%8F%AC-%EC%9E%90%EB%8F%99%ED%99%94-%EA%B5%AC%ED%98%84/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://royleej9.tistory.com/m/entry/Jenkins-SSH-%EC%82%AC%EC%9A%A9-pipeline-SSH-Agent&quot;&gt;https://royleej9.tistory.com/m/entry/Jenkins-SSH-%EC%82%AC%EC%9A%A9-pipeline-SSH-Agent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@sileeee/scp%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-%EC%9B%90%EA%B2%A9-%EC%84%9C%EB%B2%84%EC%97%90-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot;&gt;https://velog.io/@sileeee/scp사용하여-원격-서버에-배포하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;#x26;blogId=eyeballss&amp;#x26;logNo=220881562246&quot;&gt;https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;#x26;blogId=eyeballss&amp;#x26;logNo=220881562246&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://investechnews.com/ssh-permission-denied-publickey-%EC%A0%91%EC%86%8D-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0/&quot;&gt;https://investechnews.com/ssh-permission-denied-publickey-%EC%A0%91%EC%86%8D-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Redis 는 왜 싱글 쓰레드인데 성능이 빠를까?]]></title><description><![CDATA[아직 제대로 지식이 체화된 것 같지는 않지만, 이해도를 더 확보하기 위해 짧게나마 글을 작성해본다. 특히 멀티플렉싱이 아직 추가 학습이 필요해보이지만, 우선 글을 쓰며 이해도를 보다 높인 후 향후 더 자세히 추가 학습해보도록 한다. Redis…]]></description><link>https://haon.site/redis/single-thread-fast/</link><guid isPermaLink="false">https://haon.site/redis/single-thread-fast/</guid><pubDate>Fri, 20 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;아직 제대로 지식이 체화된 것 같지는 않지만, 이해도를 더 확보하기 위해 짧게나마 글을 작성해본다. 특히 멀티플렉싱이 아직 추가 학습이 필요해보이지만, 우선 글을 쓰며 이해도를 보다 높인 후 향후 더 자세히 추가 학습해보도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;redis&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis&quot; aria-label=&quot;redis permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis&lt;/h2&gt;
&lt;p&gt;Redis 를 떠올리면 인메모리, key-value, 빠른 성능, 싱글 쓰레드등의 키워드가 떠오는다. 그런데 왜 싱글 쓰레드인데 성능이 빠른 것일까?&lt;/p&gt;
&lt;h3 id=&quot;redis-는-싱글-쓰레드와-멀티-쓰레드를-함께-사용한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-%EB%8A%94-%EC%8B%B1%EA%B8%80-%EC%93%B0%EB%A0%88%EB%93%9C%EC%99%80-%EB%A9%80%ED%8B%B0-%EC%93%B0%EB%A0%88%EB%93%9C%EB%A5%BC-%ED%95%A8%EA%BB%98-%EC%82%AC%EC%9A%A9%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;redis 는 싱글 쓰레드와 멀티 쓰레드를 함께 사용한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis 는 싱글 쓰레드와 멀티 쓰레드를 함께 사용한다.&lt;/h3&gt;
&lt;p&gt;이번에 처음 알았던 사실인데, Redis 는 싱글 쓰레드로 동작하되, 일부 기능에서 멀티 쓰레드 방식으로 동작한다. 클라이언트로부터 전송된 네트워크를 읽는 부분과 전송하는 부분은 멀티 쓰레드로 구현되어 있다. 아래 사진을 보면 알 수 있듯이, 멀티플렉싱(Multiplexing) 방식으로 클라이언트의 요청을 받아 이벤트 루프(Event Loop) 에서 싱글 쓰레드로 처리한다. 즉, 우리가 Redis 에 요청한 명령을 실행하는 부분은 싱글 쓰레드로 구현되어 있다. 이 때문에 싱글 쓰레드의 장점인 Atomic 연산이 가능해진다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0a6eb12b788bab59a881a123b3e1fb4b/229ad/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 35.58282208588957%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABUUlEQVR42oWR22rbQBRF9f9f0r6FhlKapz60BHIPseNcqgQ7lmTZkgZJM9JcNFod26GhIdADm3OeFnvvE1lradsWKSXDMOCcwzhDb3p62+P9wPsZX/XRRF3XEccxi8UCrTXWGB6yez5//8TXX4dI25CWhptngQ1sZyT96haVXKObZUB4zHpGFZ8w1HOirbM8zymKgrIsaZuWu3TG+fyUi8UZtRY8ZZrjaU5nwCiBePjBZnJEU86CU4d8+snL6QFuPSHaxl0ul2RZtoNWVRWAU27zyU6iK3HBmTL+Ne4+7OgD3euwgwYTtt1H3jpMkoTVarWDNXXD9OWGq+SC6/SSQhZ7wDj+2+FmSnr2BTH5xih+/+06UkohhNhp26e1jrxOeS4fmVcxspe0yiFaTafdG1StUfk9evMIun57ykefGkePCxEG7/Dh7s2ADh+xzvO/+QMIhxegXTROnwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/0a6eb12b788bab59a881a123b3e1fb4b/a6d36/image.png&quot;
        srcset=&quot;/static/0a6eb12b788bab59a881a123b3e1fb4b/222b7/image.png 163w,
/static/0a6eb12b788bab59a881a123b3e1fb4b/ff46a/image.png 325w,
/static/0a6eb12b788bab59a881a123b3e1fb4b/a6d36/image.png 650w,
/static/0a6eb12b788bab59a881a123b3e1fb4b/e548f/image.png 975w,
/static/0a6eb12b788bab59a881a123b3e1fb4b/3c492/image.png 1300w,
/static/0a6eb12b788bab59a881a123b3e1fb4b/229ad/image.png 1356w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;다시 정리하자면, 사용자의 요청을 받을 때와 해당 요청에 대해 메모리에 I/O 작업을 할 때는 멀티 쓰레드 방식으로 처리한다. 이후 각 요청들을 큐에 넣고 처리하는 부분은 싱글 쓰레드로 동작한다.&lt;/p&gt;
&lt;p&gt;이 덕분에 수행되는 각 명령어들에 대한 원자성(atomic) 을 보장할 수 있고, 컨텍스트 스위칭 없이 빠른 작업이 가능하다.&lt;/p&gt;
&lt;h3 id=&quot;멀티플랙싱multiplexing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%80%ED%8B%B0%ED%94%8C%EB%9E%99%EC%8B%B1multiplexing&quot; aria-label=&quot;멀티플랙싱multiplexing permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;멀티플랙싱(Multiplexing)&lt;/h3&gt;
&lt;p&gt;아직 멀티플렉싱 개념이 잘 와닿지는 않지만, 일단 작성해본다. I/O 멀티플렉싱이란 싱글 쓰레드가 여러 I/O 작업을 동시에 모니터링 할 수 있도록 해주는 기술이라고 한다. 이 방법을 사용하면, 여러 파일 디스크립터(File Descriptor) 의 I/O 상태를 하나의 호출로 확인하고, Non-Blocking Model 의 단점을 극복할 수 있게 해준다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 클라이언트 연결이 발생하면(소캣 생성), Redis 는 여러 연결(소캣)을 동시에 감시(모니터링)한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 데이터가 준비되었는지 확인한 후, 준비 된 요청만 처리한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 처리중인동안 다른 요청들은 대기하지 않고, 준비가 되면 바로 처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;여기서 &quot;준비된 요청&quot; 이라는 말이 햇갈릴 수 있는데, 우선 준비가 안된 요청에 대해 먼저 설명해보겠다. 준비가 안된 요청이란 클라이언트가 Redis 에 데이터를 보내고 있지만, 아직 네트워크 지연등으로 인해 데이터가 도착하지 못해 처리하지 못하는 요청을 뜻한다. 이에 반면 준비된 요청이란 클라이언트의 요청이 완전히 도착했고, Redis 가 바로 읽고 처리할 수 있는 상태를 뜻한다. (마치 비유하자면, 식당에서 손님이 주문을 할 때 주방장이 손님이 주문을 말하고 있는 중에는 조리를 시작할 수 없고, 주문을 다 말했을 때 주문서를 보고 바로 요리를 시작할 수 있는 상황과 같다.)&lt;/p&gt;
&lt;h3 id=&quot;이벤트-기반&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98&quot; aria-label=&quot;이벤트 기반 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이벤트 기반&lt;/h3&gt;
&lt;p&gt;또한 이벤트 기반으로 처리하기 떄문에 소켓 연결의 도착, 데이터의 도착, 데이터 전송 가능 상태등과 같은 상황을 받아서 처리할 작업을 그때그때 효율적으로 선택하고 처리할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Redis 는 Multiplexing I/O 모델을 사용하여 하나의 싱글 쓰레드만으로도 여러 클라이언트에게 입출력이 가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이벤트 기반으로 처리하기 떄문에 동적으로 그때그때 처리할 이벤트를 빠르게 처리할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/why-heck-single-threaded-redis-lightning-fast-beyond-in-memory-kapur/&quot;&gt;https://www.linkedin.com/pulse/why-heck-single-threaded-redis-lightning-fast-beyond-in-memory-kapur/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://junuuu.tistory.com/746&quot;&gt;https://junuuu.tistory.com/746&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://weights-learn-develop.tistory.com/75&quot;&gt;https://weights-learn-develop.tistory.com/75&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@ohjinseo/Redis%EA%B0%80-%EC%8B%B1%EA%B8%80-%EC%8A%A4%EB%A0%88%EB%93%9C-%EB%AA%A8%EB%8D%B8%EC%9E%84%EC%97%90%EB%8F%84-%EB%86%92%EC%9D%80-%EC%84%B1%EB%8A%A5%EC%9D%84-%EB%B3%B4%EC%9E%A5%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-IO-Multiplexing&quot;&gt;https://velog.io/@ohjinseo/Redis가-싱글-스레드-모델임에도-높은-성능을-보장하는-이유-IO-Multiplexing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[토스 SLASH 22, 애플 한 주가 고객에게 전달되기 까지]]></title><description><![CDATA[💡 토스 SLASH 22, 애플 한 주가 고객에게 전달 되기까지 에서 필요한 내용을 발췌하여 간단히 정리하였다. 토스증권의 해외주식 원장계는 MSA…]]></description><link>https://haon.site/article/toss-slash/broker-issue-concurrency-and-network-latency/</link><guid isPermaLink="false">https://haon.site/article/toss-slash/broker-issue-concurrency-and-network-latency/</guid><pubDate>Fri, 20 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 &lt;a href=&quot;https://www.youtube.com/watch?v=UOWy6zdsD-c&quot;&gt;토스 SLASH 22, 애플 한 주가 고객에게 전달 되기까지&lt;/a&gt; 에서 필요한 내용을 발췌하여 간단히 정리하였다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;350&quot; src=&quot;https://www.youtube.com/embed/UOWy6zdsD-c?si=FCpZVfkB4ixiQQXx&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;토스증권의 해외주식 원장계는 MSA를 기반으로 구성하였다. 각 도메인 별 모듈들이 독립적으로 구성되어 있기 때문에 단일 모듈의 장애 전파를 방지하고, 효율적인 리소스 관리가 가능하다.&lt;/p&gt;
&lt;p&gt;토스증권은 한국 법인이므로, 나스닥과 같은 현지 거래소와 직접 거래할 수 없다. 이 때문에 중간에서 브로커(해외거레 중개인) 을 통해 주문을 제출하고, 체결한다. 브로커를 통해 주문을 체결하므로 토스증권과 브로커간의 주문 데이터 정합성 관리가 매우 중요하며, 네트워크 지연 이슈도 잘 고려해야한다.&lt;/p&gt;
&lt;h2 id=&quot;주문-체결-플로우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A3%BC%EB%AC%B8-%EC%B2%B4%EA%B2%B0-%ED%94%8C%EB%A1%9C%EC%9A%B0&quot; aria-label=&quot;주문 체결 플로우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;주문 체결 플로우&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/da9b8066304ca73cae46b3e061f0ea8c/fbf76/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.74233128834356%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABT0lEQVR42l1SPwuCcBT8lZkULpnQn6lQGgocw7VP4OAQuBTNDQ01RoQUlEJErkZBbUFCYx/u4r1QouHQd747734qhBDI5XKga7VahaqqfP+PfD6PRqMBRVEyrlAoMFev1yFJUsqLbKhUKpkhcWRCIgLNmqahWCxClmXmSqUSarUao1wu876IoghJkuD9fsO2bQwGA7xeLzweD+x2Oxa1Wi3c73fm6Fmn02ExGVM7QvoiMZvNsN/vEQQBTNNEt9uF7/tYrVYYjUa8TMkXiwXz6/UazWaTE3Kin/oE4XkelsslwjCEYRjo9XrYbDbYbreYTCa8RIbz+TwzbLfbbOK6Lo7HIw6HAxzH+ZpPp1OcTic8n09YloV+v4/b7YbL5cIpyVDXddDRnM9nXK9XbkHi8XjMe3EcYzgcfg2pUhqdDv7na3Fdmsk0/RN+9/4rk/YDAfvdUMkuZ3gAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/da9b8066304ca73cae46b3e061f0ea8c/a6d36/image.png&quot;
        srcset=&quot;/static/da9b8066304ca73cae46b3e061f0ea8c/222b7/image.png 163w,
/static/da9b8066304ca73cae46b3e061f0ea8c/ff46a/image.png 325w,
/static/da9b8066304ca73cae46b3e061f0ea8c/a6d36/image.png 650w,
/static/da9b8066304ca73cae46b3e061f0ea8c/e548f/image.png 975w,
/static/da9b8066304ca73cae46b3e061f0ea8c/fbf76/image.png 1252w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;고객의 주문은 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 매매 서버, 매매 요청 서버를 거쳐서 브로커에게 전달되고, 브로커는 해외 현지 거래소에 주문을 제출하게 된다. (이 과정은 모두 하나의 주문 트랜잭션으로 처리되는 듯 하다.) &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 브로커를 통해 해외 거래소로 제출된 주문에 대한 체결 결과 여부를 처리하는 트랜잭션은 앞선 주문 트랜잭션과는 별개로 동작한다.&lt;/p&gt;
&lt;p&gt;앞선 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 의 주문 체결 결과 처리 트랜잭션은 체결 수신 서버를 거친 뒤 Kafka 를 거쳐서, 다시 매매 서버로 유입되어 처리가 마무리된다.&lt;/p&gt;
&lt;h2 id=&quot;동시-다발적으로-발생하는-트랜잭션에서-고객-자산에-대한-동시성-처리하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%8B%9C-%EB%8B%A4%EB%B0%9C%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%97%90%EC%84%9C-%EA%B3%A0%EA%B0%9D-%EC%9E%90%EC%82%B0%EC%97%90-%EB%8C%80%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0&quot; aria-label=&quot;동시 다발적으로 발생하는 트랜잭션에서 고객 자산에 대한 동시성 처리하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동시 다발적으로 발생하는 트랜잭션에서 고객 자산에 대한 동시성 처리하기&lt;/h2&gt;
&lt;p&gt;토스증권에서는 WTS, MTS, 자동 매매등 다양한 방법을 통해 고객의 잔고를 동시에 갱신하는 트랜잭션이 발생하게 된다. 즉, 고객의 잔고 갱신에 한 동시성 처리가 필요하다. 이 방법들을 통해 유입된 요청은 매매 서버로 전달되고, 브로커에게 전달된다. (앞선 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 트랜잭션이다.)&lt;/p&gt;
&lt;p&gt;이는 어떻게 해결할 수 있을까? &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 무턱대고 대상 테이블에 대해 하나하나 모두 &lt;code class=&quot;language-text&quot;&gt;FOR UPDATE&lt;/code&gt; 로 락을 걸어버린다면 데드락을 피할 수 없다. &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 이를 위해 락을 위한 테이블(계좌락 테이블) 을 별도로 두고, 아래처럼 &lt;code class=&quot;language-text&quot;&gt;FOR UPDATE&lt;/code&gt; 를 걸어서 동시성을 제어해볼 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;BEGIN&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TRANSACTION&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; account_lock
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; account_number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1911234&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;UPDATE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ... (비즈니스 로직상에 존재하는 쿼리 실행)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TRANSACTION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 이 방식은 단일 데이터베이스 환경에서만 적용할 수 있다. MSA 환경에서 각 마이크로 서버(모듈)는 서로가 각기 다른 독립적인 데이터베이스를 바라보고, 사용한다. 즉, 각 모듈은 서로 락을 공유하지 않는다. 게다가 각 모듈이 바라보는 서버 내에서도 데이터베이스 다중화까지 발생할 수도 있다.&lt;/p&gt;
&lt;h2 id=&quot;redis-분산-락을-사용한-동시성-해결&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-%EB%B6%84%EC%82%B0-%EB%9D%BD%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%ED%95%B4%EA%B2%B0&quot; aria-label=&quot;redis 분산 락을 사용한 동시성 해결 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis 분산 락을 사용한 동시성 해결&lt;/h2&gt;
&lt;p&gt;이는 Redis 분산 락으로 해결할 수 있다. 분산 락을 통해 모든 서버는 락을 중앙화된 Redis 서버로부터 공유하고, 제어한다. 또한 분산 락은 여러 서버들에서 모두 요청하기 때문에 높은 처리량(throughput) 이 보장되어야 한다. Redis 는 메모리 기반 저장소이므로 DBMS 에서 계좌락을 별도로 사용하는 방식보다 높은 처리량을 제공한다.&lt;/p&gt;
&lt;h3 id=&quot;분산-락-타임아웃-설정과-그에-따른-갱실문제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%84%EC%82%B0-%EB%9D%BD-%ED%83%80%EC%9E%84%EC%95%84%EC%9B%83-%EC%84%A4%EC%A0%95%EA%B3%BC-%EA%B7%B8%EC%97%90-%EB%94%B0%EB%A5%B8-%EA%B0%B1%EC%8B%A4%EB%AC%B8%EC%A0%9C&quot; aria-label=&quot;분산 락 타임아웃 설정과 그에 따른 갱실문제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;분산 락 타임아웃 설정과 그에 따른 갱실문제&lt;/h3&gt;
&lt;p&gt;하지만, 분산 락을 사용하면 임계영역에 하나의 쓰레드만 진입할 수 있다. 이 때문에 하나의 트랜잭션이 무한정 락을 소유한다면 다른 서버들의 요청이 무한정 대기하여 데드락이 발생한다. 이 때문에 분산 락의 타임아웃을 설정하여 문제를 해결해보고 싶지만, 아쉽게도 &lt;strong&gt;분산 락 타임아웃은 의도대로 동작하지 않을 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;분산 락 타임아웃이 지나서 락 자체를 해제가 되었으나, 트랜잭션은 아직 끝나지 않는 상태라서 다른 트랜잭션과의 경합이 발생할 수 있다. 즉, &lt;strong&gt;갱신 분실 문제(Lost Updates Problem)&lt;/strong&gt; 가 발생할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;갱실-분실-문제-lost-updates-problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%B1%EC%8B%A4-%EB%B6%84%EC%8B%A4-%EB%AC%B8%EC%A0%9C-lost-updates-problem&quot; aria-label=&quot;갱실 분실 문제 lost updates problem permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;갱실 분실 문제 (Lost Updates Problem)&lt;/h3&gt;
&lt;p&gt;이와 관련한 유사한 문제는 &lt;a href=&quot;https://haon.blog/database/optimistic-pessimistic-lock/&quot;&gt;JPA 낙관적 락과 비관적 락으로 엔티티에 대한 동시성 이슈 해결하기&lt;/a&gt; 에서도 동일하게 다룬적이 있다. 토스증권 또한 송금 관련 트랜잭션에서 갱실 분실 문제가 발생한 듯 하다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/98566882fe3237e48414908bb8d7582e/d8d63/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.05521472392638%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB5UlEQVR42nWSv47aQBDGeRG6FJHyJDwCUkoaivAQvAAV5eUtUOhSUCAKmhBdSI6Qs2x8wsZ4116v7fWfLzObI+IuYaSVZXvnN983M52maVCUJVqTA3mMNKJzf4AJExTGoKR/RfyAKvkG1BI2mhaNJ1D7CdDiRXSKokB4itCWGRC78P0D3KcDwUpkWkPHRzztt9jvHYhEIU1TKKUglMT24TuyLLOgtv1D7piqgpQRfdFo6wzHowvP9aAokS9nIsSHwXu8e/sG8/ncJtV1jULnBFd4HZ3S1IgO90DwGa3/CThvIGRKVgtUZLmmyg4V+LL5apVdQuc5NKlElb9w3eEeRWyZEtl2G+4QnyPbV8NAUvPx7g6j0Qjr9RrsTJM6IROE7k+YYPeMerZ8LffSB+4Tgy4xnU4xGAywXC5pIIVVxnd2+1+Qr2z/A+STJAmahoBtbiuXpaF+Kpiqhgp/QEvfDsh5fIQU4jaQV8hxHJq0D52l0MEGuTojFhKCErmYIeU1rQ0PjAtfXN1UyEDXdS1ASAWZpBiPx+j1elitVn/v8bppWqtrZ/8Fcu+klDahqoxVPRwO0e12MZvN7L2KVo1hrJKf/H5TIQerux7KZDJBv9/HYrGwBdjq6XSC53nYbrcIgsCCOf83UpM+19pzYDcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/98566882fe3237e48414908bb8d7582e/a6d36/image-2.png&quot;
        srcset=&quot;/static/98566882fe3237e48414908bb8d7582e/222b7/image-2.png 163w,
/static/98566882fe3237e48414908bb8d7582e/ff46a/image-2.png 325w,
/static/98566882fe3237e48414908bb8d7582e/a6d36/image-2.png 650w,
/static/98566882fe3237e48414908bb8d7582e/e548f/image-2.png 975w,
/static/98566882fe3237e48414908bb8d7582e/3c492/image-2.png 1300w,
/static/98566882fe3237e48414908bb8d7582e/d8d63/image-2.png 2026w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;통장에 3000원이 있는 상황이다. 트랜잭션1 과 트랜잭션2 는 서로 다른 서버에서 시작되었고, 트랜잭션1이 먼저 실행되어 분산 락을 획득(acquire) 한 상태이다. Redis 분산 락의 타임아웃은 2초 설정했다 가정해보자.&lt;/p&gt;
&lt;p&gt;그런데 만약, 트랜잭션1 에 지연이 발생하여 2초 넘게 UPDATE 와 COMMIT 을 하지 못한 상태로 타임아웃되면 어떻게 될까? 우선 트랜잭션 1은 락을 해제하고, 그 즉시 트랜잭션 2가 락을 획득한다. 이때, 트랜잭션 1은 아직 커밋을 하지 못한 상태이므로 통장의 잔고를 업데이트하지 못했다. 이 타이밍에 트랜잭션 2가 통장 잔고를 데이터베이스로 부터 조회해버리게 되므로, 트랜잭션 2의 입장에서는 잔고를 0원이 아니라 3000원으로 읽어오게 된다.&lt;/p&gt;
&lt;p&gt;그 뒤로, 뒤늦게 트랜잭션 1이 잔고를 갱신하고 커밋했다. 이 때문에 데이터베이스내에 잔고는 0원으로 갱신된다. 그러나, 곧 이어서 트랜잭션 2가 이 또한 커밋되어 잔고가 -1000원 (엄밀히는 -1000이 아니라, 이때 애러 핸들링을 하긴 했을 것이다.) 이 아니라 2000원으로 갱신해버린다. 즉, 트랜잭션1의 갱신 내역을 덮어씌워버린다.&lt;/p&gt;
&lt;p&gt;이렇듯 분산 락 메커니즘을 사용하였음에도 불구학, 갱신 분실 문제가 발생했다. 이처럼 분산 락이 있음에도 락이 발생하는 경우는 언제일까? &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 분산 락을 해제하기 전에 DB 트랜잭션이 커밋되거나, &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 분산 락을 해제하고나서 커밋 되는 경우에 발생하게 된다. (위 경우는 2번에 해당할 것이다.) JPA 를 사용한다면 쿼리를 영속성 컨텍스트에 저장해 놓았다가 지연 쓰기 하므로, 이 문제가 발생할 확률이 더 높아진다.&lt;/p&gt;
&lt;h3 id=&quot;해결안&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B4%EA%B2%B0%EC%95%88&quot; aria-label=&quot;해결안 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;해결안&lt;/h3&gt;
&lt;p&gt;겡신 분실 문제를 어떻게 해결할 수 있을까? 그 해결안은 아래와 같을 것이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 명시적 잠금 (FOR UPDATE 절)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 원자적 연산 사용 (atomic 하게 연산 처리. mutex, sempahore 등)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 갱신 손실 자동 감지 (낙관적 락 기반 Version 관리와 유사)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; CAS(Compare-And-Set) 연산&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;각 메커니즘에 대한 추가적인 이해가 필요하다. (각 기법이 정확히 무엇을 말하는 것인가?)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;그런데, 명시적 잠금의 경우 앞서 살펴본 것 처럼 여러 테이블을 갱신하는 트랜잭션에서 비용이 매우 비싸다. 한편 원자적 연산 과 갱신 손실 자동 감지 기법의 경우는 DBMS 에 의존적이기 때문에 ORM 과 호환성이 좋지 않다고 한다.&lt;/p&gt;
&lt;h3 id=&quot;낙관적-락을-사용한-동시성-해결&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%82%99%EA%B4%80%EC%A0%81-%EB%9D%BD%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%ED%95%B4%EA%B2%B0&quot; aria-label=&quot;낙관적 락을 사용한 동시성 해결 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;낙관적 락을 사용한 동시성 해결&lt;/h3&gt;
&lt;p&gt;토스 증권은 CAS 연산 구현을 택하였다. JPA 에서 제공하는 @OptimisticLocking 을 사용해, 즉 낙관적 락을 사용하여 간단히 CAS 연산 구현이 가능하다.&lt;/p&gt;
&lt;p&gt;그렇다면, 분산 락 없이 낙관적 락 만으로도 동시성 해결이 가능하지 않을까? 싶다. 하지만, &lt;strong&gt;분산 락이 업다면 동시적으로 발생하는 트랜잭션들은 대기 없이 실패하게 된다. 또는 갱신 실패시 재시도를 위한 별도의 재시도(Retryable) 구현이 필요하다.&lt;/strong&gt; 재시도 구현은 &lt;a href=&quot;https://haon.blog/database/optimistic-pessimistic-lock/&quot;&gt;JPA 낙관적 락과 비관적 락으로 엔티티에 대한 동시성 이슈 해결하기&lt;/a&gt; 에서도 다루었듯이 좋은 방법이 아니고, 코드의 복잡성이 증가한다.&lt;/p&gt;
&lt;p&gt;따라서 토스증권은 분산 락으로 동시성을 제어하고, 만약의 상황을 대비하여 낙관적 락을 통해 갱신 분실 문제를 해결하였다.&lt;/p&gt;
&lt;h2 id=&quot;해외구간-네트워크-지연으로부터-안전하게-서비스하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B4%EC%99%B8%EA%B5%AC%EA%B0%84-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%A7%80%EC%97%B0%EC%9C%BC%EB%A1%9C%EB%B6%80%ED%84%B0-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-%EC%84%9C%EB%B9%84%EC%8A%A4%ED%95%98%EA%B8%B0&quot; aria-label=&quot;해외구간 네트워크 지연으로부터 안전하게 서비스하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;해외구간 네트워크 지연으로부터 안전하게 서비스하기&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/91e7a219ae0b9c904ae6df352f7bb745/1c1a4/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.558282208588956%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABgElEQVR42q2SvUuCURTG7+sXWoibW4PglOAHDoEkKA4uCbWoOLio0/tqFoJODi3hKji4+i+4JJn4B4iLg4MObpKtGtTyy/diYm5FFx7Ovc9z7nku51xhMpmwWq3YbDYsFguKoiCE+Dt6vR6TyYR+v89yuSSRSEhBNzIajRgMBhm/IbZnhzBxohhRDnip6QXb7TbdbpdOp8NwOCQej0tBL3TsruxiQLFzplh/cHtks1kqlQqaplGr1fD5fJjNZgKBAPl8nkwmQ7FYpFAocH93T+LmGr84xe88Qy3foqkaqqpSLpdxOByI+XzOZrNhOp2ir1wuJ51KpRKtVovxeMx6vWY2m/H58cHTyzOPws3DxRXv2/y31xWr1UredbvdiGg0SjqdJplMSrhcLtkPp9NJKBTC6/Wi50QiEWKxGOceD+atod12wmU4LHldD2/3+nDFYDBgNBrJPi4WC1KplHzhvsm/RbVapdFoUK/XaTabBIPBfUF9MMc4/FbH2o4X/Ce+AG2WIdsIuSz8AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/91e7a219ae0b9c904ae6df352f7bb745/a6d36/image-3.png&quot;
        srcset=&quot;/static/91e7a219ae0b9c904ae6df352f7bb745/222b7/image-3.png 163w,
/static/91e7a219ae0b9c904ae6df352f7bb745/ff46a/image-3.png 325w,
/static/91e7a219ae0b9c904ae6df352f7bb745/a6d36/image-3.png 650w,
/static/91e7a219ae0b9c904ae6df352f7bb745/e548f/image-3.png 975w,
/static/91e7a219ae0b9c904ae6df352f7bb745/1c1a4/image-3.png 1046w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;앞서 살펴보았듯이, 매매 요청 서버는 브로커와 통신하고 있다. 이때, 브로커와 매매 요청 서버가 통신하는 이 구간은 해외 망으로써 네트워크 지연이 빈번히 발생한다. 이를 위해, 토스 증권에서는 고객의 요청을 받는 매매 서버와, 브로커에게 요청하는 쓰레드를 분리하여 모든 쓰레드가 Blocking 되는 이슈를 해결하였다. (즉, 브로커 요청과 관련없는 쓰레드들까지 모두 Blocking 되는 상황을 막은 듯 하다.)&lt;/p&gt;
&lt;h3 id=&quot;브로커의-응답을-받지-못한-상태에서-타임아웃-되버린-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%8C%EB%A1%9C%EC%BB%A4%EC%9D%98-%EC%9D%91%EB%8B%B5%EC%9D%84-%EB%B0%9B%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%83%81%ED%83%9C%EC%97%90%EC%84%9C-%ED%83%80%EC%9E%84%EC%95%84%EC%9B%83-%EB%90%98%EB%B2%84%EB%A6%B0-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;브로커의 응답을 받지 못한 상태에서 타임아웃 되버린 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;브로커의 응답을 받지 못한 상태에서 타임아웃 되버린 경우&lt;/h3&gt;
&lt;p&gt;하지만, 매매 서버와 브로커가 통신하는 구간은 여전히 네트워크 지연이 발생한다. 따라서 하나의 API 에서 동기로 처리할 경우 상황에 따라 고객은 이 주문 응답을 기다리느라 다른 서비스를 이용할 수 없게 된다.&lt;/p&gt;
&lt;p&gt;토스증권은 이 구간을 비동기로 처리하여 고객 경험을 상승시키고, 트랜잭션 시간을 최소화하고 있다. 브로커 요청이 실패할 수도 있기 때문에, 브로커 요청 이전에 주문을 반드시 대기상태로 저장해야 한다. 대기상태의 주문은 브로커의 응답 결과에 따라 접수 성공/실패로 주문 상태를 갱신하게 된다.&lt;/p&gt;
&lt;p&gt;그런데 만약, &lt;strong&gt;매매 서버와 브로커간에 TCP 기반으로 통신하는 이상 타임아웃이 발생한다면 어떻게 처리해야 할까?&lt;/strong&gt; 브로커의 응답을 받지 못한체 타임아웃 되버렸기 때문에 해당 주문 상태를 임의로 우리가 판단할 수 없게 된다. (타임아웃 되버렸다고 무식하게 주문 상태를 완료 처리로 바꿔버린다면 문제가 된다. 네트워크 지연으로 인해 타임아웃 되었지만, 실제 브로커에서는 주문을 정상 처리했을 수 있기 때문이다.)&lt;/p&gt;
&lt;p&gt;또한 브로커 측의 식별자를 전달받지도 못한 상태이므로 주문 상태를 조회할 수도 없다. 토스증권은 이렇듯 브로커와의 통신에서 타임아웃되어 주문 상태를 처리하지 못하는 주문들을 &lt;strong&gt;재시도 대상으로 판단한다.&lt;/strong&gt; 재시도에서 발생할 수 있는 문제들을 간단히 살펴보고, 이를 어떻게 해결했는지 알아보자.&lt;/p&gt;
&lt;h3 id=&quot;멱등한-api-요청을-통해-안전하게-처리하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%B1%EB%93%B1%ED%95%9C-api-%EC%9A%94%EC%B2%AD%EC%9D%84-%ED%86%B5%ED%95%B4-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0&quot; aria-label=&quot;멱등한 api 요청을 통해 안전하게 처리하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;멱등한 API 요청을 통해 안전하게 처리하기&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7d79190a423986cafd1bb5817a0d7bef/d48f1/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.760736196319016%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB8UlEQVR42p1Sz2sTQRSeN7PJ7ia72U2ajalFI2ix0GMpiEgOVvDgwV+gt1hD+zcUhGoPMRcRvMQ2PUv90YKnHHJOPVRicmg8JZdQT+olpxw/Z2eS0mBVcOFj5/365r15H2OMoMFGIPzuY/+In7CJ9EG4WYjkhZGT/YFMFag/ReOIzCzAmLo4WUMRB4yb4M40yJeEIqZAhqUSOOcQQhwTUkTGuQ0yU+DpOZA7o+2QhzjYzVt3sbv3EZvVbVQqFey8+4D1jRJELKUIcrkc8vk8giBQtu1N48XLV3iz8xavZf5WtYr3u3u4cu26HnvtyTrCr9vt4rBziOFwiOaXFpKZc4jHY7BtG5ZlwTAMRehnzqPf72MwGKDVaqHztaPqC8sreorZuXk8Xn6EQqGgUCwWsbR0A9z0YFsmEgkPrusqUjW+5WNldRXlchmlUkniOTaePcWly/OSUF5KInrKw+vC8RLC7hzHUcQLi1fRaDTQbrdRq9VQr9fx7egId+4/1PnciELIIhHefgwhCT29BKIT0mA4m5vFp/199Ho9fD44QLPZxM8f33H73oPxluVGSYC4MYI8y66FlZzoekzMTR9OwsdUcAbpTBbpIAs/lULE9kYKkJ1wMzGJ0BcGT9Ehj0p5GHElFQWhQbKOcSWvv4j4P/AL6nI+CbC7O+cAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7d79190a423986cafd1bb5817a0d7bef/a6d36/image-4.png&quot;
        srcset=&quot;/static/7d79190a423986cafd1bb5817a0d7bef/222b7/image-4.png 163w,
/static/7d79190a423986cafd1bb5817a0d7bef/ff46a/image-4.png 325w,
/static/7d79190a423986cafd1bb5817a0d7bef/a6d36/image-4.png 650w,
/static/7d79190a423986cafd1bb5817a0d7bef/d48f1/image-4.png 796w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;사용자가 1번 주문을 요청하였다. 이에 따라 매매 서버는 브로커와 통신하여 주문 요청을 한다. 이때 네트워크 지연 문제로 인해 타임아웃 되었지만, 브로커 서버에서는 주문이 정상적으로 처리 되었고 주문 데이터도 1번으로 잘 생성되었다고 해보자. 이 상황에서 재시도 요청을 하였다면, 브로커는 1번 주문에 대해 2개의 주문을 생성해버린다. 즉, 토스증권의 주문과 브로커의 주문 데이터간의 정합성이 깨진다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d2b515f6171467bf81500fbd311d5c0b/d48f1/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.920245398773%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABfUlEQVR42n2Su0/CUBjF7wNKeZVqwJhYqolG1EmMaCBGIMKiiYuzg5suDg7GxE39V4wORRxxRmQVVPS/Od7ePngINjnt19tzf73nu5cQQkA9BVRQxmVNBkQpHX66NVMiYMHIiJ8yEAGRL/oyiKI7k+QYHQJ74xLMgiDJdTEn43zzGISHYSyuIb+7J1RBvljF9k4ZWioNTZ9GKpWEaZowDAMrmQx4KI609FekCmLeVqGExMy8gDEbynD/8Aj7+uh20Pv6lPXZ+QVm50xsZLPYzOWQEyoVi1hYWkWj8SI93c47fr57sj4+OfVSBHB5dY12uw2rZqH2VEer1cL+4ZE0MEZldDsmpQRc1XBzeyc8b7AsC/X6M16bTZSrB270UELcAn7TGWMOSDSbiU1yG+33k4U0yL67fur6qRID5cowcFBy93xgX1wVfsr/+n2gOgkYnQDU/RWOBVI7mjDZUfqKg4WnRJwxP1KisBchPb5i0u8sTJ4pPl4jML+f//h/AchzCkKjwwgbAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d2b515f6171467bf81500fbd311d5c0b/a6d36/image-5.png&quot;
        srcset=&quot;/static/d2b515f6171467bf81500fbd311d5c0b/222b7/image-5.png 163w,
/static/d2b515f6171467bf81500fbd311d5c0b/ff46a/image-5.png 325w,
/static/d2b515f6171467bf81500fbd311d5c0b/a6d36/image-5.png 650w,
/static/d2b515f6171467bf81500fbd311d5c0b/d48f1/image-5.png 796w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이는 멱등한 API 요청을 통해 해결했다. 멱등성은 같은 요청에 대해 항상 같은 값을 유지하는 성질로, 여러번 요청하더라도 항상 같은 결과를 반환한다. &lt;strong&gt;멱등성을 위해 토스증권은 토스 주문 ID 를 멱등키로 보내어, 하나의 토스 주문 ID 에는 하나의 브로커 주문한 생성하도록 보장하였다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;네트워크-병목이-발생하지-않기-위한-재시도-전략&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%B3%91%EB%AA%A9%EC%9D%B4-%EB%B0%9C%EC%83%9D%ED%95%98%EC%A7%80-%EC%95%8A%EA%B8%B0-%EC%9C%84%ED%95%9C-%EC%9E%AC%EC%8B%9C%EB%8F%84-%EC%A0%84%EB%9E%B5&quot; aria-label=&quot;네트워크 병목이 발생하지 않기 위한 재시도 전략 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;네트워크 병목이 발생하지 않기 위한 재시도 전략&lt;/h3&gt;
&lt;p&gt;이렇게 주문 요청에 대한 멱등성을 확보했다. 그러면 이제 성공할 때 까지 주문 요청을 계속 재시도하면 끝날까?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;타임아웃의 특성상, 짧은 주기로 재요청을 하게 된다면 네트워크 지연 상황을 더욱 악화시킬 수 있다.&lt;/strong&gt; (되려 네트워크 병목을 불러일으키는 셈이다.) 네트워크 지연으로 인해 더 빈번한 타임아웃이 발생하게 되는 악순환의 고리로 이어지는 셈이다.&lt;/p&gt;
&lt;p&gt;따라서 토스증권에서는 일정 횟수로 재시도를 제한하고, 지수적으로 간격을 두어 재시도하는 전략을 사용하고 있다. 예를들면 1분에 한번, 그리고 2분, 4분, 8분에 한 번식 재시도 요청을 보내는 것이다.&lt;/p&gt;
&lt;p&gt;혹시라도 주문 및 체결 정합성이 깨지는 경우가 발생하면 대사 배치에서 잡히게 되고, 이는 별도의 오퍼레이션에 의해 처리할 수 있도록 장치가 준비되어 있다. 그리고 토스증권내의 모든 데이터 정합성은 채결 내역에서부터 시작하기 때문에, 토스증권내의 정합성이 틀어지는 일을 절대 발생하지 않는다.&lt;/p&gt;
&lt;p&gt;이렇게 되면 해외 구간 요청은 안전하게 처리되었다.&lt;/p&gt;
&lt;h2 id=&quot;브로커-의존성-격리하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%8C%EB%A1%9C%EC%BB%A4-%EC%9D%98%EC%A1%B4%EC%84%B1-%EA%B2%A9%EB%A6%AC%ED%95%98%EA%B8%B0&quot; aria-label=&quot;브로커 의존성 격리하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;브로커 의존성 격리하기&lt;/h2&gt;
&lt;p&gt;브로커는 외부 기관이므로, 비즈니스 로직이 언제든 추가되고 변경될 수 있다. 별도의 처리가 없는 위에서 본 구조라면, 새로운 브로커가 추가되면 네트워크 프로토콜부터 인터페이스까지 매매 서버가 새로운 브로커에 맞추게 되며 변경에 같이 가해진다. 또한 매매 서버 처리량이 브로커의 처리량과 강하게 결합되어 있다. (브로커가 문제가 발생하면 매매 서버도 영향을 받는다.)&lt;/p&gt;
&lt;p&gt;이를 위해, 토스증권은 브로커 의존성을 가장 강한 격리 수준인 서버 레벨에서 격리시키고 있다. (기존에는 매매 서버가 직접 브로커와 통신했는데, 매매 서버와 브로커 사이에 매매 요청 서버를 증설했다는 뜻인듯 하다.) 즉, 브로커가 전문 통신을 지원하던, HTTP 등 어떤 스팩을 지원하던간에 상관없이 매매 서버는 영향을 받지 않도록 내부 인터페이스만 맞추면 되도록 했다.&lt;/p&gt;
&lt;p&gt;앞서 살펴본 타임아웃 주문 처리 역시 대표적인 브로커 인터페이스 의존 요소이다. 토스증권 식별자로 상태를 조회할 수 있게 제공해주는 브로커가 추가된다면, 전혀 다른 방식의 타임아웃 주문 처리를 도메인 로직과는 무관하게 확장 가능하다.&lt;/p&gt;
&lt;p&gt;또한 브로커 처리량에 따라서 매매 요청 서버를 Scale Out 하여 효율적인 리소스 사용이 가능해지고, 브로커 장애가 토스 증권 서비스 전체로부터 격리 가능한 구조로 개선되었다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d405da9359dab0141a667cc5bfe14011/78612/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.809815950920246%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABSUlEQVR42l1Sv8uCUBR9/bDEtDRoyzGahYYm2yKhoa2EBkf/ARuioCJahSRQqa0Cwf/xfNzbZ/h9wwHfeeeee+59CiEEqtUqarUao91uQ5KkL1epVEAagqZp0HX9D6eqKrrdLtcwRx9lgWmakGX5eybTer3ODVqtFjcsmlNtv9/HYDBAp9NhjSDR6XTC/X5HHMecwHVdPj8eD0wmEzaez+dI0xRJkmC9XjPXaDTYhAJRg2azCdHr9XC5XBBFEcMwDCyXS4RhiOv1Ctu2v4a3242xWq2Yo+TFJEUDQYlIsN1usdvtoCgKptMpDocDjscjLMti8Xg8xn6/ZziOw8mGwyFPdz6f4Xnex5gMgiDgEfM8513Q5fP5RJZlmM1mLFwsFni/33i9XvB9n7nRaMRrIWw2m48hxaYd8EJ/H6HYy/8/oPxQZV1RT5ofucjYqUWxOJoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d405da9359dab0141a667cc5bfe14011/a6d36/image-6.png&quot;
        srcset=&quot;/static/d405da9359dab0141a667cc5bfe14011/222b7/image-6.png 163w,
/static/d405da9359dab0141a667cc5bfe14011/ff46a/image-6.png 325w,
/static/d405da9359dab0141a667cc5bfe14011/a6d36/image-6.png 650w,
/static/d405da9359dab0141a667cc5bfe14011/e548f/image-6.png 975w,
/static/d405da9359dab0141a667cc5bfe14011/78612/image-6.png 1260w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위의 주문 체결 플로우를 보면, 매매 요청과 체결 수신 서버를 중간에 배치하여 매매 서버를 브로커와의 결합성을 낮추었다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/89e9e910c5c12ed73bb841409eb3c77f/077b7/image-7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.12883435582822%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABHElEQVR42q1QO6qDUBS88YMGglEkIqZKKrFNE0tr0W2EINgkoKBNSDaQZaTLGrRQxDoW7kGXMI97QfI+8ODBK4YDZ2bOnTuEEIJ/xe12Q5qmuFwusCwLPM8jiiJkWYYkSXC9XuF5HlRVRRzHyPMc5/OZmYMgAPWfTiems20bZBxHvF4v0Lnb7SDLMqqqQt/36LoOwzCw45vNBs/nE0VRoK5rdvB+vzO+bVvm930fxDRNUOi6Do7jmFBRFCyXSxiGAU3TIEkSZrMZttstHMfBer1muvl8jsm/Wq0gCALdv/9PTXQuFguEYYjj8Yj9fv+3DumRCVNC13XRNA3KssTj8WAP0D3lJ+3nEN92PxPSvmhvtPzD4QBRFL/wv+ED1HLZsejhtqsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/89e9e910c5c12ed73bb841409eb3c77f/a6d36/image-7.png&quot;
        srcset=&quot;/static/89e9e910c5c12ed73bb841409eb3c77f/222b7/image-7.png 163w,
/static/89e9e910c5c12ed73bb841409eb3c77f/ff46a/image-7.png 325w,
/static/89e9e910c5c12ed73bb841409eb3c77f/a6d36/image-7.png 650w,
/static/89e9e910c5c12ed73bb841409eb3c77f/e548f/image-7.png 975w,
/static/89e9e910c5c12ed73bb841409eb3c77f/077b7/image-7.png 1166w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;브로커 의존성을 격리함으로써 채결 수신 처리 서버 또한 강력한 장점들이 생겨난다. 채결 수신 서버는 브로커의 이벤트를 받아 데이터베이스에 적재하고, Kafka 로 매매 서버에게 발송한다. 이로써 매매 서버가 매우 바쁜 상황이더라도 브로커의 이벤트를 수신하지 못하는 상황을 방지할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7fde9721130afe3463212398e134c993/89048/image-8.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37.423312883435585%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABDklEQVR42o1SzUqEYBT1dyGYREWEEGLgugZE8yWs3kEQV5Ig5A+Ii0DqCdy4svW8gOAD2MZBcOernMYvZhhqBlwcLnznfOeee7kURVFYivMzBjdXNK4vtrhkT+ko2LaNLMsQRREcxyGEaZpI0xS+7yPPc1iWhTv1FtHbKz4/3vForiDLMpIkQRAEiOMYiqL8GhZFgXEc0XUd1us1MZyNh2FA27aYpgmu6+L+YYW6/trqvvH0/AJd17HZ9GiaBn3fkxDULirDMGDZ/2MIgrB/p2kakiRBFEWi3/3jOI5w+5GPYRZ4noeqqhCG4dFmJ3f412iuPM+jLEuygrquSdJDfrHhIVRVhWEY0DRt8SX8AGK7zHZJNqvQAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7fde9721130afe3463212398e134c993/a6d36/image-8.png&quot;
        srcset=&quot;/static/7fde9721130afe3463212398e134c993/222b7/image-8.png 163w,
/static/7fde9721130afe3463212398e134c993/ff46a/image-8.png 325w,
/static/7fde9721130afe3463212398e134c993/a6d36/image-8.png 650w,
/static/7fde9721130afe3463212398e134c993/e548f/image-8.png 975w,
/static/7fde9721130afe3463212398e134c993/89048/image-8.png 1242w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;또한 카프카에 이벤트를 업로드하기 전에 브로커 수신 내역을 데이터베이스에 적재하고 있기 때문에, 카프카 서버가 다운되더라도 폴링 모드로 전환하는 등 다양한 Fail Over 전략이 메시지 유실 없이 처리 가능해진다.&lt;/p&gt;
&lt;p&gt;또한 중복 이벤트 역시 브로커 인터페이스와 무관하게 처리 가능해진다. 중복 처리는 이벤트를 발행할 때 Unique 한 ID 를 발급하여 처리가 가능하다. 브로커 파트너가 여러개라면, 각 파트너끼리 ID 가 경합될 수도 있다. 하지만 이 역할을 체결 수신 서버가 여러개의 브로커 파트너에 대해서도 중복 없이 전역적인 Unique 한 ID 발급이 가능해진다.&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이렇게 토스증권이 어떻게 해외까지 이어지는 트랜잭션을 안전하게 처리하는지 알아보았고, MSA 의 특성을 이용해 확장 가능한 원장을 만들었는지 알아보았다.&lt;/p&gt;
&lt;h2 id=&quot;더-학습해볼-질문-주제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%EC%A7%88%EB%AC%B8-%EC%A3%BC%EC%A0%9C&quot; aria-label=&quot;더 학습해볼 질문 주제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 질문 주제&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;모행 서비스가 분산 환경, 데이터베이스 Source 서버가 다중화되는 경우의 동시성 처리 방법&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;지난 &lt;a href=&quot;https://haon.blog/virtual-interview/chap04/&quot;&gt;[가상 면접 사례로 배우는 대규모 시스템 설계 기초] 제 10장. 알림 시스템 설계&lt;/a&gt; 에서 등장한 내용과 연관지어 생각하기. 왜 카프카를 쓰는가? 왜 수신 내역을 데이터베이스에 저장하도록 구성했는가?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[LazyConnectionDataSourceProxy 의 커넥션 획득 지연 방법]]></title><description><![CDATA[MySQL 8.0 레플리케이션과 스프링부트 DataSource…]]></description><link>https://haon.site/spring/lazy-connection-datasource-proxy/</link><guid isPermaLink="false">https://haon.site/spring/lazy-connection-datasource-proxy/</guid><pubDate>Tue, 17 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://haon.blog/database/replication-mysql-springboot/&quot;&gt;MySQL 8.0 레플리케이션과 스프링부트 DataSource 라우팅을 통한 부하 분산&lt;/a&gt; 에서도 다루었듯이, 우리 서비스는 레플리케이션을 위해 &lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourceProxy&lt;/code&gt; 를 사용한다. 이에 대한 학습을 더 자세히 진행해볼까 한다.&lt;/p&gt;
&lt;h2 id=&quot;lazyconnectiondatasourceproxy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lazyconnectiondatasourceproxy&quot; aria-label=&quot;lazyconnectiondatasourceproxy permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LazyConnectionDataSourceProxy&lt;/h2&gt;
&lt;p&gt;스프링은 어떤 시점에 DataSource 로 부터 커넥션을 가져올까? 결론부터 말하면, &lt;strong&gt;스프링에서는 트랜잭션에 진입하는 순간 그 즉시 설정된 DataSource 의 커넥션을 가져온다.&lt;/strong&gt; 그런데, 이 특징 때문에 발생할 수 있는 문제점은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Encache 같은 Cache 를 사용하는 경우 실제로 데이터베이스에 접근하지 않지만 불필요하게 커넥션을 점유&lt;/li&gt;
&lt;li&gt;Hibernate 의 영속성 컨텍스트 1차 캐시 (실제 데이터베이스에 접근하지 않음) 에도 불필요한 커넥션을 점유&lt;/li&gt;
&lt;li&gt;외부 서비스 (http, etc ...) 에 접근해서 작업을 수행한 이후에 그 결과값을 데이터베이스에 Read/Write 하는 경ㅂ우 외부 서비스에 의존하는 시간만큼 불필요한 커넥션 점유&lt;/li&gt;
&lt;li&gt;Multi DataSource 환경에서 트랜잭션에 진입한 이후 DataSource 를 결정해야할 때 이미 트랜잭션 진입 시점에 DataSource 가 결정되므로 분기가 불가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위 단점들을 해결할 수 있는 방법이 바로 LazyConnectionDataSourceProxy 이다. 이를 사용하면 실제로 커넥션이 필요한 경우가 아니라면 커넥션 풀에서 커넥션을 점유하지 않고, &lt;strong&gt;실제로 필요한 시점에만 커넥션을 점유할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;사용에-따른-차이-확인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%82%AC%EC%9A%A9%EC%97%90-%EB%94%B0%EB%A5%B8-%EC%B0%A8%EC%9D%B4-%ED%99%95%EC%9D%B8&quot; aria-label=&quot;사용에 따른 차이 확인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;사용에 따른 차이 확인&lt;/h2&gt;
&lt;p&gt;아래 예제를 살펴보쟈.&lt;code class=&quot;language-text&quot;&gt;lazy()&lt;/code&gt; 메소드는 DBCP 의 DataSource 타입에 따라 분기처리를 한다. 실제 데이터베이스에 쿼리를 날리지 않고, 커넥션 수의 변화를 확인하는 코드이다. 여기서 중요한 점은, 실제로 데이터베이스와 통신하는 부분이 없다는 점이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LazyService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; dataSource&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lazy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;HikariPoolMXBean&lt;/span&gt; hikariPool&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;datasource &lt;span class=&quot;token keyword&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LazyConnectionDataSourceProxy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            hikariPool &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HikariDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LazyConnectionDataSourceProxy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; dataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTargetDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getHikariPoolMXBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            hikariPool &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HikariDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; dataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getHikariPoolMXBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;TotalConnection = {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; hikariPool&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTotalConnections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ActiveConnection = {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; hikariPool&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getActiveConnections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;IdleConnection = {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; hikariPool&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getIdleConnections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;LazyProxy&lt;/code&gt; 는 &lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourcePrroxy&lt;/code&gt; 를 빈으로 등록하는 코드이다. &lt;code class=&quot;language-text&quot;&gt;LazyProxy&lt;/code&gt; 를 통해 프록시를 사용했을 때와 사용하지 않았을 때 어떻게 커넥션이 변경되는지 확인해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LazyProxy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lazyDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceProperties&lt;/span&gt; dataSourceProperties&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HikariConfig&lt;/span&gt; hikariConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HikariConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        hikariConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setJdbcUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataSourceProperties&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        hikariConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDriverClassName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataSourceProperties&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getDriverClassName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        hikariConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setUsername&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataSourceProperties&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUsername&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        hikariConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataSourceProperties&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LazyConnectionDataSourceProxy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HikarDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hikariConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;LazyConnectionDataSourceProxy 를 사용하지 않았을 때의 결과는 아래와 같다. 데이터베이스에 쿼리를 날리지 않고, 아무런 작업을 하지 않았음에도 커넥션을 불필요하게 사용하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/11a5d0be06c73b4529a7d5c736568106/fa60d/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 20.858895705521473%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAArklEQVR42o2QyQ6DMBBD8zN0IyytKFuzEEhJKBL//zduMqiHHir1YNmjRKN5Zt562OGJ2cxwo8OoJohGQvUak7YYhIHuB/L49pllpyibINnu+cpvYG7y8HYh2bBAtBJVUSE75cjPBYpLSf5vZqtb8ZpXbMtGl97LmpbxY4b0wJEmnPySpF/zL7F4GaHKEVNAipiiEejrByEaMQZEDSN3j/8iXqwkohqqQEN1mmp4Awp1hHniJB0tAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/11a5d0be06c73b4529a7d5c736568106/a6d36/image.png&quot;
        srcset=&quot;/static/11a5d0be06c73b4529a7d5c736568106/222b7/image.png 163w,
/static/11a5d0be06c73b4529a7d5c736568106/ff46a/image.png 325w,
/static/11a5d0be06c73b4529a7d5c736568106/a6d36/image.png 650w,
/static/11a5d0be06c73b4529a7d5c736568106/e548f/image.png 975w,
/static/11a5d0be06c73b4529a7d5c736568106/3c492/image.png 1300w,
/static/11a5d0be06c73b4529a7d5c736568106/fa60d/image.png 1792w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;반대로 LazyConnectionDataSourceProxy 를 사용한 결과는 아래와 같다. LazyConnectionDataSourceProxy 를 사용하면 커넥션이 꼭 필요한 시점이 되기 전까지 커넥션을 가져오지 않는 모습을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7e6ff9c525423c6c7e24a7ff14b3911c/e0577/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 19.631901840490798%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAsUlEQVR42l1QSQ6DMBDLY6pubD10QUD2DUj//x03mQpU9WAljh2PPGwNKwqCDkjxjWgi5CBhhEW0M2w+jXBwypHHKw89aWiuSQsmQGXupMe9e4AVQ4qJwkIOG54jCddDhepYE+pTs983vqE6NrveXjqwErKGhMUvNJn3PAs3NOeW8Pt5B4X8a1/OopmpWqnoc6VSmfcC04vDSkfvZZDhFmos1RytoKxFDII8ejKZK6r+Abn/hV8K9DP2AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7e6ff9c525423c6c7e24a7ff14b3911c/a6d36/image-1.png&quot;
        srcset=&quot;/static/7e6ff9c525423c6c7e24a7ff14b3911c/222b7/image-1.png 163w,
/static/7e6ff9c525423c6c7e24a7ff14b3911c/ff46a/image-1.png 325w,
/static/7e6ff9c525423c6c7e24a7ff14b3911c/a6d36/image-1.png 650w,
/static/7e6ff9c525423c6c7e24a7ff14b3911c/e548f/image-1.png 975w,
/static/7e6ff9c525423c6c7e24a7ff14b3911c/3c492/image-1.png 1300w,
/static/7e6ff9c525423c6c7e24a7ff14b3911c/e0577/image-1.png 1796w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;커넥션을-가져오는-시점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EB%84%A5%EC%85%98%EC%9D%84-%EA%B0%80%EC%A0%B8%EC%98%A4%EB%8A%94-%EC%8B%9C%EC%A0%90&quot; aria-label=&quot;커넥션을 가져오는 시점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커넥션을 가져오는 시점&lt;/h2&gt;
&lt;p&gt;LazyConnectionDataSourceProxy 를 사용하지 않는다면, &lt;code class=&quot;language-text&quot;&gt;@Transcational&lt;/code&gt; 을 만나는 그 즉시 설정된 DataSource 에 대한 커넥션을 획득하게 된다. 그렇다면 트랜잭션이 시작되고 정확히 어떤 시점에서 커넥션을 획득하는 것일까?&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/df4ac193be746b94cb476ba4a8dff367/fa60d/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17.177914110429448%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAh0lEQVR42jWO6RKDIAyEef+nrIhHOQRBObSdbZORHzvLki87Ee260VXbhVIbq+f788X61hjkCDkqfsd0IJfKc+KI77ti2zy8DwxZt0GpiT3sEfOycon7Z/tomhe8BslOmXapQxuDPSaI48wgnbnwhw87ezpOGOu4kMop00wby9fSLDwc8Z35AfAp5KSvnsliAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/df4ac193be746b94cb476ba4a8dff367/a6d36/image-2.png&quot;
        srcset=&quot;/static/df4ac193be746b94cb476ba4a8dff367/222b7/image-2.png 163w,
/static/df4ac193be746b94cb476ba4a8dff367/ff46a/image-2.png 325w,
/static/df4ac193be746b94cb476ba4a8dff367/a6d36/image-2.png 650w,
/static/df4ac193be746b94cb476ba4a8dff367/e548f/image-2.png 975w,
/static/df4ac193be746b94cb476ba4a8dff367/3c492/image-2.png 1300w,
/static/df4ac193be746b94cb476ba4a8dff367/fa60d/image-2.png 1792w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;트랜잭션을 보통 위와 같이 동작한다. 트랜잭션이 적용되어 있는 객체의 CGLIB 프록시가 호출이 되면 트랜잭션이 시작된다. 그 후 트랜잭션의 커밋이나 롤백등을 관리하는 &lt;code class=&quot;language-text&quot;&gt;TransactionManager&lt;/code&gt; 의 인터페이스를 가져오고, 설정된 DataSource 를 바탕으로 커넥션을 생성한다. 그러고 난 뒤에서야 뒤 늦게 &lt;code class=&quot;language-text&quot;&gt;TransactionSynchornizationManager&lt;/code&gt; 를 통해 트랜잭션을 유지할 수 있게 같은 커넥션을 사용하도록 보장한다. &lt;code class=&quot;language-text&quot;&gt;TransactionSynchornizationManager&lt;/code&gt; 와 관련한 자세한 내용은 &lt;a href=&quot;https://haon.blog/spring/transaction-synchornization-and-abstraction/&quot;&gt;스프링 트랜잭션 동기화와 추상화 (feat. TransactionManager)&lt;/a&gt; 에서 다룬적이 있다.&lt;/p&gt;
&lt;p&gt;즉, &lt;code class=&quot;language-text&quot;&gt;TransactionSynchornizationManager&lt;/code&gt; 가 DataSource 정보를 읽어오는 시점은 이미 DataSource 가 결정되고 커넥션까지 모두 생성된 뒤에서야 그 정보를 읽어오게 된다. 그런데, 만약 &lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourceProxy&lt;/code&gt; 를 사용하면 데이터베이스를 접근하기 전 (쿼리를 날리기 직전) 타이밍이 되었을 때 커넥션을 가지고 오게 된다. 앞서 설명했듯이, &lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourceProxy&lt;/code&gt; 를 사용하면 실제로 데이터베이스와 쿼리를 주고받고 통신하며 커넥션을 획득해야 하는 시점까지 커넥션 획득을 지연시킬 수 있기 때문이다.&lt;/p&gt;
&lt;h2 id=&quot;multi-datasource-환경에서-lazyconnectiondatasourceproxy-의-역할&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#multi-datasource-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-lazyconnectiondatasourceproxy-%EC%9D%98-%EC%97%AD%ED%95%A0&quot; aria-label=&quot;multi datasource 환경에서 lazyconnectiondatasourceproxy 의 역할 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Multi DataSource 환경에서 LazyConnectionDataSourceProxy 의 역할&lt;/h2&gt;
&lt;p&gt;이전 포스팅에서 다루었듯이, 데이터베이스 레플리케이션 환경에서 원활한 라우팅을 위해 &lt;code class=&quot;language-text&quot;&gt;AbstractRoutingDataSource&lt;/code&gt; 를 사용하고 있다. 여기서  &lt;code class=&quot;language-text&quot;&gt;determineCurrentLookupKey()&lt;/code&gt; 메소드를 사용해서 DataSource 를 찾는다. 그렇다면, LazyConnectionDataSourceProxy 를 설정안하고 &lt;code class=&quot;language-text&quot;&gt;AbstractRoutingDataSource&lt;/code&gt; 만 사용해서 분기를 할 수 있을 것 같지만, 그렇지 않다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//  AbstractRoutingDataSource : Multi DataSource 환경에서 여러 DataSource 를 묶고 분기해줄 때 사용한다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AbstractRoutingDataSource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// determineCurrentLookupKey 메소드 : 여러 datasource 중에서 실제로 사용될 DataSource 를 결정하는 역할&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 현재 트랜잭션의 속성에 따라 targetDataSourceMap 의 조회 Key 를 결정하기위해 AbstractRoutingDataSource 를 상속받아서 determineCurrentLookupKey 를 구현했다.&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;determineCurrentLookupKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; isReadOnly &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionSynchronizationManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isCurrentTransactionReadOnly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;현재 트랜잭션 속성이 ReadOnly인가?:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; isReadOnly&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; isReadOnly &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;slave&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;내부적으로 &lt;code class=&quot;language-text&quot;&gt;TransactionSynchornizationManager&lt;/code&gt; 를 통해서 트랜잭션의 Read Only 여부를 확인하고 있다. 그런데 앞서 학습했듯이, 스프링은 기본적으로 커넥션을 &lt;code class=&quot;language-text&quot;&gt;TransactionManager&lt;/code&gt; 에서 &lt;code class=&quot;language-text&quot;&gt;TransactionSynchornizationManager&lt;/code&gt; 으로 넘어갈 때 가져온다. 따라서 &lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourceProxy&lt;/code&gt; 를 사용하지 않고서는 Read Only 여부에 따라 다른 DataSource 를 선택할 수 없게 된다.&lt;/p&gt;
&lt;p&gt;결론적으로, &lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourceProxy&lt;/code&gt; 통해 커넥션을 획득하는 시점을 실제 데이터베이스와 쿼리로 통신하는 시점으로 미루기 때문에, &lt;code class=&quot;language-text&quot;&gt;TransactionSynchornizationManager&lt;/code&gt; 를 통해 트랜잭션 Read Only 여부를 파악해 알맞은 DataSource 로 라우팅 해줄 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@yyong3519/LazyConnectionDataSourceProxy%EB%9E%80&quot;&gt;https://velog.io/@yyong3519/LazyConnectionDataSourceProxy란&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kookiencream.tistory.com/135#1.%20LazyConnectionDataSourceProxy%EB%9E%80%3F-1&quot;&gt;https://kookiencream.tistory.com/135#1.%20LazyConnectionDataSourceProxy%EB%9E%80%3F-1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Redis가 제공하는 RedLock 알고리즘 간단히 살펴보기]]></title><description><![CDATA[지난 Redis 분산 락(Distribution Lock)을 구현하여 동시성 해결하기 에서 다루었듯이, Redis 는 모니터(monitor) 기반의 상호 배제(mutual exclusion) 기법인 분산 락(Distribution Lock…]]></description><link>https://haon.site/database/redis-redlock/</link><guid isPermaLink="false">https://haon.site/database/redis-redlock/</guid><pubDate>Thu, 12 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6d008d5502ef05ea1ca2e0ba65bc548b/9685e/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.533742331288344%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABfUlEQVR42pXSzStscRzH8ck483Cez5gzjx5mjnGdkRALdBNNoWRDjcLCwlJhYePeSxEW8pAUW2Uh5R+gLO7d3fhP7G3ffvMgk2YmFq/O4nc+n985v9/XY2gRPtPLT1Vo8IfwSiY+v4VXsZFVm2qZdx5Tj1KNIdhGjFy7y2lulOupSWbjSVpDcWplCuoW7jspXjbXeT054GV5nv9dDlfZDmJiI12sW18ptMplIeGyw+HRTfM395N/Y8M8uA632QzNZgztq4WVLjJpnns7uf+RKpY99WW56XSIf+cLK3+527BZTyS5czPcpJtZsSP0mOLw9UjtM6x3Y+FoCs1K0qI0MTEwRP/QGJqZqH/LmhiDSoWRCfhNnJTLn1/bHB0es7q2we+tHc7PztnbPcBuakGVw+hqRGRK9DKPqoQpsYsKpQExc4mEw+DgCNPTM0yMTzGXXyAvLC4uEYu2EQyEiu9+5Es8imxRjRw08ftUfJKCJMliE41GbxCpURZrBrVyb0M5EXArLQavAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/6d008d5502ef05ea1ca2e0ba65bc548b/a6d36/image.png&quot;
        srcset=&quot;/static/6d008d5502ef05ea1ca2e0ba65bc548b/222b7/image.png 163w,
/static/6d008d5502ef05ea1ca2e0ba65bc548b/ff46a/image.png 325w,
/static/6d008d5502ef05ea1ca2e0ba65bc548b/a6d36/image.png 650w,
/static/6d008d5502ef05ea1ca2e0ba65bc548b/e548f/image.png 975w,
/static/6d008d5502ef05ea1ca2e0ba65bc548b/3c492/image.png 1300w,
/static/6d008d5502ef05ea1ca2e0ba65bc548b/9685e/image.png 1336w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;지난 &lt;a href=&quot;https://haon.blog/database/redis-distribution-lock/&quot;&gt;Redis 분산 락(Distribution Lock)을 구현하여 동시성 해결하기&lt;/a&gt; 에서 다루었듯이, Redis 는 &lt;strong&gt;모니터(monitor) 기반의 상호 배제(mutual exclusion) 기법인 분산 락(Distribution Lock) 을&lt;/strong&gt; 제공한다. 이 분산 락은 &lt;code class=&quot;language-text&quot;&gt;RedLock&lt;/code&gt; 알고리즘에 기반하여 구현되어있다. 이 RedLock 알고리즘은 어떠한 방식으로 동작할까? 이를 간단히 학습해보도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;redis-set-nx&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-set-nx&quot; aria-label=&quot;redis set nx permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis SET NX&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;RedLock&lt;/code&gt; 은 분산 환경에서 Redis 가 권장하는 락을 제공하는 방법이다. Redis &lt;code class=&quot;language-text&quot;&gt;SET&lt;/code&gt;
명령어에 &lt;code class=&quot;language-text&quot;&gt;NX 옵션&lt;/code&gt;을 통한 락을 제공하는 방법과 한계, RedLock 의 특징과 한계에 대해 알아보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;SET key value NX PX 30000
// key, value 를 저장하는데 Not Exists 일 경우에만 저장하고, 30초(30,000ms) 동안 유지해줘&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Redis 2.6.12 버전 이전에는 &lt;code class=&quot;language-text&quot;&gt;SETNX&lt;/code&gt; 명령어가 제공되었지만, 2.6.12 버전 이후 부터는 SETNX 명령어는 deprecated 되었고, &lt;strong&gt;SET 명령어에 NX 옵션을 전달하는 방향으로 수정했다. NX 옵션을 전달하면 SET 하려는 key 가 없는 경우에만 SET 이 성공한다.&lt;/strong&gt; Redis 는 싱글 쓰레드로 동작하기 때문에 여러 프로세스가 공유 자원에 접근할 때 발생하는 동시성 문제를 이 명령어로 해결할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;즉, 먼저 접근한 쓰레드가 NX 옵션을 전달한 SET 에 성공한다면 다른 쓰레드들은 대기한다.&lt;/strong&gt; 여기서 다른 쓰레드들이 대기하도록 while 과 같은 루프와 sleep 같은 함수는 개발자가 직접 제공해야 한다. 그리고 락을 획득해 먼저 자원을 선점한 쓰레드는 작업을 끝낸 후 key 를 제거한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;DEL key&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;여기서 단순히 &lt;code class=&quot;language-text&quot;&gt;DEL&lt;/code&gt; 명령어로 key 를 삭제하면, 락을 획득하지 않은 다른 클라이언트들도 삭제가 가능하다. 따라서 key 가 존재하고 값이 일치할 때만 삭제할 수 있도록 아래처럼 Lua 스크립트를 통해 삭제할 것을 권장한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;get&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;KEYS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ARGV&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; then
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;del&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;KEYS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
end&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;여기서 문제점은, Redis 가 단일 서버 1대로 동작하면 SPOF 가 된다는 점이다. 이를 위해 Master-Slave 레플리케이션 구조를 도입할 경우, 복제구조가 비동기 방식이기 때문에 Lock 데이터에 대한 정합성 문제(경쟁 상태)가 발생할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;redlock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redlock&quot; aria-label=&quot;redlock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedLock&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;RedLock 은 N대의 Redis 서버가 있다고 가정할 때, 과반 수 이상의 노드에서 락을 획득했다만 락을 획득한 것으로 간주한다.&lt;/strong&gt; RedLock 알고리즘 동작은 간단히 설명하면, 아래처럼 동작한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 클라이언트가 락을 획득하기 위해 모든 Redis 서버에게 락을 요청한다. 과반 수 이상의 Redis 서버에게 락을 획득하면 락을 획득하게 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 과반수 이상의 락 획득에 실패했다면, 모든 Redis 서버에게 락 해제를 요청하고 일정 시간 후에 락을 획득하기 위해 재시도한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;redlock-은-완벽하지-않다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redlock-%EC%9D%80-%EC%99%84%EB%B2%BD%ED%95%98%EC%A7%80-%EC%95%8A%EB%8B%A4&quot; aria-label=&quot;redlock 은 완벽하지 않다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedLock 은 완벽하지 않다.&lt;/h3&gt;
&lt;p&gt;RedLock 알고리즘 또한 문제가 발생할 수 있다. 아래 코드는 락을 획득 후 파일에 데이터를 저장하는 코드이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// THIS CODE IS BROKEN&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;writeData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; lock &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lockService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;acquireLock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;lock&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Failed to acquire lock&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; file &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; storage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; updated &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateContents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    storage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; updated&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    lock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;release&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드에서 어떻게 문제가 발생할 수 있을까?&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1863e68187df9e03e28f495fb4db2dc4/d4b10/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.94478527607362%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABGklEQVR42oWS24qDUAxF/f+PK/goRa2KWm8trXerpqxARPowEwhHk5i9kqOz77uYY5zLsqh/Ph85Gzli5uc49ZhTVZXc73cZhkETWZZJHMfqeZZL13XS973M8yxFUUiSJJqjrm1bzVtu2zZxns+nBsdhlDRNtZhCM0iiKJI8z/Ucx/GgosF5OiVsmuYggPbxeIjv+3ILb+J5nvrlchHXdeV6vUoYhtoccSjf77dOhxCNnXVddX5OAvYOJcWcr9dLgiAQxOu6VnEjZ10YtEpIAR9R8DuGjYJIWZRKTxOIyrLUyRDhGyCUkEZG+JcZPSNzAYyNITJN0yHuoEQQVUM/u5HaM+LsDjqM5uzWLtKxfZxv6j/7/W/pYTv8AgXtuKt4/DAxAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/1863e68187df9e03e28f495fb4db2dc4/a6d36/image-1.png&quot;
        srcset=&quot;/static/1863e68187df9e03e28f495fb4db2dc4/222b7/image-1.png 163w,
/static/1863e68187df9e03e28f495fb4db2dc4/ff46a/image-1.png 325w,
/static/1863e68187df9e03e28f495fb4db2dc4/a6d36/image-1.png 650w,
/static/1863e68187df9e03e28f495fb4db2dc4/e548f/image-1.png 975w,
/static/1863e68187df9e03e28f495fb4db2dc4/3c492/image-1.png 1300w,
/static/1863e68187df9e03e28f495fb4db2dc4/d4b10/image-1.png 1394w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트 A 가 마스터 서버에서 락을 획득한다.&lt;/li&gt;
&lt;li&gt;클라이언트 A 에서 &lt;code class=&quot;language-text&quot;&gt;Stop-The-World GC&lt;/code&gt; 로 인해 애플리케이션 코드 중지가 발생하고, 그 사이에 락이 만료된다.&lt;/li&gt;
&lt;li&gt;클라이언트 B 가 분산 락을 획득하고 파일에 데이터를 쓴다.&lt;/li&gt;
&lt;li&gt;클라이언트 A 가 GC 가 끝난 후 파일에 데이터를 쓰면서 동시성 문제가 발생한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;글로벌 트랜잭션에 대한 이론은 &lt;a href=&quot;https://haon.blog/database/global-transaction/&quot;&gt;글로벌 트랜잭션 (feat. GTID)&lt;/a&gt; 에서 다룬적이 있으니, 참고하자. 일반적으로 글로벌 트랜잭션은 매우 빠르게 수행되지만, Stop-the-World 글로벌 트랜잭션은 드물게 락이 만료될 정도로 지속될 수 있다. GC 말고도 네트워크 지연이나 Timing 이슈에 따라 RedLock 이 깨질 수 있음을 유의하자.&lt;/p&gt;
&lt;p&gt;RedLock은 Redis가 공식적으로 권장하고 있는 분산 락 알고리즘이다. 이 방법을 사용해 공유 자원에 대한 동시성 문제를 해결할 수 있다. 다만 발생할 확률이 낮기는 하지만 RedLock도 완벽하지 않다는 것은 이해하고 있어야 한다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://redis.io/docs/manual/patterns/distributed-locks/&quot;&gt;https://redis.io/docs/manual/patterns/distributed-locks/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/311&quot;&gt;https://mangkyu.tistory.com/311&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[토스 SLASH 23, 은행 최초 코어뱅킹 MSA 전환기(feat. 지금 이자 받기)]]></title><description><![CDATA[💡 현재 포스팅은 토스 SLASH 23, 코어뱅킹 MSA…]]></description><link>https://haon.site/article/toss-slash/core-banking-msa/msa-conversion/</link><guid isPermaLink="false">https://haon.site/article/toss-slash/core-banking-msa/msa-conversion/</guid><pubDate>Wed, 11 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스팅은 &lt;a href=&quot;https://www.youtube.com/watch?v=amTJyIE1wO0&amp;#x26;t=1237s&quot;&gt;토스 SLASH 23, 코어뱅킹 MSA 전환기&lt;/a&gt; 를 듣고 정리한 내용이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;400&quot; src=&quot;https://www.youtube.com/embed/amTJyIE1wO0?si=o1JKz_Tpz801Stfu&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;/iframe&gt;`
&lt;p&gt;토스뱅크는 기존의 코어뱅킹 서비스를 고객 중심으로 변화시키기 위해, 금융의 혁신을 위해 많은 노력을 기울이고 있다. 그런데, 문제가 있다. 기존의 전통적인 뱅킹 시스템을 구현하는 방식으로는 안정적인 고객 중심 뱅킹 서비스에 한계가 있었다. 토스뱅크는 어떤 기술로 고객 중심의 뱅킹 서비스를 제공할 수 있었을까?&lt;/p&gt;
&lt;h2 id=&quot;전통적인-금융-시스템&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%ED%86%B5%EC%A0%81%EC%9D%B8-%EA%B8%88%EC%9C%B5-%EC%8B%9C%EC%8A%A4%ED%85%9C&quot; aria-label=&quot;전통적인 금융 시스템 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전통적인 금융 시스템&lt;/h2&gt;
&lt;h3 id=&quot;채널계와-코어뱅킹계정계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B1%84%EB%84%90%EA%B3%84%EC%99%80-%EC%BD%94%EC%96%B4%EB%B1%85%ED%82%B9%EA%B3%84%EC%A0%95%EA%B3%84&quot; aria-label=&quot;채널계와 코어뱅킹계정계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;채널계와 코어뱅킹(계정계)&lt;/h3&gt;
&lt;p&gt;일반적인 금융권 시스템은, 특히 코어뱅킹 서버는 매우 거대한 모놀리식 아키텍처로 구성되어 있다. 은행에는 크게 고객의 요청을 코어뱅킹 서버로 전달하는 채널계와, 금원과 관련된 메인 비즈니스 로직을 처리하는 코어뱅킹(계정계) 라고 하는 2개의 서버를 중심으로 아키텍처가 구성되어있다.&lt;/p&gt;
&lt;h3 id=&quot;코어뱅킹-시스템-아키텍처-역사&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BD%94%EC%96%B4%EB%B1%85%ED%82%B9-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%97%AD%EC%82%AC&quot; aria-label=&quot;코어뱅킹 시스템 아키텍처 역사 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;코어뱅킹 시스템 아키텍처 역사&lt;/h3&gt;
&lt;p&gt;코어뱅킹 시스템은 왜 거대한 모놀리식 아키텍처를 유지해왔을까? 이 이유는 금융 시스템의 변천사를 이애하면 그 정답을 알 수 있다.&lt;/p&gt;
&lt;p&gt;1970년대부터 은행의 계좌 데이터를 적절하게 가공하고 처리해야 하는 니즈가 생기면서, 1세대와 2세대 코어뱅킹 아키텍처가 생겨났고, 2000년대에 디지털 붐이 일면서 모바일 뱅킹, 웹 뱅킹, 텔레뱅킹 등 다양한 거래 요청을 한 곳에서 적절하게 처리해줄 수 있도록 현재의 모놀리식 코어뱅킹 아키텍처가 생겨나게 되었다.&lt;/p&gt;
&lt;p&gt;지난 20여년간 코어뱅킹 아키텍처는 운영체제와 개발 언어의 크고 작은 변화는 있었지만, 현재의 모바일 트렌드와는 맞지 않는 20년전의 모놀리식 아키텍처를 대부분의 은행에서 사용한다. MSA 의 구조를 취하지 않고, 현재의 거대한 모놀리식 형태로 몸집을 더 키워나가고 있었다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cdf12c76f4744fb7403c01aafb79d970/229ad/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.21472392638037%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACU0lEQVR42nWT60uTYRjGd3B7d97cdLhDLp3RCNmc0OzDgpzRIKw2j0unm6fMA9VUSCWyA5UgEQQVQRSRX/xSRGme6XN0/It+Pe8rrhX24eK+74f3vp7rue/rVanVaiRJwmKxYDabMRgMWK3WYu1wOJRao9Gg1WoVyD0qlaoIvV6Pz+fD7XajksnC4TAtiQSxE01EGhpIiDyZTBKJRIhGo9TX1/9FcBBKLtkr1EKBSS9hMxgxGyTlrKa2lsLUNKOXx8jlB7g0Ns616Rlqg0GluVS1nBcJFVKNFpfJhEunwyYgn3m8Xvrzg6TaO+nu7GK4K8NIbx/Vh6r/VVWKvUS+RZ5F6YcanYRGsuM5HCIWb+FY+Dj+mhCSyaqMKVhXR09vlmxfvxJ1e/0qDEahrMKNu8qD0WTGZrMTj8exOly09QyycG+J9Z3PrG7t8uzlGyo9fubmZkm1tfH1x0+BX3z59l1ZpEKYG73C4PgU6UyOqfk7gtjH9dlZ9EYL87cfcOv+Eu9XN3j3YY1HT55jd1aSSqc51ZxgbWOTze0dtrZ3FVcohGfTFznXkeXk6VYyA2P4AzXcXFjAVl7BjbuLLD58zMf1bT4Jha+WVzBa7TQ2RvcJMAtl5U7n/qhUdPTk6cwOcaa1neGJAhXuKsYnJjCIWSUvZEgJ5U9fvGZ55S0jk9NCoVu8YI6joRBXCwVisabi1lX/85S8JMloptwbxOj0cr47x9DkDC7/EaHcRS6Xx+f30yyeHQgESpcpEqWQo1qJf8gFcVmZAo38h8i+E7ni24Mtw296LUxmYgLF/QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/cdf12c76f4744fb7403c01aafb79d970/a6d36/image-1.png&quot;
        srcset=&quot;/static/cdf12c76f4744fb7403c01aafb79d970/222b7/image-1.png 163w,
/static/cdf12c76f4744fb7403c01aafb79d970/ff46a/image-1.png 325w,
/static/cdf12c76f4744fb7403c01aafb79d970/a6d36/image-1.png 650w,
/static/cdf12c76f4744fb7403c01aafb79d970/e548f/image-1.png 975w,
/static/cdf12c76f4744fb7403c01aafb79d970/3c492/image-1.png 1300w,
/static/cdf12c76f4744fb7403c01aafb79d970/229ad/image-1.png 1356w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;현재 토스뱅크의 채널계는 기존 토스 전반 생태계의 DNA를 이어받아 모두 MSA 환경으로 구성되어 있다. 하지만, &lt;strong&gt;기존의 토스 코어뱅킹 시스템은 여타 은행과 다름없이 채널계와의 통신을 위한 모놀리식 시스템으로 구성되어 있었다.&lt;/strong&gt; 기존의 토스 코어뱅킹 시스템은 Redis, Kafka 등의 모던한 기술을 사용하고는 있었지만, 여타 은행과 다름없이 채널계와의 통신을 위한 &lt;strong&gt;MCI&lt;/strong&gt;, 대외연계를 위한 &lt;strong&gt;FEP&lt;/strong&gt;, 대내 단위 시스템과의 연계를 위한 &lt;strong&gt;EAI&lt;/strong&gt; 가 코어뱅킹 서버에 강하게 결합되어 있는 구조를 취하고 있었다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 &lt;code class=&quot;language-text&quot;&gt;용어 정리&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MCI(Message Channel Interface)&lt;/strong&gt; : 시스템 간의 데이터 교환을 위해 사용하는 메시지 기반 통신 인터페이스&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FEP(Front-end Processor)&lt;/strong&gt; : 메인 시스템 앞단에서 데이터 통신을 처리하는 전처리 장치 또는 프로세서&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EAI(Enterprise Application Integration)&lt;/strong&gt; : 조직 내 여러 이기종 애플리케이션간의 통합 및 데이터 교환을 가능하게 하는 소프트웨어 아키텍처&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;모놀리식-아키텍처의-한계-msa-로의-전환&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A8%EB%86%80%EB%A6%AC%EC%8B%9D-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%9D%98-%ED%95%9C%EA%B3%84-msa-%EB%A1%9C%EC%9D%98-%EC%A0%84%ED%99%98&quot; aria-label=&quot;모놀리식 아키텍처의 한계 msa 로의 전환 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;모놀리식 아키텍처의 한계. MSA 로의 전환&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/214d02857638bebe02f58bc55ea815dc/c45c7/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.14723926380368%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB9UlEQVR42p1SPW/TUBT1txPHtR3bSRwDQaSkTiOXOmmUxLFQQAoQSqsOQKOKJUItCxIIhJBYGFgYUAcGfgALI0xhAKkMLIwsDPyag99V1Tgrw5HfPee8857vu5wkSXAcB6VSCZZloVKpwPd9GIYB13UJ1WqVwNae56FcLsM0TXAcR1AUhTjbtsFpmoZOp4MkSRCGIeI4xng8RhAE6Pf7GAwG6Ha7GA6H6PV6pI9GI0RRRGE8z58Fn4LDf0MUwckMEjiBXwRmT2FrBlmWwdrBvoIgkJbLqdB1/cwvaDnYbgmakf6+JGYDOWhaPt2QI5IFsX5YVpH6ykJ5XkCjGSLa6qFgWuSzkwH8j+9w/s0LyHltEaioObTCTQStDciKSsKdvXs4fv8BD2ZHVBedMj7Pv+P3n7+4fmuXuPrkNpL5J7SP30ItrCwC1zc6OPn5C/NvP7C61iJh9ugpvnw9wZOXr6k2iy52p0fY3j9E7XJIXCNoY+fmAeLBdnqpzA39iw3MHr/C9OEzuF6NhLXoKrau3UUjGoFPe6gbNuIb+0gmB2ngFfK02gnuHz5HPJlCzRcWgWp+BfX1Ni6shpBPT3LS4OZmH16tQbUoK6g3I7SiGLrlEmc5FfKcu9SEKMnLYyOlrySKwtJYKIq8NAFMZw+W9TAuO4v/AAb1Bu9R5XhrAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/214d02857638bebe02f58bc55ea815dc/a6d36/image.png&quot;
        srcset=&quot;/static/214d02857638bebe02f58bc55ea815dc/222b7/image.png 163w,
/static/214d02857638bebe02f58bc55ea815dc/ff46a/image.png 325w,
/static/214d02857638bebe02f58bc55ea815dc/a6d36/image.png 650w,
/static/214d02857638bebe02f58bc55ea815dc/e548f/image.png 975w,
/static/214d02857638bebe02f58bc55ea815dc/3c492/image.png 1300w,
/static/214d02857638bebe02f58bc55ea815dc/c45c7/image.png 1346w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;토스 코어뱅킹 시스템은 과거의 모놀리식 아키텍처를 금융권 최초로 MSA로 전환하였다. 모놀리식 코어뱅킹 아키텍처는 어떤 한게가 있었기에 MSA 로 전환을 했을까?&lt;/p&gt;
&lt;p&gt;모놀리식 아키텍처도 몰론 장점이 있긴하다. 로컬 트랜잭션으로 하위 도메인 데이터를 ACID 하게 변경할 수 있고, 개발하게도 쉽다는 점이다. 하지만, &lt;strong&gt;SPOF(Single Point Of Failure)&lt;/strong&gt; 에 취약하다. 모놀리식으로 구성된 코어뱅킹 시스템에 트래픽이 몰린다면, 특정 코어뱅킹 시스템만 스케일 아웃하는 전략을 취할 수 없다.&lt;/p&gt;
&lt;p&gt;토스뱅크는 대량 트래픽에 특화되어 있고, 각 업무별 서비스 영향도를 분리할 수 있는 MSA로 전환하기로 했다. 그 중에서도 토스뱅크 서비스 중 가장 트래픽이 많은 &lt;strong&gt;이자 받기 서비스를 모놀리식 코어뱅킹 시스템에서 분리하여 MSA로 전환하기로 했다.&lt;/strong&gt; 지금부터 토스뱅크가 이자 받기 서비스를 어떻게 MSA 로 전환했는지에 대해 알아보자.&lt;/p&gt;
&lt;h4&gt;기술 스택&lt;/h4&gt;
&lt;p&gt;MSA 로의 전환을 위해, 토스뱅크 채널계에서 사용하는 기술들을 대부분 채택했다. 쿠버네티스위에 스프링부트, 코틀린, JPA 등을 기반으로 개발했고, 비동기 메시지 처리와 캐싱은 Kafka, Redis 를 사용하기로 결정했다.&lt;/p&gt;
&lt;h2 id=&quot;지금-이자-받기-기능-as-is-흐름&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%80%EA%B8%88-%EC%9D%B4%EC%9E%90-%EB%B0%9B%EA%B8%B0-%EA%B8%B0%EB%8A%A5-as-is-%ED%9D%90%EB%A6%84&quot; aria-label=&quot;지금 이자 받기 기능 as is 흐름 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&quot;지금 이자 받기&quot; 기능 AS-IS 흐름&lt;/h2&gt;
&lt;h3 id=&quot;msa-아키텍처-구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#msa-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;msa 아키텍처 구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MSA 아키텍처 구조&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ea3610017abf06c6bec75cb1cab4ed73/d0c2f/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.239263803680984%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABiElEQVR42pWRTUsbURiFJzM1iDYzzmS+xZmJSZPYOrVFI34imoAoCm0HWyLtQrCu4qIqKEhXLe1GQfqHn869mBBxlcWBc8+B574vr1IoFLBtG8/zME0TRVFGVrFYxHVdDMNAUVWVTqdDlmW0220ZCnC5XEZ0I3/Qn1D84DgOjusQhiFxHI8EFBwh5VkwpEIO1LQXea7KTvo8UzVNqt+rqjYMV2RRDhKssIY9XWPCCqWsoMKY7hK9SonqKWMlF9NPZDc+Fci+qHtMmL5kDIBiNTOHrayvUWmmfOye8On4hOp8i7PzK9Z29lne2uX7+SWNhRUOj77y+dsps68XZbbR3h9sOVg5qr9ls7NHPW3Ru7ihd3nDm6UN/tw9cJAds/fhC39z/251m7PeBT+uf9J8v5pn/zjMuk+BwvhJk7C2QFBNKXkVdH8WO2pKf/v7nttfd7x0E+yZBkZQlbkbzzHpxOheMnxAeWpKlofhTKPbIaY3IyXelh/RWt+WEl5k/X7qsdftIAfJ6fgPGGXooAdzXqwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/ea3610017abf06c6bec75cb1cab4ed73/a6d36/image-2.png&quot;
        srcset=&quot;/static/ea3610017abf06c6bec75cb1cab4ed73/222b7/image-2.png 163w,
/static/ea3610017abf06c6bec75cb1cab4ed73/ff46a/image-2.png 325w,
/static/ea3610017abf06c6bec75cb1cab4ed73/a6d36/image-2.png 650w,
/static/ea3610017abf06c6bec75cb1cab4ed73/e548f/image-2.png 975w,
/static/ea3610017abf06c6bec75cb1cab4ed73/3c492/image-2.png 1300w,
/static/ea3610017abf06c6bec75cb1cab4ed73/d0c2f/image-2.png 1362w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;우선, 토스뱅크가 MSA 구조를 어떻게 구성했는지 전반 아키텍처 플로우에 대해 알아보자. 토스뱅크는 MSA 전환을 시작하자마자 첫 번째 고민에 봉착했다. 현재 모놀리식으로 강하게 결합된 업무별 비즈니스 의존성을 어느 정도까지 느슨하게 가져갈 것인가이다. &lt;strong&gt;지금 이자 받기 서비스&lt;/strong&gt;를 위해 필요한 도메인은 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 고객 정보 조회, &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 금리조회 및 이자계산, 그리고 &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 이자의 회계 처리를 위한 회계 정보 조회 및 처리. 이 모든것을 하나의 마이크로 서버에서 처리하는 것은 MSA의 장점을 활용하지 못할 것이라 판단하여, 도메인 단위로 서비스를 나누기로 결정했다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0be63af50f8fad49e4b078ecb3def544/21482/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.306748466257666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABsElEQVR42pWRX0/TUBjGz9Z2ZaUr69zarQXGNqFbB1mYOpIFRE0aXcKMOhMxkADRxMiNiVx4Y/TGRG+88SMYE2OI8cbv97OnMAThQi6e8/49z3nP8wohBIqioKpqYmV8Wei6ju/7OI6DyGQyRFHEcDik1+slDalUinQ6/d+Esl/TtKOB5BEEAWEY0mg0qFQ8qtU5WmELwzBOLgjxFzL+N3fqAZFMcwZxsxLbo0uXlkCcmiImV1T82gKON0t20qLeXGSqUGLCnMIsuORLHuWZegLb8TFtl2zOPk8oUchPomcNbty8R7vbxy75rA9G+PUAZ6ZBeH0N7+oS4bU12sd+q7sa11vnCU0rT3+lg1t2+fj5C68O3hAsdvj6/ZDb0YDR5jaHv37TWVnn3YdPvI/R7d/h24+f7Dx7wVi6E0K5gFzOImuYPNjc5dbd+xTcafb2X7PQXqbozcWTreJUmwwePmXj8XbiN5f7eLXg4i+PNXyy+5Jo41GsV4X9g7c0l7pMWEWKswH5ci15cLT1HLtS58r0PFYszYVLOQu5ceW4lkZRNdSMjqrpSSwhfZmTtTHhH7Lc+xqcCHkAAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/0be63af50f8fad49e4b078ecb3def544/a6d36/image-3.png&quot;
        srcset=&quot;/static/0be63af50f8fad49e4b078ecb3def544/222b7/image-3.png 163w,
/static/0be63af50f8fad49e4b078ecb3def544/ff46a/image-3.png 325w,
/static/0be63af50f8fad49e4b078ecb3def544/a6d36/image-3.png 650w,
/static/0be63af50f8fad49e4b078ecb3def544/e548f/image-3.png 975w,
/static/0be63af50f8fad49e4b078ecb3def544/3c492/image-3.png 1300w,
/static/0be63af50f8fad49e4b078ecb3def544/21482/image-3.png 1350w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;과거의 고객의 지금 이자 받기 요청은 고객 정보 조회를 거쳐, 금리 조회와 이자계산, 이자 송금, 회계처리를 1개의 트랜잭션으로 처리하고 있었다. MSA 로 전환한 새로운 코어뱅킹 아키텍처에서는, &lt;strong&gt;트랜잭션으로 엮이지 않아도 되는 도메인은 별도의 마이크로 서버로 구성했다.&lt;/strong&gt; 그리고 각 서버의 API 호출을 통해 비즈니스 의존성을 느슨하게 가져가도록 구성했다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt; 💡 아키텍처를 보면서 궁금한점은, 각 마이크로 서버간의 데이터베이스는 어떻게 구성되어있고, 공유하는가이다. 정확한 구현 방식은 안그래도 조만간 토스뱅크 면접이 잡혀있으니, 한번 어쭤봐야겠다 😎&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;동시성-제어&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B4&quot; aria-label=&quot;동시성 제어 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동시성 제어&lt;/h3&gt;
&lt;p&gt;은행 시스템의 안정성과 직결되는 부분은 동시성 처리이다. 토스뱅크 또한 동시성 처리를 위해 많은 고민이 있었다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/59990a1400377582e3e7ac351690e741/f8915/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 73.61963190184049%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAACY0lEQVR42qVUyW7TUBR17MRTnKEZ7SQKNEFhaJJSRBlatiQMEu0PQGEFVb8AgYC27ECgAjsQY1umii1iKlNBICGWiEGVqkp8x+He19iRaVAXLI5s3/fuefeec58lSZLACAQCHtzYWvhHjoSeahXTs/cxdfUa7tybwclTp8WiLMurSNzY4SMjmJl9gMtTV/Dw8RwazWabcPfAAL79+Im37z/g+69F3L477SWrqgorHEYwGBRQFEWsjU+ex+LSMp6/msfS8m+MHD3WJgxTQrFYRKlURi6XRyQS8SqKxmIolzfAdhw4BDdumibyhQK6u0vIZrPQNX1lrd7bi6fPXuDR3BNs7dsmgpZlCYJ0JiMSHTqkVC7DtrPoSiToOwcrYkHTdXpG/LLE43HsP3AQjUYTBTqRg4eGhvH63QKu37yFaDRKbWvQdUOsXbh4CQsfP+H4iVGvA9cgr2UXXBk/ubW9jQa29/cjlU6jUtmIZDIppKjV62g294mKhVytHA+GYaBaq2HT5i1IJJKrxoZN6KK44+SQy+d91bBJJunvI+wj3T5/+YqX82+wY+cuqJqGWCyOOCFGcrBOqXQGYTPsOc9ETKooHQhDoZAwIJPJCiKuyB3WIG3gDvJUGYuvqiF/9UTMEyLLAXFIa0bbG3gx2JqzdUR0plIRuq6n0bBtR4zHmi27bfAmJlTkFcIobe4hMrklPBuVSqWgaey4LmAYpjjo3MQkbtBEDA7u8VfILrIuf183JmQ9uUWdKmddmdigGbXJrNGxMZwdn3Dn2H9P3XYCrJH7LjSVO/4gOtx3Cf8LNpLB5H8AkOWEdu/8aPIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/59990a1400377582e3e7ac351690e741/a6d36/image-4.png&quot;
        srcset=&quot;/static/59990a1400377582e3e7ac351690e741/222b7/image-4.png 163w,
/static/59990a1400377582e3e7ac351690e741/ff46a/image-4.png 325w,
/static/59990a1400377582e3e7ac351690e741/a6d36/image-4.png 650w,
/static/59990a1400377582e3e7ac351690e741/f8915/image-4.png 748w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt; 💡 이와 거의 똑같은 동시성 처리에 관하여 &lt;a href=&quot;https://haon.blog/database/optimistic-pessimistic-lock/&quot;&gt;JPA 낙관적 락과 비관적 락으로 엔티티에 대한 동시성 이슈 해결하기&lt;/a&gt; 에서 다룬적이 있다. 이를 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;동시성 문제가 터지는 상황을 하나 가정해보자. 현재 사용자 계좌에 남은 잔액은 100원이다. 그리고 트랜잭션 1은 &quot;100원 지금 이자 받기&quot; 를 처리하며, 트랜잭션 2는 &quot;300원 타행 송금&quot; 을 처리한다.&lt;/p&gt;
&lt;p&gt;가장 먼저 트랜잭션1을 통해 이자를 받았고(잔액 100원 감소 로직), 트랜잭션2를 통해 입금을 받았다(잔액 300원 증가 로직)고 가정해본다면, 트랜잭션1은 현재 잔액인 100원에 지금 이자 받기를 수행하여 200원으로 계좌 잔액을 갱신할 것이다.&lt;/p&gt;
&lt;p&gt;이렇게 트랜잭션 1이 수행되는 그 사이에 트랜잭션2가 수행된다면? 트랜잭션2는 200원이 아니라 기존 잔액 값인 100원이라는 값을 읽어오고, 300원을 더하여 400원이라는 엉뚱함 금액으로 잔액을 갱신하게 된다. 즉, 데이터 정합성 문제가 발생한다. 토스뱅크는 동시성 처리를 위해 &lt;strong&gt;Redis Global Lock 과 JPA 에서 제공하는 락&lt;/strong&gt;을 사용하여 문제를 해결했다.&lt;/p&gt;
&lt;p&gt;다만, 락을 사용할떄는 주의사항이 있다. 락을 꼭 잡아야하는 최소한의 데이터에 대하서만 락을 획득해야 데드락과 성능 저하를 예방할 수 있다는 점이다. 지금 이자 받기 API 의 경우 잔액을 갱신하는 이벤트가 메인 비즈니스 로직이다. 따라서 계좌 단위 현재 잔액 데이터에 대해서만 &lt;strong&gt;ROW 단위의 락이 걸리도록&lt;/strong&gt; 개발하여, 동시성을 처리했다.&lt;/p&gt;
&lt;p&gt;또한 트랜잭션 2의 동시성이 발생했을 때, 트랜잭션 1이 끝날 때까지 기다릴 수 있도록 &lt;strong&gt;재시도할 수 있는 로직과 적절한 타임아웃을 적용해주었다.&lt;/strong&gt; 이를 통해 고객은 락이 걸렸는지도 모르게 안정적으로 이자를 받을 수 있게 된다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 추측컨데, 토스뱅크는 이 당시 동시성 처리를 위해 &lt;strong&gt;JPA 낙관적 락을&lt;/strong&gt; 사용하지 않았을까라는 생각이 든다. 우선 낙관적 락은 충돌이 자주 발생하지 않고 락을 거는 방식이다. &quot;지금 이자 받기&quot; 의 기능은 동시성이 아무래도 자주 발생할 경우는 아니다. 또한 재시도 로직은 &lt;code class=&quot;language-text&quot;&gt;@Retryable&lt;/code&gt; 로 처리할 수 있읉텐데, 비관적 락의 경우 처음부터 충돌을 방지하기 위해 락을 걸어두므로 재시도 로직을 수행할 필요가 없다. 타임아웃의 경우 낙관적 락과 비관적 락 어느 하나에 한정되지 않고 처리할 수 있는 로직이다. 낙관적 락을 활용해 @Retryale 로 재시도 로직을 구현하다 무한 대기에 빠지는 경우를 처리하기 위해 JPA 의 QueryHint 를 사용하지 않았을까? 라는 추측이다. 이 또한 토스뱅크 면접때 가능하다면, 한번 어쭤봐야겠다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 또한 Redis Global Lock 을 사용했다고 하는데, 이는 단일 서버가 아닌 분산 환경에서 흔히 사용되는 락 메커니즘 일 것이다. JPA 락 메커니즘과 Redis 분산락을 어떻게 함께 사용했다는 건지 이 영상만 보고는 이해가 안간다. 이 또한 알아봐야겠다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 나 또한 모행 서비스에서 동시성 처리를 해주었는데, 우리 서비스는 비관적 락으로 처리해두었다. &quot;지금 이자 받기&quot; 와 달리 여행지 조회시 카운팅 로직은 충돌이 자주 발생할 수 밖에 없는 상황이다. 이와 관련한 내용은 이전에 따로 정리해두었다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;성능-개선을-위해-kafka-를-활용한-비동기-처리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%EC%9D%84-%EC%9C%84%ED%95%B4-kafka-%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC&quot; aria-label=&quot;성능 개선을 위해 kafka 를 활용한 비동기 처리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;성능 개선을 위해, Kafka 를 활용한 비동기 처리&lt;/h3&gt;
&lt;p&gt;다음으로, 토스뱅크는 Kafka 를 활용하여 비동기 트랜잭션을 구현했다. 기본 코어뱅킹 시스템에서는 1번의 이자를 지급받기 위해, 무려 20개의 테이블에 80번의 UPDATE, INSERT &lt;strong&gt;대량의 쿼리를 날려야하는 복잡한 구조였다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;따라서 지금 이자 받기 서비스의 속도는 평균 300ms 으로, 전체 코어뱅킹 서비스 중에서 정말 느린 편에 속했다. 이렇게 대량의 쿼리가 나가는 상황이라면, 아무리 DB 정규화를 잘 하고, 인덱스로 쿼리 튜닝을 하더라도 매우 빠른 응답속도를 기대하기는 어려울 것이다. 그래서 기존의 지금 이자 받기 기능의 &lt;strong&gt;트랜잭션에서 분리가 가능한 테이블은 카프카를 이용해 트랜잭션에서 분리했다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;카프카의 비동기 처리에 대한 추가 학습이 필요하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5982ba37c3f88cee360bb34ebe72ba7c/21482/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.44171779141104%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB3klEQVR42pWSvW/TQBjG/Xnnj/NXEjeJY6dNnDZJSbIggrLgJQogESRAZUAgVWqndikwdIMBgRjoyp/CxMJf9vCeXTUIZcnw89nnu+d93g9FURQYhlGi6zrk965wzpFlGZrNJhQpslwusV6vsVgsygOapu0kqKrqxpB8jMcjHI/HyPM+uhQpCsPbgxU7OqaUTZNeVDBuw/VCmLQ6IoBKTmX0Cg3Mdkts4dP/impPkDtjI3iQD/H52w+s1id49eYUX77f4NFqjfPLD7h4fw1NNykoR6t/D/H+CIPZHKvnrzGbF2h0h2jnE9hetBHMeoe4uv6Eh8VjnF1+xK/ffzAvnuDk3Tnenl1AN0iQcbQHU9SzI0weFPh68xPF0xeIkgGSwQyO/4+gziyozINu+RhO72P17CXS/pD2BDTuUZP0UjTYSyviDvw4RaPTQz05QNjqglPad4LMchHGCfxaEyLaQ6ebI+0dIbrdY9y6Gy2JSTUXwkUtoDo6zpamkEM3qIO7AUTYQNxKYXC3rIvj10jEpKJr5VhIDMIWHhS6s7XL0q7XaCPZH+DweIZ8NEV/OEG93YVHDi3LAWOM4BUmgwgiRFT7rYIatVyOCpcj4XqEKFdGQialK2sox+Z/tC0z+hdM6QmkBumeGQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/5982ba37c3f88cee360bb34ebe72ba7c/a6d36/image-5.png&quot;
        srcset=&quot;/static/5982ba37c3f88cee360bb34ebe72ba7c/222b7/image-5.png 163w,
/static/5982ba37c3f88cee360bb34ebe72ba7c/ff46a/image-5.png 325w,
/static/5982ba37c3f88cee360bb34ebe72ba7c/a6d36/image-5.png 650w,
/static/5982ba37c3f88cee360bb34ebe72ba7c/e548f/image-5.png 975w,
/static/5982ba37c3f88cee360bb34ebe72ba7c/3c492/image-5.png 1300w,
/static/5982ba37c3f88cee360bb34ebe72ba7c/21482/image-5.png 1350w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;트랜잭션 분리에 대한 기준은 어떻게 했을까? 이는 고객의 잔액과 통장 데이터 관점에서 DB 쓰기 지연이 발생했을 때, 실시간으로 문제가 발생하느냐? 로 접근했다. 또한, 반드시 트랜잭션이 보장되어야 하는 데이터 모델과 즉시성(즉시 처리되지 않아도 되는) 을 요구하지 않는, 즉 세금 처리와 같이 지금 이자 받기 트랜잭션과 묶이지 않아도 되는 데이터 모델의 DML 은 트랜잭션을 분리했다.&lt;/p&gt;
&lt;p&gt;더 자세히 알아보자면, &lt;strong&gt;지금 이자 받기 서버에서 지금 이자 받기의 트랜잭션 종료와 동시에 세금 카프카 토픽에 메시지를 Produce 하고, 비동기 처리 서버가 Consume 해서 세금 DB 에 저장하도록 구현했다.&lt;/strong&gt; 이와 유사한 브로트캐스팅 기법은 &lt;a href=&quot;https://haon.blog/spring/redis-pub-sub-local-cache-synchornization/&quot;&gt;Redis Pub/Sub 을 사용한 분산 환경에서 로컬 캐시 동기화&lt;/a&gt; 에서 다룬적이 있다. 정상적인 상황이라면, 이자 DB 와 세금 DB 에도 준 실시간으로 업데이트가 되었을 것이기 때문에 지금 이자 받기 거래는 정상적으로 종료될 것이다.&lt;/p&gt;
&lt;p&gt;그런데, 카프카 메시지가 정상적으로 처리되지 않는 경우도 있다. 따라서 &lt;strong&gt;Dead Letter Queue&lt;/strong&gt; 를 이용해서 세금 DB 에 대한 트랜잭션을 안정적으로 처리할 수 있도록 보장했다. 또한, 재처리시 중복으로 세금이 업데이트 안 되도록 API 도 멱등하게 설계했다.&lt;/p&gt;
&lt;p&gt;이렇듯 토스뱅크는 세금 DML 을 지금 이자 받기 트랜잭션에서 분리함으로써, 기존 80회의 DML 쿼리가 이루어지던 트랜잭션을 50회의 DML 로 줄이는 개선 효과를 얻어내었다.&lt;/p&gt;
&lt;h3 id=&quot;redis-를-활용한-캐싱-전략&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%BA%90%EC%8B%B1-%EC%A0%84%EB%9E%B5&quot; aria-label=&quot;redis 를 활용한 캐싱 전략 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis 를 활용한 캐싱 전략&lt;/h3&gt;
&lt;p&gt;기존 코어뱅킹 시스템에서의 이자 계산은 RDB 기반의 일자별 거래내역 DB 를 조회해서 연산하는 방식으로 구현되어있었다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/41930b0c9bbfe2e2c9345504191cfac0/53639/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.14723926380368%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABg0lEQVR42qWSzUsbQRjGd7OriRCTyexmd5LdZGPs+gF+oKmSipe0DVRWBJFYaUVRVBREL6KCHsRDsT204D/8M7ttqkIv2sPDw7wz/OadZ15N13WklFiWRS6XQ9O0FyudTuO6CiEEmmmaRFFEp9Oh3W6Tz+eTjUwm8yq4lkqlUEoRBAFhGCYeK5vNJgf0l0OfF+IIYr2qux6wBzDNPvoHbfSBAsKtMjw2jV8fp6BqWN4wWVFMOu5d+lTxS58BTdNICrtHJ9z9vGfhQ8TN3S8OTs6RXkhldAbh+Fi2g7CKCGmTExLZXcduFd1HoFIOI/USmm7Q+rTCxvYBE28Xeb+8TrMVMegEpAseA8IhqIeUa29w/RrFcpVSZQjpepS7/hcYjoxS8f2ksLz2ha39Y8KpJjuHpyytbtBY/MjV7XcmG01sp4zyatiuj3Q8nFKVvKW67j8C4xHpjcnX7T3OLq6YaLzj7PKaz5s7zC20uP32g+nZ+T9ZmxiGkeQW++8MjX//8v/qAZtN9CSIqOmvAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/41930b0c9bbfe2e2c9345504191cfac0/a6d36/image-6.png&quot;
        srcset=&quot;/static/41930b0c9bbfe2e2c9345504191cfac0/222b7/image-6.png 163w,
/static/41930b0c9bbfe2e2c9345504191cfac0/ff46a/image-6.png 325w,
/static/41930b0c9bbfe2e2c9345504191cfac0/a6d36/image-6.png 650w,
/static/41930b0c9bbfe2e2c9345504191cfac0/e548f/image-6.png 975w,
/static/41930b0c9bbfe2e2c9345504191cfac0/3c492/image-6.png 1300w,
/static/41930b0c9bbfe2e2c9345504191cfac0/53639/image-6.png 1358w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;고객이 지금 이자 받기를 할 때마다, 계좌의 매일 매일 거래내역을 데이터베이스로 부터 조회해서 이자 계산과 세금을 계산하는 구조였다. 그 때문에 성능적으로 오래 걸릴 수 밖에 없는 구조였다. 그러나 &lt;strong&gt;고객은 하루에 1번밖에 이자를 못 받기 때문에&lt;/strong&gt; Redis 를 활용하면, 하루에 1번만 DB I/O 를 발생시킬 수 있을 것이라 판단해서 Redis 를 이용해 캐시를 활용하기로 했다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bd3b845a9487fba101f5ceed229aa9f1/187fa/image-7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.65030674846626%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABn0lEQVR42o2Sy0sbURTGZ5KJM5lYjBLzINEmGcnoaDSjUqGhCD523RRRpL7yMCTLLNWVuBIK3bnrriuXKrQaIaXpphAq+Ae0f4AbN4Xiwv5qrkSS0oIXPu493z3nO/fccyRJkmhAlmWBpt0Kh8Px37t/4P6gaRqmaWJZFrZtE4lEBN/VG8btDeLvMzCTE1ipZ2L3heP0hGK4VHe7YGvm09MzmiubyQguNjTOzt4bDt69Z/blIoHoEH2JFMGoRSg+gqo/aRf0uFW6vZ3CqFQq/Ly55foWVjdygnuaGKVU3mZrd5/JF/N0BWP0m2MiUcgYpkPzCL+wrtHReJxlmSRiAWSHwuHJF75fQe0HLLwuCkf7+QzHH8+pX1yyuLZJcmqas+pnvta/kS2Vcd4J9nR6eDsygHEnKo2mxvEHgiL46EOVXze/RcnrG/clR+KDLCyvs5YrYtlTBPoTvFpaYXk1S3Iyjay4cLkUfL1+dF1H6vZ6UVVVBFc/1R7+MJ/PC05RlMd2t73LDaSn58hkcxQKBQzDeBgZp9Mp0Gzg33Yr/gBVB/kb7tkWEgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/bd3b845a9487fba101f5ceed229aa9f1/a6d36/image-7.png&quot;
        srcset=&quot;/static/bd3b845a9487fba101f5ceed229aa9f1/222b7/image-7.png 163w,
/static/bd3b845a9487fba101f5ceed229aa9f1/ff46a/image-7.png 325w,
/static/bd3b845a9487fba101f5ceed229aa9f1/a6d36/image-7.png 650w,
/static/bd3b845a9487fba101f5ceed229aa9f1/e548f/image-7.png 975w,
/static/bd3b845a9487fba101f5ceed229aa9f1/187fa/image-7.png 1194w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;기존의 이자 금액은 고객이 계좌 상세탭에 접근할 때 마다, 이자계산을 위한 DB I/O 가 발생하고 있었다. 이를 고객이 하루 중 처음으로 계좌 상세탭에 접근할 때만 DB 에 접근하도록 구현했고, &lt;strong&gt;이자예상조회의 결과를 Redis 에 캐싱해 두도록 구현했다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;그래서 고객이 하루에 2번이상 계좌 상세탭에 접근할 경우에는 Redis 에 미리 저장되어있던 이자계산 결과를 리턴하도록 했다. 그래서 불필요하게 DB 리소스가 낭비되는 것을 방지했다. 또한, Redis 에 캐싱된 이자 데이터의 &lt;strong&gt;만료일자도 하루로 두어서,&lt;/strong&gt; 이자금액이 잘못 계산되는 오류 케이스도 원천적으로 방지했다. 그래서 매일 자정 이후 고객이 계좌 상세탭에 처음 접근할 때만, 이자 에상 조회의 결과를 캐싱해서 이자 &lt;strong&gt;데이터의 정합성을 보장할 수 있게 되었다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;기존-모놀리식-아키텍처를-msa-로-안전하게-마이그레이션-하는-방법-api-검증&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EC%A1%B4-%EB%AA%A8%EB%86%80%EB%A6%AC%EC%8B%9D-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EB%A5%BC-msa-%EB%A1%9C-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-api-%EA%B2%80%EC%A6%9D&quot; aria-label=&quot;기존 모놀리식 아키텍처를 msa 로 안전하게 마이그레이션 하는 방법 api 검증 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기존 모놀리식 아키텍처를 MSA 로 안전하게 마이그레이션 하는 방법 (API 검증)&lt;/h2&gt;
&lt;p&gt;위와 같은 과정을 통해, 토스뱅크는 이자 지급 마이크로 서버에 이자 조회 거래, 지금 이자 받기 거래 개발을 마쳤다. 그렇다면 이제, 기존 코어뱅킹(계정계) 를 참조하던 서비스를 이자 지급 마이크로 서버를 바라보도록 라우팅 환경을 전환해야 할 것이다.&lt;/p&gt;
&lt;p&gt;그런데, 이 라우팅 환경을 재구축하기 위한 선수 과정이 필요했다. 이자 지급 마이크로 서버 API 가 기존에 모놀리식 아키텍처에 구현된 방식과 똑같이 동작하는 것에 대해 검증이 필요하다는 점이다. 이를 위해 어떤 방식으로 검증을 할 수 있을까?&lt;/p&gt;
&lt;h3 id=&quot;실시간-검증을-통한-건별-검증-방식-온라인-검증&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%EC%8B%9C%EA%B0%84-%EA%B2%80%EC%A6%9D%EC%9D%84-%ED%86%B5%ED%95%9C-%EA%B1%B4%EB%B3%84-%EA%B2%80%EC%A6%9D-%EB%B0%A9%EC%8B%9D-%EC%98%A8%EB%9D%BC%EC%9D%B8-%EA%B2%80%EC%A6%9D&quot; aria-label=&quot;실시간 검증을 통한 건별 검증 방식 온라인 검증 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실시간 검증을 통한 건별 검증 방식 (온라인 검증)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4ad3dc52727a97328f2efc04fb88c759/e4900/image-8.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.94478527607362%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB3UlEQVR42mNgYGD4D8KMjIxgWkRE5L+Kisp/LS2t/4KCgihyRGELS8v/9Y2N/0vKyv7X1NX9t7Cw/C8jI/NfXl7+v7eP7//a+vr/DY1N/61tbMEazM0twOorq2uA6uvBdENT03//gECIgQ0NDf9B4Nu3b2A6Lj4ebtv06TP+f/n2/f+37z//9/dPAIs1ATWDwJ37D/7fuHXn/70Hj8D8TZs2Q/RVVlX///33///LV6///wOUiIiMghvY1dP7//W7D//ffvj0v6m5FSxWWlb+/xdQ/XWgYZev3fh/7ebt/5+//fi/ZNkKqIHV1f+v377zf868Bf9v3bn3PyEpCSzBxsYODEvV/wGBQWDvgMIVJK6rp/c/L7/wf05e3v/ComIgu+B/QWHRf2cXF4iBJUAbX7x+8//K9ZtA+u3/sPAIsAQ3Nw9KYDMzM4PptIzM/2+Arj57/uL/A4eP/D96/OT/l0B902bMhIYhMIBB4Cs0DGPj4sASQsLC/7m4uf/z8PKCMR8fH1i8uaUFrO7MuXP/T5w6/f/QkWP///77/3/LZmgY+vn7/1+3YeP/eQsX/V+7fsN/SysrqItYwMkFhmEu9PbxAaubO3/B/3kLFv6fD9S3as1asNehvmH4T00MAIGecgjSDOCxAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/4ad3dc52727a97328f2efc04fb88c759/a6d36/image-8.png&quot;
        srcset=&quot;/static/4ad3dc52727a97328f2efc04fb88c759/222b7/image-8.png 163w,
/static/4ad3dc52727a97328f2efc04fb88c759/ff46a/image-8.png 325w,
/static/4ad3dc52727a97328f2efc04fb88c759/a6d36/image-8.png 650w,
/static/4ad3dc52727a97328f2efc04fb88c759/e548f/image-8.png 975w,
/static/4ad3dc52727a97328f2efc04fb88c759/e4900/image-8.png 988w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;토스뱅크는 이를위한 첫번째 방안으로, 온라인 검증을 시도했다. 먼저, 앱(채널계)에서 고객이 이자 조회 거래를 일으키면 채널계에서 &lt;code class=&quot;language-text&quot;&gt;MCI(Message Channel Interface)&lt;/code&gt; 통한 기존 코어뱅킹 서버에 이자 조회 서비스를 호출하고, 이자 지급 마이크로 서버의 API 를 &lt;strong&gt;&quot;동시에&quot;&lt;/strong&gt; 호출했다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;코어뱅킹 서버에서 리턴된 이자 값과 이자 지금 마이크로 서버에서 리턴된 이자 값을 각각 리턴받아, 두 이자값이 불일치 할 경우 토스뱅크 내부 모니터링 채널에 해당 내용을 알림받도록 했다.&lt;/strong&gt; 채널에 알림이 오면 대상 및 로그를 확인하고 원인을 확인하여, 이자 계산 로직을 수정해주는 과정을 거쳤다.&lt;/p&gt;
&lt;h3 id=&quot;배치를-활용한-대량-검증-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%EC%B9%98%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%8C%80%EB%9F%89-%EA%B2%80%EC%A6%9D-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;배치를 활용한 대량 검증 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배치를 활용한 대량 검증 방식&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2f9dfc3a9027f99b7011f6a0c7eca6cc/ee3fb/image-9.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.331288343558285%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAAByUlEQVR42oWR7UtTYRjGz8Y+VAuPZ07nS75MD5WslHaaDsywhSBRBFI0Tczp2XFBEuj/EAWl69X+gKiorxFBIH0I9ENRXyKmcw3FpfVh1CiMRr+enkWTQHzg4rkv7osf982tKIrCRtntdlRVRdNKcTgc/N/fUtudKntaDPb726jY1YjmqSXYGaKtowt3dYNUi9FOs8hsE1mn00mTruPxeCTA5XKhC69prgLQXVXHu8Qif96pAZMmX4Dc93WyuW8Yh7rpORmWvURqiZKySoYiEemfPH0mAZPx69Jfm4oXgBU1Dcy+esPKx1XC56LsPRAkmUrzfj5JsKuHE6cHyKyuMScy5dX1DI+YZL/mePjosQTEb9zkR/4XVyenisC512/JrH2iPxJjX+Awi+klEskUHUeP09sfEfB5XrycpaZexzSjIvuZe/cf/Jsw+yXHpctXiisvpJfl2GcGLRp9B8mLev1nnkBnN8d6+2Tvw3IG1V2FZY1K/3xmRgJuT9+V/tad6QJwZ2k54UGT2Ng4za3tVNbtZiQ2xvDoBWp1H7rPz/mLE5wdsthRUobX68WMRjkSCkmAYRjCW/jF//fSxZPbbDapzbyysd5EvwFP8zb0UF2xVwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/2f9dfc3a9027f99b7011f6a0c7eca6cc/a6d36/image-9.png&quot;
        srcset=&quot;/static/2f9dfc3a9027f99b7011f6a0c7eca6cc/222b7/image-9.png 163w,
/static/2f9dfc3a9027f99b7011f6a0c7eca6cc/ff46a/image-9.png 325w,
/static/2f9dfc3a9027f99b7011f6a0c7eca6cc/a6d36/image-9.png 650w,
/static/2f9dfc3a9027f99b7011f6a0c7eca6cc/e548f/image-9.png 975w,
/static/2f9dfc3a9027f99b7011f6a0c7eca6cc/ee3fb/image-9.png 1144w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Staging 환경이란, 실제 운영환경과 동일하게 구성된 내부 API 테스트용 환경을 뜻한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;또한 배치를 활용한 대량 검증 방식을 도입했다. &lt;strong&gt;Staging 환경에서 채널계 배치를 통해 매일 대량의 검증 대상 목록을 추출했고,&lt;/strong&gt; 앞선 온라인 검증 방식과 동일하게 코어뱅킹 서버와 이자 지급 마이크로 서버를 각각 호출해주었다. 대상 목록에 대한 검증이 모두 끝나면, 이자 리턴 값이 불일치했던 건들에 대한 내용을 담아 내부 모니터링 채널에 알림으로 받았고, 로직 수정을 반복하였다.&lt;/p&gt;
&lt;h2 id=&quot;테스트-시나리오-작성을-통한-e2e-통합-테스트-수행하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4-%EC%9E%91%EC%84%B1%EC%9D%84-%ED%86%B5%ED%95%9C-e2e-%ED%86%B5%ED%95%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%88%98%ED%96%89%ED%95%98%EA%B8%B0&quot; aria-label=&quot;테스트 시나리오 작성을 통한 e2e 통합 테스트 수행하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;테스트 시나리오 작성을 통한 E2E 통합 테스트 수행하기&lt;/h2&gt;
&lt;p&gt;위와 같은 API 검증 방식을 통해, 이자 조회 거래에 대한 검증을 마쳤다. 그런데 실제 이자를 지급받는 지금 이자 받기 거래의 경우 코어뱅킹 DB 원장에 잔액을 갱신하고, 회계 처리를 해주는 등의 작업이 필요했다. 따라서 &lt;strong&gt;거래가 발생했을 때 실제 데이터가 정확하게 쌓이고 갱신되었는지 추가로 검증해야 했다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;토스뱅크 통장은 잔액을 구간별로 나누어 이자를 차등 지급하고 있다. 잔액 구간별로 나누어 차등 계산되어, 이자가 지급되었는지 검증이 필요했다. 그리고 명의도용, 해킹 피해, 사망 등 토스뱅크 고객의 상태에 따른 검증이 필요했고, 계좌의 상태 및 출금/입금 정지 상태에 따른 검증이 필요했다.&lt;/p&gt;
&lt;p&gt;해당 검증 케이스들을 고려해서 테스트 시나리오를 작성했고, 케이스 별로 테스트를 진행하여 이자 계산 및 실제 데이터베이스에 데이터가 정확하게 갱신되었는지를 확인하며 로직을 수정해주는 과정을 거쳤다. 이 과정을 통해, 이자 받기 거래에 대한 정합성 검증을 완료할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;순차-배포를-통한-안정적인-마이그레이션하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%9C%EC%B0%A8-%EB%B0%B0%ED%8F%AC%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%95%88%EC%A0%95%EC%A0%81%EC%9D%B8-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98%ED%95%98%EA%B8%B0&quot; aria-label=&quot;순차 배포를 통한 안정적인 마이그레이션하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;순차 배포를 통한 안정적인 마이그레이션하기&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 여기서 말하는 순차 배포란, 무중단 배포 전략 중 하나인 롤링(Rolling) 배포를 말하는 듯 하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이렇게 API 검증이 끝났으니, 코어뱅킹을 바라보던 서비스를 이제는 이자 지급 마이크로 서버 API 만을 바라보도록 라우팅 환경을 전환해주면 된다. API 를 전환할 때 대상 모수를 점차 늘려가며, 순차적으로 오픈했다. 먼저 토스뱅크 수신 개발팀에 오픈하여 직접 이자 받기 거래를 일으키며 데이터 검증 값을 검증했다. 특이사항이 없는 것을 확인하여 토스뱅크 내부 팀원에게 오픈했고, 모니터링을 진행했다.&lt;/p&gt;
&lt;p&gt;다음으로는 &lt;strong&gt;일부 고객을 대상으로 오픈하고 점차 모수를 늘려가며 순차 오픈하여, 전체 고객을 대상으로 전환을 완료하는 방식을 선택했다.&lt;/strong&gt; 코어뱅킹 서버를 바라보던 API 호출량과 이자 지급 마이크로 서버를 바라도면 API 호출량을 조절하여 이자 지급 마이크로 서버의 트래픽을 점차 늘려가는 형태로 진행했다.&lt;/p&gt;
&lt;p&gt;이로써 토스뱅크는 순차 배포 방식을 채택함으로써 기존에 운영하고 있던 시스템을 무중단하고, 안정적으로 시스템을 마이그레이션했다.&lt;/p&gt;
&lt;h2 id=&quot;코어뱅킹-msa-전환의-성과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BD%94%EC%96%B4%EB%B1%85%ED%82%B9-msa-%EC%A0%84%ED%99%98%EC%9D%98-%EC%84%B1%EA%B3%BC&quot; aria-label=&quot;코어뱅킹 msa 전환의 성과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;코어뱅킹 MSA 전환의 성과&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;코어뱅킹 시스템의 세대 전환&lt;/li&gt;
&lt;li&gt;오픈소스 기반의 개발 환경 변화에 따른 유연성 및 확장성 증가&lt;/li&gt;
&lt;li&gt;지금 이자 받기 거래의 성능 170배 개선&lt;/li&gt;
&lt;li&gt;계정계 서버로부터 독립저깅ㄴ 서버를 구축함으로써 안정성 증가&lt;/li&gt;
&lt;li&gt;지금 이자 받기 피크타임 트래픽에도 개별적으로 이자 지급 서버 스케일 아웃 가능&lt;/li&gt;
&lt;li&gt;도메인 단위로 분리하여 효율적인 MSA 코어뱅킹 시스템 구축&lt;/li&gt;
&lt;li&gt;빅뱅 배포 방식을 탈피하여 무중단 시스템 전환 가능&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL 레플리케이션 바이너리 로그 기록 타입 (Row, Statement, Mixed)]]></title><description><![CDATA[MySQL 8.0 레플리케이션 동작 과정과 아키텍처 구성 방식 에서 설명했듯이, MySQL 레플리케이션은 현재 GTID 기반의 안정적인 복제 방식을 택했다. 이때 궁금한 점은, 정확히 어떤 이벤트를 기록한다는 것일까? Row…]]></description><link>https://haon.site/database/replication-event-format/</link><guid isPermaLink="false">https://haon.site/database/replication-event-format/</guid><pubDate>Sun, 08 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://haon.blog/database/replication-architecture/&quot;&gt;MySQL 8.0 레플리케이션 동작 과정과 아키텍처 구성 방식&lt;/a&gt; 에서 설명했듯이, MySQL 레플리케이션은 현재 GTID 기반의 안정적인 복제 방식을 택했다. 이때 궁금한 점은, 정확히 어떤 이벤트를 기록한다는 것일까?&lt;/p&gt;
&lt;h2 id=&quot;row-포맷-변경된-데이터를-기록&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#row-%ED%8F%AC%EB%A7%B7-%EB%B3%80%EA%B2%BD%EB%90%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EA%B8%B0%EB%A1%9D&quot; aria-label=&quot;row 포맷 변경된 데이터를 기록 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Row 포맷 (변경된 데이터를 기록)&lt;/h2&gt;
&lt;p&gt;MySQL 데이터베이스에서 실행한 작업을 바이너리 로그에 저장하는 다양한 형태(포맷)이 있다. 그 중 &lt;strong&gt;하나는 SQL 실행을 통해 변경이 일어난 데이터 전부를 바이너리 로그 파일에 기록&lt;/strong&gt;하는 방법이 있다. 데이터베이스 변경 내용을 각각의 행(row) 단위로 기록한다고 하여, Row 기반 바이너리 로그 포맷이라고 한다.&lt;/p&gt;
&lt;p&gt;Row 기반 바이너리 로그 포맷은 &lt;strong&gt;변경된 데이터가 모두 바이너리 로그에 기록된다&lt;/strong&gt;는 것이 가장 큰 특징이다. 따라서 변경되는 데이터가 많을수록 바이너리 로그에도 많은 데이터가 기록된다.&lt;/p&gt;
&lt;p&gt;이는 무엇이 문제가 될까? 만약 MySQL 에서 실행한 쿼리가 대량의 데이터를 변경했거나, 또는 데이터 수가 대량으로 삭제되어도, 또는 BLOB 처럼 엄청 큰 용량의 값이 새롭게 추가되거나 수정된다면 문제가 된다. 바이너리 로그 파이릐 크기가 매우 커지면서 저장 공간을 크게 차지하게 된다.&lt;/p&gt;
&lt;h2 id=&quot;statement-포맷-실행한-sql-을-기록&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#statement-%ED%8F%AC%EB%A7%B7-%EC%8B%A4%ED%96%89%ED%95%9C-sql-%EC%9D%84-%EA%B8%B0%EB%A1%9D&quot; aria-label=&quot;statement 포맷 실행한 sql 을 기록 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Statement 포맷 (실행한 SQL 을 기록)&lt;/h2&gt;
&lt;p&gt;그렇다면 변경된 데이터를 모두 기록하느 방법 대신에 다른 방법이 있지 않을까? 그에 대한 방안으로, 실행한 SQL 을 기록하는 방법이 등장했다.&lt;/p&gt;
&lt;p&gt;Statement 기반 바이너리 로그 포맷은, &lt;strong&gt;각 이벤트에서 실행한 SQL 문을 바이너리 로그 파일에 기록&lt;/strong&gt;하는 방법이다. 바이너리 로그의 용량이 크게 줄어들기 때문에, 더 이상 용량을 걱장할 필요가 없다.&lt;/p&gt;
&lt;p&gt;하지만 이 또한 문제점이 있다. SQL 문이 복제되어 동일한다고 한들, 실행할 때 마다 결과 다르게 나오는 &lt;strong&gt;비확정적(Non-Deterministic) 쿼리&lt;/strong&gt;가 존재한다면, 소스 서버와 레플리카 서버의 데이터가 다르게 기록된 &lt;strong&gt;데이터 정합성 문제&lt;/strong&gt;가 발생할 수 있기 때문이다. &lt;code class=&quot;language-text&quot;&gt;UUID()&lt;/code&gt; 함수를 사용하는 쿼리가 전형적인  &lt;strong&gt;비확정적(Non-Deterministic) 쿼리&lt;/strong&gt; 에 해당한다. UUID 함수는 고유한 식별자를 만들기위해 실행할 때 마다 매번 다르고 고유한 값을 만든다.&lt;/p&gt;
&lt;p&gt;결국, 소스 서버에서 사용한 SQL 문을 가져와 레플리카 서버에서 동일하게 실행하는 방법은, 소스 서버와 레플리카 서버간에 비확정적 쿼리로 인해 데이터 정합성 문제가 발생할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;mixed-포맷-row--statement-혼합&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mixed-%ED%8F%AC%EB%A7%B7-row--statement-%ED%98%BC%ED%95%A9&quot; aria-label=&quot;mixed 포맷 row  statement 혼합 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Mixed 포맷 (Row + Statement 혼합)&lt;/h2&gt;
&lt;p&gt;이를 해결한 방식이 Mixed 포맷이다. 이는 Row 와 Statement 방식을 혼합한 방식이다. 평상시에는 Statement 포맷으로 로그를 기록하여 바이너리 로그의 크기를 최소한으로 관리한다. 그러다 Statement 포맷으로 복제했을 때 문제가 발생할 수 있는 비확정적 쿼리를 저장하는 경우, Statement 대신에 Row 방식으로 바이너리 로그에 이벤트를 기록한다. 이를 통해 용량 문제도 해결하고, 데이터 정합성 문제도 해결할 수 있다.&lt;/p&gt;
&lt;p&gt;MySQL 의 경우 Mixed 포맷을 기본적으로 택한다. 어떤 쿼리가 비확정적 쿼리인지는 MySQL 스토리지 엔진이 자체적인 기준에 따라 판별한다.&lt;/p&gt;
&lt;h4&gt;만능은 아니다.&lt;/h4&gt;
&lt;p&gt;편리하긴 하지만 포맷 형식을 MySQL 이 잘못 판단하 저장할 가능성도 조금은 존재하기 때문에, Mixed 방식이 항상 만능은 아니라는 점을 알고있자. 따라서 자신에게 가장 맞는 바이너리 로그 포맷을 찾아서 적용할 수 있어야함을 알자.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Real MySQL 8.0&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@backfox/%EB%AC%B4%EB%87%BD%EC%9D%B4%EC%99%80-%EC%95%8C%EC%95%84%EB%B3%B4%EB%8A%94-%EB%8C%80%EA%B7%9C%EB%AA%A8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B4%80%EB%A6%AC-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B3%B5%EC%A0%9C%ED%95%98%EA%B8%B0%EB%A6%AC%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-f4pota6h&quot;&gt;https://velog.io/@backfox/무뇽이와-알아보는-대규모-데이터-관리-데이터베이스-복제하기리플리케이션-f4pota6h&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[무중단 배포 환경에서 구버전 프로세스를 안전하게 종료해보자!(feat. Graceful Shutdown)]]></title><description><![CDATA[현재 포스팅은 하모니 팀 크루 하온(haon) 이 작성했습니다. 현재 무중단 배포 환경의 문제점 지난 Jenkin와 Nginx를 활용한 Blue/Green…]]></description><link>https://haon.site/deployment/graceful-shutdown/</link><guid isPermaLink="false">https://haon.site/deployment/graceful-shutdown/</guid><pubDate>Sun, 08 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;현재 포스팅은 하모니 팀 크루 &lt;a href=&quot;https://github.com/msung99&quot;&gt;하온(haon)&lt;/a&gt; 이 작성했습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;현재-무중단-배포-환경의-문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%98%84%EC%9E%AC-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC-%ED%99%98%EA%B2%BD%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;현재 무중단 배포 환경의 문제점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;현재 무중단 배포 환경의 문제점&lt;/h2&gt;
&lt;p&gt;지난 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/blue-green/&quot;&gt;Jenkin와 Nginx를 활용한 Blue/Green 무중단 배포 도입기&lt;/a&gt; 에서도 다루었듯이, 무중단 배포 전략을 도입하여 다운타임을 최소화했다. 무중단 배포 프로세스를 다시 정리하자면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1fc564bdf36c4d5b8bedcf68f5342830/01a87/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.668711656441715%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACP0lEQVR42o1SS4/SUBTuH3Pt33Dpyt/gxp2JK0fNLERN0IExuhnBKMxkkhlgKK/yKH1gkVJsgdJCGyi05fPeCxoSn6c5ub0n53zn3O98HH5j2+2WObXNZoPVasU8CAKEYbjLIV8UxohDekbMaQ0X7wu9VYBnl3UII/snKIsvPGiahq4sodFooNvtYj6fY2Y7KAvXEI06NFOGpLXhez64H4VTf4l7pxd4W6ih3RIgKQps24bv+5jPXOhqH2Kng15PhWl+w2zi4qx0gjT/GCX5HJohs0acKIrgeR7nuc8YGUNM+xqyr45hDAb4Qv6n1gRXSgXPa+/R+6pBH+iwLAue52E0GiH5JokXLxMsHkURuFq1inw+h0w2C0XtoclXkDh6AmtkYEjc6Os4Lp/izqf7uKxdo3LDkzwVKpm00xbhOUt4rg/Xme8Aabdmq8XGVUmiJMuoEa7K5TIcx2G8JE9e4+HRI9yUSmwpNJcCCHIVXZuH4anQrC5msxm4QrGIVCrNLhSgQ3hKp9OgVNAn0e1apoUx8clkgvF4jGAdkPsEF+UsnmYeIPUxgZZS33PYEZHL5VgxlUgQrNgiqESWyyUMw0Cz2USlUmFnoVBgsfV6TYZwULgqIvMhA0VW2XK5OI5JVwsykcXUtn/RISVfIRsXBIFJRpIk1vBQWofGeZKC3Ls8/HAvavzdKAWLxWInJ/JE2pDSRV/EAEUil1u370Kzwz92PYxTkOFwyEBM02QS0nUdruvuACOS6AXRPyf7X/sO9C93ECtKeewAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/1fc564bdf36c4d5b8bedcf68f5342830/a6d36/image.png&quot;
        srcset=&quot;/static/1fc564bdf36c4d5b8bedcf68f5342830/222b7/image.png 163w,
/static/1fc564bdf36c4d5b8bedcf68f5342830/ff46a/image.png 325w,
/static/1fc564bdf36c4d5b8bedcf68f5342830/a6d36/image.png 650w,
/static/1fc564bdf36c4d5b8bedcf68f5342830/e548f/image.png 975w,
/static/1fc564bdf36c4d5b8bedcf68f5342830/01a87/image.png 1288w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 깃허브 브랜치에 코드가 병합되면, 깃허브는 Jenkins 에 Webhook 요청을 보낸다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; Jenkins는 새로운 버전 코드를 pull 해오고 jar 파일을 빌드한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 빌드한 jar 파일을 배포 서버로 전송한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; Blue 서버에 구버전 애플리케이션이 띄워져있는지 Health Check 하고, 그 결과에 따라 Blue 또는 Green 서버에 신버전 애플리케이션을 띄운다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; (Blue 서버에 구버전 프로세스가 띄워져있다는 가정하에) Green 서버에 신버전 애플리케이션이 잘 띄워졌는지 지속적으로 Health Check 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(6)&lt;/code&gt; Green 서버에 애플리케이션이 잘 떴다면, 리버스 프록시로 사용중인 Nginx의 프록시 방향을 Blue 에서 Green 스위칭한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(7)&lt;/code&gt; 곧바로 Blue 에 띄워져있던 구버전 애플리케이션 프로세스를 즉시 kill 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그런데 위 과정에서 문제가 하나있다. 7번 과정에서 즉시 애플리케이션을 kill 한다는 점이 문제가 된다는 점이다. 만약 7번 과정을 수행하는 도중에 구버전 애플리케이션에 요청이 들어와 처리중인 작업이 있다면, 해당 요청은 어떻게 처리가 될까? 만약 별다른 처리가 없다면, &lt;strong&gt;해당 시점에 유입된 유저의 요청은 아직 정상적으로 처리 완료가 되지 않았음에도 애플리케이션이 강제 종료되어 문제가 될 것이다.&lt;/strong&gt; 이를 해결하기 위한 방법이 바로 Graceful Shutdown 이다.&lt;/p&gt;
&lt;h2 id=&quot;강제로-구버전-프로세스를-kill-하던-스크립트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%95%EC%A0%9C%EB%A1%9C-%EA%B5%AC%EB%B2%84%EC%A0%84-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EB%A5%BC-kill-%ED%95%98%EB%8D%98-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8&quot; aria-label=&quot;강제로 구버전 프로세스를 kill 하던 스크립트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;강제로 구버전 프로세스를 kill 하던 스크립트&lt;/h2&gt;
&lt;p&gt;기존에 구성했던 무중단 배포 스크립트 일부를 보자면 아래와 같다. 구버전 프로세스를 강제로 Kill 하는 모습을 볼 수 이다. 문제는 &lt;code class=&quot;language-text&quot;&gt;-k&lt;/code&gt; 옵션은 &lt;code class=&quot;language-text&quot;&gt;kill&lt;/code&gt; 명령어를 축약한 것으로, 별다른 지정이 없다면 &lt;code class=&quot;language-text&quot;&gt;-15&lt;/code&gt; 옵션으로 실행된다. 이는 무엇이 문제가 될까?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${deployment_target_port}&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${blue_port}&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    ssh root&lt;span class=&quot;token variable&quot;&gt;@$&lt;/span&gt;{BACKEND_DEV_IP} &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k ${GREEN_PORT}/tcp&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
    ssh root&lt;span class=&quot;token variable&quot;&gt;@$&lt;/span&gt;{BACKEND_DEV_IP} &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k ${BLUE_PORT}/tcp&quot;&lt;/span&gt;
fi

echo &lt;span class=&quot;token string&quot;&gt;&quot; ✅ 구버전 프로세스를 종료하고, 신버전 프로세스로 교체합니다.&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;unix-시그널signal&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#unix-%EC%8B%9C%EA%B7%B8%EB%84%90signal&quot; aria-label=&quot;unix 시그널signal permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Unix 시그널(SIGNAL)&lt;/h3&gt;
&lt;p&gt;유닉스는 &lt;strong&gt;시그널(SIGNAL)&lt;/strong&gt; 을 사용하여 프로세스에 **인터럽트(Interrput)**를 건다. 시그널의 종류는 매우 다양하지만, 그 중 눈여겨 봐야할 시그널은 바로 &lt;code class=&quot;language-text&quot;&gt;9 - SIGKILL&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;15 - SIGTERM&lt;/code&gt; 으로, 이 둘은 프로세스를 종료하기 위해 사용하는 시그널이다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;9 - SIGKILL&lt;/code&gt; 의 경우, 프로세스의 의사와 상관없이 프로세스를 강제로 종료해버린다. 프로세스는 이 시그널을 무시하거나 차단할 수 없다는 특징이 있어, 항상 강제로 즉시 종료된다. 또한 &lt;code class=&quot;language-text&quot;&gt;SIGKILL&lt;/code&gt; 시그널을 받은 프로세스는 자식 프로세스를 종료하라는 시그널을 보낼 수 없다. 따라서 &lt;code class=&quot;language-text&quot;&gt;SIGKILL&lt;/code&gt; 은 정상적으로 프로세스를 내릴 수 없을 때 강제로 즉시 중단하도록 사용하는 최후의 수단이다.&lt;/p&gt;
&lt;p&gt;그에 반면, &lt;code class=&quot;language-text&quot;&gt;15 - SIGTERM&lt;/code&gt; 은 &lt;strong&gt;프로세스를 정상적으로 종료시킨다.&lt;/strong&gt; 프로세스는 현재 사용중인 리소스를 해제하거나, 데이터를 저장하는 등 &lt;strong&gt;현재 처리중인 모든 작업을 안전하게 처리한 뒤 종료된다.&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;-k&lt;/code&gt; 옵션의 경우, 디폴트로 &lt;code class=&quot;language-text&quot;&gt;SIGTERM&lt;/code&gt; 이 수행된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k ${GREEN_PORT}/tcp&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;sigterm-의-문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sigterm-%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;sigterm 의 문제점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SIGTERM 의 문제점&lt;/h3&gt;
&lt;p&gt;앞서 살펴보았듯이, 우리 팀은 구버전 프로세스를 &lt;code class=&quot;language-text&quot;&gt;-k&lt;/code&gt; 을 통해 종료하며, 기본 시그널인 &lt;code class=&quot;language-text&quot;&gt;SIGTERM&lt;/code&gt; 를 이용해 종료한다. 그런데 이때 또한 문제가 존재한다. &lt;code class=&quot;language-text&quot;&gt;SIGTERM&lt;/code&gt; 시그널을 사용시 애플리케이션에 별다른 설정이 없다면, 애플리케이션을 어떻게 종료해야 할지 몰라서 강제 종료가 되버린다는 점이다. 즉, SIGKILL 과 같이 강제로 종료되버리므로 애플리케이션에 별다른 설정이 필요하다는 점인데, 이때 사용하는 것이 바로 &lt;code class=&quot;language-text&quot;&gt;Graceful Shutdown&lt;/code&gt; 이다.&lt;/p&gt;
&lt;h2 id=&quot;springboot-graceful-shutdown&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#springboot-graceful-shutdown&quot; aria-label=&quot;springboot graceful shutdown permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SpringBoot Graceful Shutdown&lt;/h2&gt;
&lt;p&gt;Graceful Shutdown 은 스프링부트 2.3.0 부터 제공하는 기능이다. Graceful Shutdown 을 활성화 하면, 스프링부트는 프로세스 종료 요청인 &lt;code class=&quot;language-text&quot;&gt;SIGTERM&lt;/code&gt; 이 들어왔을 때 &lt;strong&gt;더 이상 새로운 요청을 받지 않으면서, 처리중인 요청은 모두 처리한 뒤 프로세스를 종료하게 된다.&lt;/strong&gt; (반면 &lt;code class=&quot;language-text&quot;&gt;SIGKILL&lt;/code&gt; 이 들어온다면 Graceful Shtudown 활성화는 무의미하다.)&lt;/p&gt;
&lt;h3 id=&quot;graceful-shutdown-활성화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#graceful-shutdown-%ED%99%9C%EC%84%B1%ED%99%94&quot; aria-label=&quot;graceful shutdown 활성화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Graceful Shutdown 활성화&lt;/h3&gt;
&lt;p&gt;설정은 간단하다. 아래처럼 application.yml 에서 활성화시켜주면 끝이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;server:
  &lt;span class=&quot;token keyword&quot;&gt;shutdown&lt;/span&gt;: graceful&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;타임아웃-추가-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%83%80%EC%9E%84%EC%95%84%EC%9B%83-%EC%B6%94%EA%B0%80-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;타임아웃 추가 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;타임아웃 추가 설정&lt;/h3&gt;
&lt;p&gt;그런데 만약 현재 진행중인 요청이 데드락에 빠져서 영원히 작업을 끝내지 못한다면, 스프링부트 프로세스는 영원히 종료되지 않고 대기할 것이다. 이러한 미연의 상황을 방지하기 위해 우리는 아래와 같이 타임아웃을 추가 설정할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;spring:
  lifecycle:
    timeout&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;per&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;shutdown&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;phase: &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;s&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.spring.io/spring-boot/docs/current/reference/html/web.html#web.graceful-shutdown&quot;&gt;https://docs.spring.io/spring-boot/docs/current/reference/html/web.html#web.graceful-shutdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@dongvelop/Springboot-Graceful-Shutdown&quot;&gt;https://velog.io/@dongvelop/Springboot-Graceful-Shutdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://effectivesquid.tistory.com/entry/JVM%EC%9D%98-%EC%A2%85%EB%A3%8C%EC%99%80-Graceful-Shutdown&quot;&gt;https://effectivesquid.tistory.com/entry/JVM의-종료와-Graceful-Shutdown&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Elastic Search 역 인덱스(Inverted Index) 구조 살펴보기]]></title><description><![CDATA[…]]></description><link>https://haon.site/database/elastic-search-basic/</link><guid isPermaLink="false">https://haon.site/database/elastic-search-basic/</guid><pubDate>Fri, 06 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;검색엔진을 이용하다보면, 검색어를 타이밍하는 순간 연관된 추천 검색어들이 등장하는 것을 알 수 있다. 그런데, 어떻게해서 사용자가 타이핑을 하는 그 &apos;찰나&apos; 에 추천 검색어를 빠르게 찾아낼 수 있을까? 그리고 많은 인터넷 문서들 가운데, 검색 키워드에 맞는 문서를 찾아낼 수 있을까? 일반적인 선형탐색을 떠올린다면 절대로 불가능한 속도이다. 이를 가능하게 하는 방법 중 하나가 바로 Elastic Search 를 활용하는 것이다.&lt;/p&gt;
&lt;h2 id=&quot;elastic-search&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#elastic-search&quot; aria-label=&quot;elastic search permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Elastic Search&lt;/h2&gt;
&lt;p&gt;엘라스틱 서치는 Lucene 기반의 오픈소스 &lt;strong&gt;검색엔진&lt;/strong&gt;이다. 잠시 RDBMS 의 인덱스를 떠올려보자. MySQL 과 같은 RDBMS 를 떠올려본다면, 테이블이 인덱싱되면 복잡한 쿼리에서 훨씬 빠른 성능을 보여준다는 것을 알 것이다. 인덱스를 생성하면, 주어진 검색어를 찾기 위해 모든 열을 찾아보는 대신에 인덱스를 활용하여 원하는 데이터가 어디있는지를 찾아낼 수 있다.&lt;/p&gt;
&lt;p&gt;엘라스틱 서치의 경우, &lt;strong&gt;검색어가 될 수 있는 문자 또는 문자열에 대해 유연하게 여러 토큰(term)으로 토큰화하고 역 인덱싱한다.&lt;/strong&gt; (역 인덱스에 대한 내용은 조금 뒤에 설명하도록 한다.) 예를들어 &quot;Hello my world&quot; 가 있다고 해보자. 이 문자열에 대해 빠른 검색 기능을 제공하고자 Elastic Search 를 도입한다면, 이 문장에 대해선 아래와 같은 방법으로 토큰화되고, 인덱싱 될 것이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[&quot;Hello my world&quot;] : 문자를 통째로 토큰화 할 수 있고,&lt;/li&gt;
&lt;li&gt;[&quot;Hello&quot;, &quot;my&quot;, &quot;world&quot;] : 각각의 단어를 토큰화 할 수도 있고,&lt;/li&gt;
&lt;li&gt;[&quot;Hello&quot;, &quot;world&quot;] : 의미있는 중요한 단어들로만 토큰화 할수도 있고,&lt;/li&gt;
&lt;li&gt;[&quot;hElLo&quot;, &quot;WoRlD&quot;] : 대소문자를 구분하지 않고 모두 검색 대상에 포함될 수 있도록 다양하게 토큰화할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이렇게 토큰화된 단어들은 인덱스 key 가 되어, 훨씬 빠른 검색 기능을 지원하게 된다.&lt;/p&gt;
&lt;h2 id=&quot;역-인덱스-inverted-index&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%AD-%EC%9D%B8%EB%8D%B1%EC%8A%A4-inverted-index&quot; aria-label=&quot;역 인덱스 inverted index permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;역 인덱스 (Inverted Index)&lt;/h2&gt;
&lt;p&gt;엘라스틱 서치는 검색할 데이터를 역 인덱싱한다. 역 인덱스란 무엇일까? 우리가 일반적으로 알고있는 데이터베이스 인덱스는 책 앞 부분의 목차로 비유한다. 그에 반면, 역 인덱스는 책 뒷부분의 찾아보기에 비유할 수 있다. 책에서 찾아보기는 &lt;strong&gt;특정 키워드가 책의 몇 페이지에서 등장했는지&lt;/strong&gt;를 나타낸다.&lt;/p&gt;
&lt;p&gt;MySQL 과 같이 일반적인 RDBMS는 텍스트로 데이터를 검색하기 위해서 &lt;code class=&quot;language-text&quot;&gt;LIKE&lt;/code&gt; 연산, 즉 패턴 매칭으로 데이터를 탐색한다. 그런데 문제는, &lt;code class=&quot;language-text&quot;&gt;LIKE&lt;/code&gt; 연산은 성능이 못하다는 점이다. 데이터베이스의 50번째 행, 1,000번째 행에 &lt;code class=&quot;language-text&quot;&gt;devhaon&lt;/code&gt; 라는 데이터가 저장되어있는 경우를 가정해보자. 만약 RDBMS 에서 &lt;code class=&quot;language-text&quot;&gt;LIKE %devhaon%&lt;/code&gt; 으로 데이터를 검색한다면, 1,000번째 행까지 모두 탐색을 해야한다. 반면 역 인덱스 구조에선 &lt;code class=&quot;language-text&quot;&gt;devhaon&lt;/code&gt; 이라는 키워드가 어디있는지 바로 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ef7ac981498fe1987bed709802a71f71/bcec6/Screenshot%202024-12-06%20at%207.43.29%20PM.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.65030674846626%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABoUlEQVR42lWSS0/CQBSF+7tJqCTsfOz9A8aFURMTV8ZoNFoMEKtgsLTQ0lL7mJa2tFAFyXHuEFAXJ9Np535zzr2ViqLARrPZTCjP83+aTqdCm/36fMk1x4yryH9rpDRNEYYhGGMIgkA8/4VR8WKxECqKXIC04RkaLztodXehqDJ06wLz+Te/NINEIE3TYFkWdF2HaZrIskQUk9s4jqEoihCLGMryE+96A+rbJYbOIwb2HYKozy8v1kDHcQTEMIztGoYBdxzwAyna7Raq1SoqlQqazSaWiyVcbwTD7IHFHuLUx5Qn+SzB439BGo/HIJE7gpPTIPRhDPr44IWNp3vUajXIsixcLpcr2O4DDPsc+ugUveEx+tYJX4/geMoaSL2bTCY8aiYURaFwR33rdrqo1+sCqqrP/N2Kg67Q7BziRjnAfWsft41dPKp7GDjXkAiQJMl2MARmLOLA9VB83xc9pgT0jXoVTXyMnD5eu230tFe0n59gOTrSLIZEU6RC13VFdAL//XVs2xYJSJ7nianHic8HYePDN+FyDS0NXmAhyRh+AMz0RSawAFioAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/ef7ac981498fe1987bed709802a71f71/a6d36/Screenshot%202024-12-06%20at%207.43.29%20PM.png&quot;
        srcset=&quot;/static/ef7ac981498fe1987bed709802a71f71/222b7/Screenshot%202024-12-06%20at%207.43.29%20PM.png 163w,
/static/ef7ac981498fe1987bed709802a71f71/ff46a/Screenshot%202024-12-06%20at%207.43.29%20PM.png 325w,
/static/ef7ac981498fe1987bed709802a71f71/a6d36/Screenshot%202024-12-06%20at%207.43.29%20PM.png 650w,
/static/ef7ac981498fe1987bed709802a71f71/e548f/Screenshot%202024-12-06%20at%207.43.29%20PM.png 975w,
/static/ef7ac981498fe1987bed709802a71f71/3c492/Screenshot%202024-12-06%20at%207.43.29%20PM.png 1300w,
/static/ef7ac981498fe1987bed709802a71f71/bcec6/Screenshot%202024-12-06%20at%207.43.29%20PM.png 1834w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위 그림을 살펴보자. 우선 Doc1 ~ 3 을 일반적인 RDBNS 에 저장하고, &lt;code class=&quot;language-text&quot;&gt;LIKE&lt;/code&gt; 연산자를 활용하여 &lt;code class=&quot;language-text&quot;&gt;database&lt;/code&gt; 라는 단어가 포함된 테이블을 검색한다고 해보자. (Doc 은 마치 데이터베이스의 하나의 행(row) 개념이라고 이해하면 쉽다.) 이 경우 해당 테이블의 모든 row 를 탐색해야 하므로, O(n) 이라는 시간이 소요될 것이다.&lt;/p&gt;
&lt;p&gt;반면 Elastic Search 를 사용하여 역 인덱스를 검색하는 경우라면, 곧 바로 &lt;code class=&quot;language-text&quot;&gt;database&lt;/code&gt; 행을 찾아보니 3번째 Doc 에 있다는 것을 알 수 있다. 마치 해시 테이블과 같이, key 값만 안다면 그 즉시 value 를 O(1) 에 찾아낼 수 있는 것과 같다. 이때 key 값이란 역 인덱스에서 &lt;code class=&quot;language-text&quot;&gt;토큰(term)&lt;/code&gt; 이 될 것이다.&lt;/p&gt;
&lt;h2 id=&quot;역-인덱싱-과정-토큰-생성-과정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%AD-%EC%9D%B8%EB%8D%B1%EC%8B%B1-%EA%B3%BC%EC%A0%95-%ED%86%A0%ED%81%B0-%EC%83%9D%EC%84%B1-%EA%B3%BC%EC%A0%95&quot; aria-label=&quot;역 인덱싱 과정 토큰 생성 과정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;역 인덱싱 과정 (토큰 생성 과정)&lt;/h2&gt;
&lt;p&gt;다시 한 번 앞서 예시로 들었던 &quot;Hello My World&quot; 를 예시로 들어, Elastic Search 에서 토큰이 생성되는 과정을 알아보자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 텍스트를 특정 단위 (여기서는 띄어쓰기) 단위로 분리한다. [&quot;Hello&quot;, &quot;My&quot;, &quot;World&quot;] 와 같이 분리한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 대문자를 모두 소문자로 변경하고, 토큰을 아스키 코드 순서대로 정렬한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 검색에 잘 사용되지 않는 굳이 필요없는 불용어(a, the, of, for, to 등) 를 제거한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 형태소 분석 과정을 거쳐서 단어를 원형으로 변환한다. (예를들어 executing 단어를 execute 와 같이 원형으로 변환)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; 중복되는 토큰에 대해 토큰 단 1개만을 남긴다. (중복 토큰 제거)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이렇듯 역 인덱스는 생각보다 생성하는데에 많은 작업이 요구된다. 그 때문에 원하는 데이터가 역 인덱싱되는데 까지 걸리는 시간이 좀 오래 걸리는데, 이 생성되는데 까지 걸리는 시간을 &lt;strong&gt;NRT(Near Real Time)&lt;/strong&gt; 이라고 한다.&lt;/p&gt;
&lt;h2 id=&quot;elk-stack-에서의-역할&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#elk-stack-%EC%97%90%EC%84%9C%EC%9D%98-%EC%97%AD%ED%95%A0&quot; aria-label=&quot;elk stack 에서의 역할 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ELK Stack 에서의 역할&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/81d73bd0515ea5c30169fe39cec3572a/3ceac/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.122699386503065%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABwklEQVR42p2Sz2sTQRTH8wd4EvIn6Kkn714qlRbpQQ9e7FWsh0IPFVEsQTzrQUEqHhTBghHbgwSNF6G/WJvVXY1ukW0b4oYsCW2zNenW7e7Ox8m6wU2DEv3Cm5n3ePN93zdvUhyGCKUFCV+ehejOCcVvO4QUfUHQL1LJCyL0CGovCCsPEF5N+gd41mOClhlliPYq1R4smbTmVfakCceNKUSCsN2mhG3Ms/FqmNq7QfbtOdnSPiyl4eOpRLsh25efsDuQwb2T79GeEjFzy3W5MHqG0yPnOfFyC3VHBr1t3s4cZ/HRAO/rFmV3L9J5Y2yc6bFJHPdHLE50t9xxM89mOXn3NmcXCry27Sg2MjTMkXSaY88fcuXLKvgB5yYucfT+TS5qi3hB0PXKMeEvd9Ntkfmqc8tQ+OxsRbE1Y42pa9eZWH7D02/rIAnuFRa4+klhSl9hvbkbD170Tjkp/XvDodFo9Eyx2Wzi+z5JIX/8Nu0q7RRfqlgtFDAMA+2DhmVZFItFSqUSqqpimmZX8b/+w05ivV4nl8uhKAq6rpPNZqNd0zTy+Xz/hB3YcijVajWycrkcFahUKtHuOA6e5/0b4f/iJzkjffu0nYqgAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/81d73bd0515ea5c30169fe39cec3572a/a6d36/image.png&quot;
        srcset=&quot;/static/81d73bd0515ea5c30169fe39cec3572a/222b7/image.png 163w,
/static/81d73bd0515ea5c30169fe39cec3572a/ff46a/image.png 325w,
/static/81d73bd0515ea5c30169fe39cec3572a/a6d36/image.png 650w,
/static/81d73bd0515ea5c30169fe39cec3572a/e548f/image.png 975w,
/static/81d73bd0515ea5c30169fe39cec3572a/3c492/image.png 1300w,
/static/81d73bd0515ea5c30169fe39cec3572a/3ceac/image.png 1456w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Elatic Search 는 흔히 부르는 ELK Stack 에 사용된다. ELK Stack 이란 Elastic Search, Logstatch, Kibana 를 합친 약자로, 데이터를 수집하고 분석하는 도구를 뜻환다.&lt;/p&gt;
&lt;p&gt;그 중에서 Elastic Search 는 Lucene 기반 검색엔진 (NoSQL 데이터베이스로도 분류하기도 함) 이며, 대량의 데이터를 빠르게 저장하고 검색할 수도 있도록 지원한다. 앞서 살펴봤듯이, 빠른 데이터 검색을 위해 데이터를 &lt;strong&gt;역 인덱싱(Inverted Index)&lt;/strong&gt; 하여 저장한다. Elastic Search 는 클러스터 구조로 분산 구성된다. 따라서 고가용성과 확장성있는 설계에 유리하다. 또한 우리가 아는 일반적인 RDBMS 와 다르게 SQL 을 지원하지 않고, &lt;strong&gt;Restful API 를 통해 데이터를 추가하고, 검색&lt;/strong&gt;한다. 마치 SQL 의 SELECT 쿼리가 엘라스틱 서치에서는 GET 요청에 대응되고, INSERT 는 POST 요청에 대응된다.&lt;/p&gt;
&lt;p&gt;ELK 스택은 일반적으로 로그 수집, 처리 및 분석에 활용된다. 이 중에서 엘라스틱 서치는 계속 설명했듯이 검색 엔진으로, 수집한 로그를 저장하는데에 활용된다.&lt;/p&gt;
&lt;h2 id=&quot;단점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90&quot; aria-label=&quot;단점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h2&gt;
&lt;p&gt;엘라스틱 서치 또한 단점이 존재하기에, RDBMS 를 많이 사용한다. 엘라스틱 서치는 JOIN 절을 지원하지 않는다고 한다. 또한 트랜잭션을 지원하지 않는다.&lt;/p&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;엘라스틱 서치란?&lt;/strong&gt; : 엘라스틱 서치는 역 인덱스 구조의 검색엔진으로, 검색어가 될 수 있는 문자에 대해 여러 토큰(term) 을 생성하고 O(1) 에 검색할 수 있도록 지원한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Elastic Search의 인덱스구조와 RDBMS의 인덱스 구조의 차이점&lt;/strong&gt; : 일반적인 RDBMS의 인덱스는 B+ Tree 기반의 자료구조로 구성되며, 정형 데이터에 대한 검색만을 빠르게 지원한다. 반면, 엘라스틱 서치는 역 인덱스 자료구조로 구성되며, 비정형 데이터에 대한 검색을 빠르게 지원한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Elastic Search의 키워드 검색과 RDBMS의 LIKE 검색의 차이점&lt;/strong&gt; : RDBMS 는 단순히 패턴과 일치하는 검색(단순 패턴 매칭)만을 제공하여, 동의어나 유의어 같은 검색은 불가능하다. 하지만, 엘라스틱 서치는 동의어나 유의어를 활용한 검색이 가능하며, 비정형 데이터의 색인과 검색이 가능하고, 역 인덱스를 지원하므로 매우 빠른 검색이 가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 MySQL 최신 버전에서 n-gram 기반의 &lt;strong&gt;Full-Text Search&lt;/strong&gt; 지원하여, 동의어와 유의어에 대한 검색이 가능해졌다고는 한다. 하지만, 한글 검색의 경우 아직 많이 빈약한 감이 있다고 한다. 이때 Full-Text 란 이미지, CSS, 글 등의 복합적으로 이루어진 컨텐트에서 순수하게 텍스트만 추출한 데이터를 뜻한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/elasticsearch-inverted-index/&quot;&gt;https://hudi.blog/elasticsearch-inverted-index/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sirzzang.github.io/dev/Dev-elk-stack-01/&quot;&gt;https://sirzzang.github.io/dev/Dev-elk-stack-01/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://esbook.kimjmin.net/06-text-analysis/6.1-indexing-data&quot;&gt;https://esbook.kimjmin.net/06-text-analysis/6.1-indexing-data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lcs1245.tistory.com/entry/SQL-LIKE-%EC%97%B0%EC%82%B0%EC%9E%90-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%B6%80%EB%B6%84%EC%9D%BC%EC%B9%98-%EA%B2%80%EC%83%89&quot;&gt;https://lcs1245.tistory.com/entry/SQL-LIKE-%EC%97%B0%EC%82%B0%EC%9E%90-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%B6%80%EB%B6%84%EC%9D%BC%EC%B9%98-%EA%B2%80%EC%83%89&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev-coco.tistory.com/158&quot;&gt;https://dev-coco.tistory.com/158&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MSA 기초]]></title><description><![CDATA[MSA 등장 배경과 이유는 어쩌면 다 풀어 설명할 수 있을 것 같지만, MSA 의 전반 오버뷰에 대해 다시 학습해보겠다는 차원에서 포스팅을 작성해본다. 모놀리식 아키텍처의 한계 MSA…]]></description><link>https://haon.site/kafka/msa/</link><guid isPermaLink="false">https://haon.site/kafka/msa/</guid><pubDate>Thu, 28 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;MSA 등장 배경과 이유는 어쩌면 다 풀어 설명할 수 있을 것 같지만, MSA 의 전반 오버뷰에 대해 다시 학습해보겠다는 차원에서 포스팅을 작성해본다.&lt;/p&gt;
&lt;h2 id=&quot;모놀리식-아키텍처의-한계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A8%EB%86%80%EB%A6%AC%EC%8B%9D-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%9D%98-%ED%95%9C%EA%B3%84&quot; aria-label=&quot;모놀리식 아키텍처의 한계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;모놀리식 아키텍처의 한계&lt;/h2&gt;
&lt;p&gt;MSA 에 대해 본격적으로 학습하기 전에, 우선 모놀리식 아키텍처 시스템에 대해 다시 짚어볼 필요가 있다. 모놀리식 시스템이란 웹 애플리케이션을 하나의 단일한 독립 시스템으로 구축하는 아키텍처이다. 즉, 하나의 애플리케이션에 모든 비즈니스 로직을 통째로 모아놓은 구조를 취한다.&lt;/p&gt;
&lt;p&gt;이 특징 덕분에 모놀리식 아키텍처는 소규모 프로젝트에 적합하다. 개발, 빌드, 배포 및 테스트가 용이하다. 하지만, 기업의 시스템 규모가 점점 커지고 복잡해지는 경우 모놀리식 아키텍처의 한계점이 드러나기 시작한다. 무엇이 문제가 될까?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 작은 수정사항 하나에도 전체 빌드 및 재배포가 다시 일어나야 한다. 이 때문에 불필요하게 빌드 및 배포 시간을 잡아먹는 시간이 많아진다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 많은 양의 코드가 몰려있어서 유지보수가 어려워진다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 시스템내의 자그마한 한 부분에 장애가 발생하면 전파되어 시스템 전체에 영향을 미치고, 자칫 시스템의 모든 기능이 중단될 수도 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 특정 부분에 대해서만 Scale Out 이 어렵다. 만약 배달의민족 서비스내의 여러 기능중에 주문 기능에 대해서만 요청의 빈도수가 매우 높아, 이에 대해서만 성능을 올리고 싶다 해보자. 즉, Scale Out 을 하고 싶은 경우인데, MSA 와 달리 주문 도메인 서버가 분리되어 있지 않으므로 부분 Scale Out 이 어렵다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;msa&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#msa&quot; aria-label=&quot;msa permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MSA&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/12f095b7174e91730c0d0eeac6b1cf2b/e4611/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.05521472392638%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACPklEQVR42oWTXWsTURCG84cEEX+C9ErxSqneKIKIiHhTe6PglZSKH1T8AJEirS1pbC9SadWmQk1LxIZa/Era0qw1TdIkm3STbDabbPbs49ndJCoUPDDsMDvnnYeZOQE6R9d1VFWl0Wjwv6NpFZrNpuc7jtOLu7GAIxwZFFS1fXKZXSlcdbNw494FIf4xT7Bcomn6hYUtsG3byzdNk4AbjCfLTEayzMaKTEQyfFe0TnWfoEtR1JqMvUszHS3IvCxzsZxbsUfYE1xay3Fn/BtPZpIMy288UfSrC1/M9mgd9nWLYOQnU5EUE2+3eR/Pkc/n0esmVvsvwntjMY6efc6Ja0GO9D9jbHbdE7RFl84nNBsGhl6homkYRh3R0hl5GePUQJDr9xfIqxVfcPRVlJOXHnF+cJTjFx8SXvQFU4riUTYtm0qtTqGo4sLmCyUM08KyLJY+Jpl685mV9bQkNXzBu0+nOdx3lb4zNzl07AovQhE6EyFTqHLhVpjTAyHml7eYi27RPzjN5dtzbOyUGB5d5tyNGR4HV2V+2xV0qFTrZPdUUqkd9gplarJSr9GtNitrsl+xBMV9g72S7vmfvv5Cb1j82C4wv/SFDUXFarUIiM4qpLI1JhcUcqrxZyAd0XhCZXY5TcsS0mzCH3Z6g0tlaoQWFVTNpG3JPbRtX3B9s8zQeIKtdKUzEEF3Z1+vZBgJbVI3LOqS6kFwg3B01/u3KosNjSdRJJCwJWF3zw6y7kvwfEccnOP4D8NtnftSfgP3zCJg+dLRIgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/12f095b7174e91730c0d0eeac6b1cf2b/a6d36/image.png&quot;
        srcset=&quot;/static/12f095b7174e91730c0d0eeac6b1cf2b/222b7/image.png 163w,
/static/12f095b7174e91730c0d0eeac6b1cf2b/ff46a/image.png 325w,
/static/12f095b7174e91730c0d0eeac6b1cf2b/a6d36/image.png 650w,
/static/12f095b7174e91730c0d0eeac6b1cf2b/e548f/image.png 975w,
/static/12f095b7174e91730c0d0eeac6b1cf2b/e4611/image.png 1298w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;MSA 는 이러한 모놀리식 시스템의 대안으로 등장했다. MSA 환경에서는 시스템을 여러 작은 독립적인 서비스로 분할하여 독립적으로 개발하고, 관리하고, 배포한다. 또한 장애 발생시 다른 서비스로 전파될 위험이 줄어든다. 발생한 해당 장애는 해당 마이크로 서버에서만 한정되고, 다른 마이크로 서버에는 영향을 미치지 않는다. 즉, 각 마이크로 서버는 서로에게 영향을 받지않고 독립적으로 확장(Scale Out) 가능하고, 관리할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;msa-의-장점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#msa-%EC%9D%98-%EC%9E%A5%EC%A0%90&quot; aria-label=&quot;msa 의 장점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MSA 의 장점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 개발 유연성의 한계 극복 : 모놀리식 아키텍처는 모든 구성요소가 하나의 애플리케이션으로 구성되어 있어, 변화에 대한 대응이 어렵다. 만약 새로운 기능 추가를 하거나 자그마한 수정을 하나만 하고싶더라도 전체를 빌드, 재배포해야하는 문제가 있었다. 반면 MSA 는 꼭 필요한 독립적인 마이크로 서버만 수정 및 빌드/배포가 신속히 가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 배포/롤백 리스크 감소 : MSA 는 여러개의 서비스로 구성되어 있다. 이 중 수정이 필요한 부분만 빠르게 찾아 정확히 대응이 가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 장애 전파 방지 : 모놀리식은 특정 부분에 대한 장애가 시스템 전체로 전파될 수 있다. 반면 MSA 는 각각의 서비스가 약한 결합도를 가지므로 장애 전파를 최소화하거나 아예 막아버릴 수 있다. 이를 통해 장애 서비스만 빠르게 FailOver 할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 독립적인 Scale Out 가능 : 모놀리식에서 Scale Out 을 하고 싶다면 불필요한 서비스 및 도메인 코드까지 포함하여 함께 확장해야 한다. 반면 MSA 는 원하는 마이크로 서버만 Scale Out 이 가능하다. 이 덕분에 효율적인 리소스 사용이 가능해진다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;단점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90&quot; aria-label=&quot;단점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;p&gt;하지만 당연하게 단점 또한 존재한다. MSA 는 만능이 아니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 모놀리식에 비해 복잡도가 매우 크다. 코드가 직관적이지 않기 때문에 단번에 코드를 이해하고 수정하는 것이 어려워서 유지보수가 자칫 어려워질 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 성능이 저하될 수 있다 : MSA 의 각 마이크로 서버는 서로 네트워크 통신을 해야하므로 네트워크 지연 및 Latency 가 발생할 수 있다. 즉, 모놀리식 아키텍처에 비교한다면 성능이 더 느리다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 데이터 일관성 : 서비스가 독립적으로 데이터를 가지고 있을 때 일관성을 유지하기 어려울 수 있다. 분산 시스템에서 트랜잭션 처리, 일관성 유지 등에 대한 처리 기법에 대한 높은 이해도가 요구된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 테스트가 어려워진다 : 모놀리식에 비해 여러 서비스간의 통합 테스트와 E2E 테스트를 수행하기 어려워진다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://metanetglobal.com/bbs/board.php?bo_table=tech&amp;#x26;wr_id=38&quot;&gt;https://metanetglobal.com/bbs/board.php?bo_table=tech&amp;#x26;wr_id=38&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=H_DaPyUOeTo&amp;#x26;t=54s&quot;&gt;https://www.youtube.com/watch?v=H_DaPyUOeTo&amp;#x26;t=54s&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=dSGnJWHuxtQ&amp;#x26;list=PLkiWrI-TY1CepeQNZAySCu8NrhL_yjfZs&quot;&gt;https://www.youtube.com/watch?v=dSGnJWHuxtQ&amp;#x26;list=PLkiWrI-TY1CepeQNZAySCu8NrhL_yjfZs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=0lyrd5FlETQ&quot;&gt;https://www.youtube.com/watch?v=0lyrd5FlETQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=KNfNopXVYyc&amp;#x26;t=18s&quot;&gt;https://www.youtube.com/watch?v=KNfNopXVYyc&amp;#x26;t=18s&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hahahoho5915.tistory.com/71&quot;&gt;https://hahahoho5915.tistory.com/71&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@tedigom/MSA-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-1-MSA%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90-3sk28yrv0e&quot;&gt;https://velog.io/@tedigom/MSA-제대로-이해하기-1-MSA의-기본-개념-3sk28yrv0e&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[✒️ 카카오테크 부트캠프에서 찾아낸 나만의 효과적인 프로그래밍 학습법]]></title><description><![CDATA[…]]></description><link>https://haon.site/회고/effective-study-way/</link><guid isPermaLink="false">https://haon.site/회고/effective-study-way/</guid><pubDate>Thu, 28 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 카카오테크 부트캠프는 자율 주제 글쓰기 &amp;#x26; 소정의 이벤트가 주어진다. 이 포스팅은 &quot;카카오테크 부트캠프에서 찾아낸 나만의 효과적인 프로그래밍 학습법&quot; 주제로 작성한 글이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;최고의-소프트웨어-개발자-양성-과정-카카오테크-부트캠프&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B5%9C%EA%B3%A0%EC%9D%98-%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%96%91%EC%84%B1-%EA%B3%BC%EC%A0%95-%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%85%8C%ED%81%AC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84&quot; aria-label=&quot;최고의 소프트웨어 개발자 양성 과정 카카오테크 부트캠프 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;최고의 소프트웨어 개발자 양성 과정, 카카오테크 부트캠프&lt;/h2&gt;
&lt;p&gt;벌써 첫 눈이 내린 11월 말이네요. 저희 카카오테크 부트캠프 크루들과 어제 라이언 눈사람도 만들고, 교육장에 들어와 따뜻한 커피도 한 잔 했답니다. 최고의 동료, 훌륭한 멘토들과 함께 성장하는 이곳이 정말 행복하기만 한데, 벌써 수료를 앞두고 있네요. 아직 카카오테크 부트캠프에서 동료들과 하고 싶은 것들이 넘쳐나는데, 시간은 무정하게 흘러갔습니다. 곧 12월부터 카카오테크 부트캠프 2기 모집이 시작된다고 합니다.&lt;/p&gt;
&lt;p&gt;문득, 수료를 앞두고 있으니 작년 이맘때 혼자서 프로그래밍을 공부할 때가 생각나네요. 혼자서 공부하니 무엇을 공부해야하는지 조차 스스로 찾아야 했고, 혼자 긴 시간을 방황해야 했습니다.  곁에서 올바른 방향으로 바로 잡아줄 멘토가 없다보니, 정말 많이 방황하게 되더라고요.. 스스로가 제대로 공부하고 있는 게 맞는지 끊임없이 의심하면서 감정 소모도 많이 했습니다. 그런 저에게 카카오테크 부트캠프는 큰 길잡이가 되었어요. &lt;strong&gt;무엇을 시작해야하는지 조차 몰랐던 저에게 현장 중심의 교육 기회를 제공해주었고, 평생동안 자기 주도적으로 학습할 수 있는 역량을 가진 인재로 만들어주었거든요.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;지금부터, 카카오테크 부트캠프만의 소프트웨어 교육 철학을 소개해볼까 해요. 최고의 몰입 환경 속에서 그간 어떻게 성장했는지를 돌이켜보고자 합니다. 그리고 6개월의 과정 속에서 제가 만들어낸 나만의 자기주도 학습법에 대해 공유해볼까 합니다 😎&lt;/p&gt;
&lt;h2 id=&quot;전통적인-교육-그리고-카카오테크-부트캠프&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%ED%86%B5%EC%A0%81%EC%9D%B8-%EA%B5%90%EC%9C%A1-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%85%8C%ED%81%AC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84&quot; aria-label=&quot;전통적인 교육 그리고 카카오테크 부트캠프 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전통적인 교육, 그리고 카카오테크 부트캠프&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e8fc08abff4e060ca214176168bae8c9/891d5/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.828220858895705%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC+klEQVR42h2SW0+bBRjHe++tH8Bs8WJ+ARPvNItxLhIMxC2aiBKzOI0HSnCUHigt9GDXao/Qg6W0tH1LaaEHWt6Ot+fj2rEO5iIuekHQbPohfr548U+ePMn/l+fwVyS3Q+QyCcRimnIuQa2SoSXl6DbKDHsNhv0Wg16LXqdJp1mjWatSr0lIRyLVS4lFxEKGUk6gkNlCoddpiUVD9LtHskmi3Xwg15fGEp2WyPHxiJOnzxifPGN0/ITBYEir1UY8LFHYS8ugHVkJGRZhX1hHseFx4vM6icc3KRxkaber9Ht1Wk2Rfr9DszeiM36O1H5EZm+fwLoL08oiqvk7JKIO0nEH+0knecFFKmxCUcjGsViMfDA1zfu3P+Lm5Ht8qTTz6V09OvUca6saDMuL6Jbm0au/R6P8goWvbnNnZpp0cp2AV4N64XO8di1hnxnFoTxufdCj99c/RAsl5rUa4o0hRn8ck1mLw6qjWMzRGzxE2A5iMKj5ePYbrPp36D24SSBoweazsyuJSKPHKBrVA+zWFSbffYtPJt5mZuo6d2cm+PqzSZw2PcmIl8PCLo1ahcSmn8KuiTXDFLc+fJVq8TpCyk0o4iKdF6j/+vwSWKC4l8DnsuJ1GPA6zUTDbrY3zIQ8ZoQtD6lNN7Ggg0jART2ppOW8wptvvILbbSElbBD4xYN4mYLT32SgHJP8nkB4M0RCjlBsy89uKobdsiLfbY6dZJj99JYM/JlYwI5R9x3Xrl3lytXXsK4qCXgMRPz3kSp56lURhdNqxKTXYNQusaxRoVH9wNKiknsL32JzmBg+HjIeP6Hb7VCXDuW1XcxOvM6t6Rvcm5slEb5PvRynWUlTLaVRqBYX0GlVGA0azCYddpsB+48raDVyf1lFo17h4vyci4sLzv/8nbOTIdVyhqwQ5iAbQcxFKe4EyaeCZLZ9KNZW1djMOhwyxG7VYzJqMeiX8Ht/QipnOTsd8O/Lv3nx4iV/nJ0yHtQYdeXwN8v/a9QWeSSrL//iKB/nP3Tfc9oosbq8AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e8fc08abff4e060ca214176168bae8c9/a6d36/image.png&quot;
        srcset=&quot;/static/e8fc08abff4e060ca214176168bae8c9/222b7/image.png 163w,
/static/e8fc08abff4e060ca214176168bae8c9/ff46a/image.png 325w,
/static/e8fc08abff4e060ca214176168bae8c9/a6d36/image.png 650w,
/static/e8fc08abff4e060ca214176168bae8c9/e548f/image.png 975w,
/static/e8fc08abff4e060ca214176168bae8c9/3c492/image.png 1300w,
/static/e8fc08abff4e060ca214176168bae8c9/891d5/image.png 1520w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;figcaption&gt; 카카오테크 부트캠프 오프라인 교육장 &lt;/figcaption&gt;
&lt;p&gt;저희는 초, 중, 고등학교와 대학교를 거치며 책상에 가만히 앉은 상태로 지식이 주입되는 교육 방식에 익숙합니다. 하지만, 그런 교육에는 주관과 비판적 사고가 결여되어있죠. 이런 전통적인 교육과정을 거쳐 간 저희 대부분은 스스로 생각하고, 고민하는 방법을 잊은 지 오래 일 것입니다. 자신이 진정 무엇을 하고 싶은지도 모른 채 높은 점수를 위한, 평가를 잘 받기위한 학습만을 해온 저희는 학습의 본질을 잊은 채 살아왔던 것은 아닐까요? 🤔&lt;/p&gt;
&lt;p&gt;일방적으로 답을 알려주는 교육은, 교육받는 이로부터 스스로 답을 찾기 위한 고민의 기회를 박탈하는 것입니다. 타인이 정의해준 문제를 남이 정해놓은 방법으로 해결하는 것을 의미있는 학습이라고 부를 수 있을까요? &lt;strong&gt;&quot;Why?&quot;&lt;/strong&gt; 라는 질문을 던지는 학생을 찾아볼 수 없는 교육에서 저희는 정말로 본질을 이해하고, 깨달을 수 있을까요?&lt;/p&gt;
&lt;h3 id=&quot;pblproblem-based-learning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pblproblem-based-learning&quot; aria-label=&quot;pblproblem based learning permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PBL(Problem Based Learning)&lt;/h3&gt;
&lt;p&gt;카카오테크 부트캠프는 일방적인 강의를 진행하면서 정답을 제공하는 교육이 아닙니다. 카테부는 강의 위주의 교육 보다는 잘 설계된 미션, 그리고 관련된 최소한의 지식이나 키워드만을 크루들에게 던져줍니다. 그리고 크루들은 주어진 개인 &amp;#x26; 팀 미션을 해결하기 위해 정해진 해결 방법이 아닌 자신만의 해결 방법을 만들기 위해 끊임없이 고민하고, 스스로 찾아야합니다. &lt;strong&gt;즉, 학습자는 지식보다 문제을 먼저 접하고, 문제를 해결하는 과정에서 자연스럽게 필요한 지식을 습득합니다.&lt;/strong&gt; 그렇게 크루들은 자신이 직접 학습한 지식의 필요성에 대해 깊은 공감을 할 수 있습니다. 카카오테크 부트캠프는 이 교육 철학을 &lt;strong&gt;PBL(Problem Based Learning)&lt;/strong&gt; 이라고 부르고 있어요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project Based Learning&lt;/strong&gt; : 프로젝트 기반 학습&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Problem Based Learning&lt;/strong&gt; : 문제 해결 기반 학습&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;(Sequential) Progress Based Learning&lt;/strong&gt; : (순차적) 진행 기반 학습&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;카테부(일명, 카카오테크 부트캠프) 는 &lt;strong&gt;PBL(Problem Based Learning)&lt;/strong&gt; 를 위와같이 정의하고 있어요. 전통적인 수동적 수업 방식을 벗어나 학습자가 학습의 주체가 되어 능동적인 학습 활동을 할 수 있도록 설계된 수업을 말하는 것으로, 주체적이고 협동적인 학습 활동을 통해 문제해결 능력, 자기주도학습 능력, 협동학습 능력을 향상시킬 수 있는 효과적인 학습 방법을 뜻합니다.&lt;/p&gt;
&lt;p&gt;주어진 개인 &amp;#x26; 팀 미션을 열심히 마치고 멘토님에게 피드백을 요청하면, 그에 대한 상세 피드백이 미션이 끝난 시점에야 비로소 진행됩니다. 문제를 해결하기 위해 많은 고민을 끝마치고, 이미 자신만의 해결 방법을 만들어낸 상황에서 듣는 강의는 우리에게 &apos;정답&apos;이 아닌 문제에 대한 &apos;새로운 관점&apos; 이죠. 이 과정에서 잘못 알고 있던 내용을 교정할 수 있고, 모호하게 알고 있던 부분이 좀 더 선명해지게 됩니다.&lt;/p&gt;
&lt;p&gt;미션 수행 과정에서 만들어진 각자의 문제 해결 방법은 &lt;strong&gt;평가되거나, 채점되는 방식이 아닙니다.&lt;/strong&gt; 생각이 다르고 더 경험이 풍부한 멘토님들을 통해 피드백과 자유로운 의견을 주고받는 과정만이 존재할 뿐이죠. 🙂&lt;/p&gt;
&lt;h2 id=&quot;카카오테크-부트캠프에서-발견한-나만의-평생-자기주도-학습법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%85%8C%ED%81%AC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84%EC%97%90%EC%84%9C-%EB%B0%9C%EA%B2%AC%ED%95%9C-%EB%82%98%EB%A7%8C%EC%9D%98-%ED%8F%89%EC%83%9D-%EC%9E%90%EA%B8%B0%EC%A3%BC%EB%8F%84-%ED%95%99%EC%8A%B5%EB%B2%95&quot; aria-label=&quot;카카오테크 부트캠프에서 발견한 나만의 평생 자기주도 학습법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;카카오테크 부트캠프에서 발견한 나만의 평생 자기주도 학습법&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;맹목적으로, 자신의 생각과 깊은 고민이 담겨있지 못한 학습 태도는 학습자 본인에게 절대 유의미한 성장을 끌어낼 수 없습니다.&lt;/strong&gt; 학습 방법도 마찬가지입니다. 모두에게 적용되는 절대적인 최적의 학습법이 있을까요? 한 사람에게 매우 효과적으로 작용한 학습 방법이 다른 사람에게는 무용지물일 수 있습니다. 사람 모두는 각기 다른 성향을 갖고 있기 때문이죠.&lt;/p&gt;
&lt;p&gt;저 또한 카카오테크 부트캠프에서 나만의 효과적인 학습법을 찾아내는데 이것저것 많은 방법을 시도했습니다. 맹목적으로 귄위있는, 일방적인 누군가의 의견을 따르는 것 보다, 평생 저만의 학습법을 찾는 것이 좋겠다는 생각이었습니다. 카테부 교육 철학 또한 일방적인 주입식 교육이 아닌, 본인과 크루별로 자율적인 문화, 학습법을 찾아내길 강조하죠. 저는 이 교육 철학 속에서 아래와 같은 학습법을 찾아낸 것 같습니다. 몰론 아직 다듬어야 할 부분은 많지만, 어느정도 학습법이 정교화된 것 같습니다.&lt;/p&gt;
&lt;h2 id=&quot;기록하고-공유하고-설명하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EB%A1%9D%ED%95%98%EA%B3%A0-%EA%B3%B5%EC%9C%A0%ED%95%98%EA%B3%A0-%EC%84%A4%EB%AA%85%ED%95%98%EA%B8%B0&quot; aria-label=&quot;기록하고 공유하고 설명하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기록하고, 공유하고, 설명하기&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 제 프로그래밍 학습법에 대한 자세한 내용이 궁금하다면 &lt;a href=&quot;https://haon.blog/about/&quot;&gt;프로그래밍을 공부하며 능동적인 성장을 위한 학습방법&lt;/a&gt; 을 참고해주세요! 😎&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;저는 한 주제에 대한 학습을 제대로 마쳤다! 의 기준을 &lt;strong&gt;남에게 명확히 설명할 수 있는가?&lt;/strong&gt; 로 판단합니다. 즉, 주어진 학습 키워드를 나만의 언어로 타인에게 제대로 설명할 수 있어야지 자신만의 지식으로 체화한 것이라고 생각해요. 이를 확인하기 위한 좋은 방법은, 주변 크루들에게 내가 학습한 내용을 설명해보는 것입니다. 학습한 내용을 타인에게 설명하다보면 내가 어떤 부분을 잘 모르고, 잘 이해했는지 금방 알아차릴 수 있거든요. &lt;strong&gt;즉, 학습한 지식을 머리 밖으로 인출(Output)하는 과정속에서 생각이 정교화(elaboration) 되고, 메타인지가 활성화됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5a304fd53cf1a19929c152c381f19aec/229ad/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.828220858895705%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAADIElEQVR42jWPW0zbZRiH/xdG3ZXKIToTgxsi2YbMQFetbIkZjgIaLxTnxYgbWguKrjgwbJHNbBwmKNmKAzKEVeIkZOKmtFk3TkMZsDWDEcdKDeXQ0gOUthTbCri2j3+IfnmffN/7XTz5/YQH4QgrD8KshiIi4Y33Ot7g39hcbrz+IB5/APfyMq4lHwveJeYX3djnnczZ7czOWbGImM2TTExMIESAtVCIUCSCOITD6z8QXFll1u4kFA5v7JH/+P8EVv/B5QvgWfbjdHuxOpwbcmFsxsac241fFKyL12+jeUpM4mHW6WTetcDkjIVFp4m/lmw4Fjw4rCZsJh3+VR9LgSCLHhsenxuLKBVGp+3cNv5J99AdpuddnKprRip/B01bB+NTFto7LhOXJEH5qYqmCxriX0wjWbqH0tJi1tYCWC1mTPcNTE9NUFh0BKG2rp5BwyhNrW1MTs+g7xugRt2I4pMSNO2XOXbiJDslMrLefItvzqiJfTqOzc/EcTDvA7F4CJ/XgfGegQ+VSvZmyBESk3fx46WfKTxcxJ2RERbE+udbNOzPPcSVTi36rmv09vVwrqGBki+Ok7B9B+kZGRQVF1NxupqGejVtF5tJSEwkJTUFIb/kJJ36LhTKAl6TZ9Pc0kJfbzey3XuoPF3J8PAgA7/fYPBmP6kvv0L2G6/T+esVggEf5VU17JNnkV+Qz5b4rcQ++RTCL9cG6Orp49W96TwWFct7h/IYHroppshk2/ZtqA5/xK2hfu6PjyHPyiRtdxpv5+TQ8dMluq7rab/4HY31Z8SUF/jh+/MIhrtGdFevk75PTopkF2VlZdR8XU2qRMqjmzYRHRND8RHVBi/sTGZHUhIymUyUnMU2N8VKwIvLMY3VPMbt/k6EkT9MaPU9fNvYRNmJL8X4BZSXn6L06FG2PpfAgdxcWls1Yr2vSJa8JLaI5tktcRw/pqKt9RzjowMM/XaVutoKGutqEO7eM3LLMEJ37w20Oh06rZazajXvKxTkKZTk7H+Xz0s+o7KqivjnE3k86gmiYqJ56OFHkEolqAoVfKw8SHZ2JrXVFfwLLbx20an6xecAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/5a304fd53cf1a19929c152c381f19aec/a6d36/image-1.png&quot;
        srcset=&quot;/static/5a304fd53cf1a19929c152c381f19aec/222b7/image-1.png 163w,
/static/5a304fd53cf1a19929c152c381f19aec/ff46a/image-1.png 325w,
/static/5a304fd53cf1a19929c152c381f19aec/a6d36/image-1.png 650w,
/static/5a304fd53cf1a19929c152c381f19aec/e548f/image-1.png 975w,
/static/5a304fd53cf1a19929c152c381f19aec/3c492/image-1.png 1300w,
/static/5a304fd53cf1a19929c152c381f19aec/229ad/image-1.png 1356w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;figcaption&gt; 실시간 오프라인 오피스 작업 공간 &lt;/figcaption&gt;
&lt;p&gt;이 믿음을 갖고 카테부에서 제 학습법을 정교화시키기 위해 크루들과 정말 다양한 방법을 시도해봤어요. 한 뜻을 가진 크루들과 스터디를 하며 회의실에서 발표도 해보고, 교육장 내의 오피스에서 일과시간에 끝나고 다같이 남아서 뜨겁게 토론도 해봤습니다.&lt;/p&gt;
&lt;p&gt;카테부 오프라인 교육장은 &lt;strong&gt;자신만의 효과적인 학습법을 찾아낼 수 있도록 장기간 몰입하기에 최고의 환경&lt;/strong&gt;이에요. 자율적인 몰입 환경에서 성장에 열망 넘치는 크루들이 가득하고, 함께 성장하며 여러 학습법을 시도해볼 수 있고, &lt;strong&gt;정해진 규정없이 자신만의 정답을 찾아갈 수 있도록 지원합니다.&lt;/strong&gt; 즉, 나만의 색깔과 개성을 찾아갈 수 있도록해요. 이를 위해 중앙 타운홀 무대에서 짧게 강연을 해도 좋고, 각 팀 마다 제공되는 오피스에서 프로젝트 회의를 해도 좋고, 회의실을 예약하고 스터디와 소규모 발표 세미나를 열여도 되거든요. (카테부 멘토님들, 운영진, 크루들 모두에게 항상 땡큐 😉)&lt;/p&gt;
&lt;p&gt;그런데, 문제점이 하나 있었습니다. 저 또한 교육장에서 여러 학습법을 시도해보던 중 한 가지 아쉬운 점을 발견했어요. 그건 바로 24시간 내내 저를 따라다니며 설명을 들어줄 크루는 그 어디에도 없었다는 점이에요. 밤새 공부하고 싶어도 사람마다 공부하는 시간대, 주제 모든 것이 똑같을 순 없죠.&lt;/p&gt;
&lt;h3 id=&quot;블로깅을-통해-학습한-지식을-설명하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%94%EB%A1%9C%EA%B9%85%EC%9D%84-%ED%86%B5%ED%95%B4-%ED%95%99%EC%8A%B5%ED%95%9C-%EC%A7%80%EC%8B%9D%EC%9D%84-%EC%84%A4%EB%AA%85%ED%95%98%EA%B8%B0&quot; aria-label=&quot;블로깅을 통해 학습한 지식을 설명하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;블로깅을 통해 학습한 지식을 설명하기&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1d6213942d4909985abc0084053deb9f/21b4d/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.48466257668711%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACPUlEQVR42o2Sy04UQRSGm4hhYTBENLASo0YuJiZG3RkX7nXt9QF4Ad/EhQ9glAQQIQqoJMKKjEYQ5DI4g9Igk2Ga6ctc+jZdn6d7JhEFlUr+nOqqU1+fv05pURSxV7VajTAMicfW1hbpdJr1bIb0Wobl5SX5XpW4TLVaRSnFn+c1/jGCIMC2LQHn0TPTOFYOy3awLCv58UFjH9BxHHK5HIZhYBaLVF2fMLBh4xbYTxtZ0V+L2Af0PI9sNpvYNYwC5arY/9GPWulBrfahKilUnKiiwwErlUpiaXd3By+Qc9ZzWGiBr9fhSwdkbspi0MhW/wfGd1Mo5Kl6khysoZZOS3Xdol5U+jJq4Thq+1EDFR3Gsisd9OIZrN+ARUlZ7YSVdomn6nGxCUpDB1rX4lbHNj3Px3VdURX9e47PmWd8TF0i9ek2s/MPRfeZnbtDav6urF/lw/w9ir7fMP7LuhbDNjc30XWdbxs6QdlmKufSOpzn2IhD1yubngkz0YVxk26J7aMltEGDBymnDtxzlQmwXC6jC9QsGhhejStTJVpe2JwYtTg/7tA3WaJn0qG3oY4xi7ZRm6PDJk/WvfrdN6Ba/Hgty6Qg747Qp3/Olb8XOSmwtpcCnLC5+EaqnLQFLHNR51h9r3XESuALVv2RR6rRFFfuws5v8zhdRhswaR4yOSLSBs0EfG7c5uxrmzOiLlGzVNYk+3GF2kCRa+8cnFD93mVfupsyfKZ2QmYKIdN7NFP4tfb+gL23+YBdvw78CXbNnZgvGS+rAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/1d6213942d4909985abc0084053deb9f/a6d36/image-2.png&quot;
        srcset=&quot;/static/1d6213942d4909985abc0084053deb9f/222b7/image-2.png 163w,
/static/1d6213942d4909985abc0084053deb9f/ff46a/image-2.png 325w,
/static/1d6213942d4909985abc0084053deb9f/a6d36/image-2.png 650w,
/static/1d6213942d4909985abc0084053deb9f/e548f/image-2.png 975w,
/static/1d6213942d4909985abc0084053deb9f/21b4d/image-2.png 1280w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;그렇다면 현실적으로 가장 좋은 방법은 무엇일까요? 저는 바로 &lt;strong&gt;블로깅&lt;/strong&gt;이라고 생각해요. &lt;strong&gt;내가 그날 공부한 개념에 대해 잘 모르는 다른 사람들도 쉽게 이해할 수 있도록 글을 쓰는 그 과정에서 메타인지가 향상&lt;/strong&gt;되는 경험을 많이 할 수 있었습니다. 누군가에게 가르치듯이 설명하고, 시끄럽게 떠드는 학습 방식은 이미 뇌과학적으로 높은 학습효과를 보인다는 것이 이미 증명된 사실이죠. 반면 조용하게 인강과 책을 읽고, 문제를 대충 풀어보는 수동적인 방식은 매우 낮은 학습효과를 보인다는 것 또한 이미 증명된 사실이에요.&lt;/p&gt;
&lt;p&gt;블로깅은 자신의 상황에 알맞게 언제든지 글쓰기를 시작할 수 있습니다. 글을 작성하면서 본인 스스로 남을 위해 설명하듯 글을 작성하다보면, 생각이 &lt;code class=&quot;language-text&quot;&gt;정교화(elaboration)&lt;/code&gt; 되고, 이곳저곳 흩어진 생각을 한 곳으로 모으며 지식을 글로 &lt;code class=&quot;language-text&quot;&gt;설명(explain)&lt;/code&gt; 할 수 있어요. &lt;strong&gt;타인에게 설명을 하듯 글쓰기를 시작하면, 자연스레 지식과 생각들이 정리됩니다.&lt;/strong&gt; 또한 글을 적다가 내가 잘 모르는 부분이 있다면 반드시 설명을 하는데에 막힐 수 밖에 없습니다. 이 과정에서 메타인지가 활성화됩니다. 내가 부족한 부분을 글을 작성하며 정확히 알 수 있게되죠. 그를 보충하기 위한 추가 학습 키워드가 생기며, 필요에 의해 능동적으로 학습하고, 배우고, 고민하고, 보충할 수 있는 기회가 주어집니다.&lt;/p&gt;
&lt;p&gt;또한 블로깅은 &lt;strong&gt;기억의 장기화&lt;/strong&gt;에 매우 큰 효과를 보이기도 해요. 인간은 망각의 동물입니다. 모든것을 기억할 수 없으며, 설령 높은 이해도를 갖고 있던 지식들도 오랜 시간이 지났을 때 잊기 마련이죠. 이때 블로깅은 장점을 보입니다. 그 당시 내가 갖고 있던 정교화된 생각들을 빠르게 상기할 수 있으며, 휘발된 기억을 빠르게 되찾을 수 있거든요. 이렇게 정교화된 생각들을 내가 필요할 때 마다 계속 꺼내볼 수 있다는 점도 매우 큰 장점입니다.&lt;/p&gt;
&lt;p&gt;글을 쓰고 있는 지금 기준으로, 카카오테크 부트캠프 시작 이후에 작성한 블로그 포스트가 110개가 쌓였어요. 약 2일에 한 번꼴로 글을 쓴 것 같아요. 이렇게 글쓰기 습관을 들이면, 나중에 성장이 지체되는 것 같은 불안감이 오더라도 그간 작성한 블로그 포스트를 돌아보며 &lt;strong&gt;&apos;맞아. 그래도 나 이런 것들을 공부했었어. 힘내보자!&apos;&lt;/strong&gt; 라는 생각도 할 수 있습니다. 이렇듯 블로그 포스팅은 학습의 &lt;strong&gt;정량적인 지표&lt;/strong&gt; 가 되기도 합니다.&lt;/p&gt;
&lt;h3 id=&quot;함께-글쓰기를-통해-성장하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%A8%EA%BB%98-%EA%B8%80%EC%93%B0%EA%B8%B0%EB%A5%BC-%ED%86%B5%ED%95%B4-%EC%84%B1%EC%9E%A5%ED%95%98%EA%B8%B0&quot; aria-label=&quot;함께 글쓰기를 통해 성장하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;함께 글쓰기를 통해 성장하기&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cbf811ac8d45ca5fa0aa3cf22952aa20/89587/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.576687116564415%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB4klEQVR42o2Ty27TUBCG8wjE9+Pj+z22KaFBkXBDLrWLFClkBUJCgkXfpjvEkl37BH3DnxkjSyE10MXIPp7x55n/H0+yLEOe5/B9H7Ztw5E2VE2gfefi4cdnHA9zlNUcSRzBcWRf86+YrFYrcKRpCiEEvWRDUQX2rYPHnx/w5VONlxdLLJdv4Lnu/4HcIYPck2JhWQijDHV9AUM3qWtJH3Jg0XPOcT3fjwJ5XM/z+hgecnGcxJiVFYIwJHhE5wRRHCOmGM7cBMOfADnxR4dUFAQBiqJATsFyMCQMI0RRDJ9yIX3IHZFgUpZlP85phwzkFyKCqJoKRVGgqip0XYemaX1e2GJ85LqungD7kWm0/X6Pm67D4XDAbrfDdrtF27awDAH9hQlbjAB5rL91uF6vsdls0HU3aJoGxWyG0I9RNgk2t5dwI0kmnQHnr+pe7HMNHTqXVYWMNGb9EtIxnxXwZYi332p8fegQv3Zh6TS6PAEGtNC81OdA7pg7StPsd9B6JWmCwCczQon40h/XcFiZc8cYymZMp1PoZIRhGL0ppmlCCglLo+tzgYMpx+OxN4TN4L+JDVksFj3YkB4MqpPPAQ6mNFdXuL5uUZGWkv4WXnJDU/D+4y3uvt9jWZXQqHPODcBf+Wxr0f3FVGMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/cbf811ac8d45ca5fa0aa3cf22952aa20/a6d36/image-4.png&quot;
        srcset=&quot;/static/cbf811ac8d45ca5fa0aa3cf22952aa20/222b7/image-4.png 163w,
/static/cbf811ac8d45ca5fa0aa3cf22952aa20/ff46a/image-4.png 325w,
/static/cbf811ac8d45ca5fa0aa3cf22952aa20/a6d36/image-4.png 650w,
/static/cbf811ac8d45ca5fa0aa3cf22952aa20/e548f/image-4.png 975w,
/static/cbf811ac8d45ca5fa0aa3cf22952aa20/3c492/image-4.png 1300w,
/static/cbf811ac8d45ca5fa0aa3cf22952aa20/89587/image-4.png 2624w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;figcaption&gt; 하모니 팀 기술 블로그 &lt;/figcaption&gt;
&lt;p&gt;몇 달 전부터, 블로깅을 저 혼자가 아닌 카테부 크루들과 함께 작성하며 성장할 수 있는 방법도 떠올려봤어요. 팀원 서로가 태스크를 진행하며 학습하거나 경험한 내용을 글로 공유하며 이해도를 높이는 고유 문화를 만들기 위해, &lt;a href=&quot;https://kakaotech-harmony.netlify.app/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 주도적으로 운영하고 있어요. 팀 기술 블로그 플랫폼은 제가 직접 개발한 &lt;a href=&quot;https://github.com/msung99/Gatsby-Starter-Haon&quot;&gt;Gatsby 블로그 오픈소스 프로젝트&lt;/a&gt;를 사용해서 개설했답니다. 팀 기술 블로그내에 작성한 글을 토대로 자체 세미나를 열고, 다른 크루들에게 설명하는 시간을 매주 가지니 더 효과적인 학습이 되는 것 같습니다. &lt;strong&gt;이렇듯 저는, 글쓰기를 기반으로 카테부 오프라인 교육장에서 저만의 색깔과 학습법을 찾아가고 있어요.&lt;/strong&gt; 😁&lt;/p&gt;
&lt;p&gt;카테부에서 찾아낸 학습법이 더 궁금하다면 &lt;a href=&quot;https://haon.blog/about/&quot;&gt;프로그래밍을 공부하며 능동적인 성장을 위한 학습방법&lt;/a&gt; 을 참고해주세요. 블로깅을 통한 프로그래밍 학습 방법, 이 좋은걸 저만 할 수는 없죠! 훗. 😄&lt;/p&gt;
&lt;h2 id=&quot;뜨겁게-토론하고-잡담하고-발표하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9C%A8%EA%B2%81%EA%B2%8C-%ED%86%A0%EB%A1%A0%ED%95%98%EA%B3%A0-%EC%9E%A1%EB%8B%B4%ED%95%98%EA%B3%A0-%EB%B0%9C%ED%91%9C%ED%95%98%EA%B8%B0&quot; aria-label=&quot;뜨겁게 토론하고 잡담하고 발표하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;뜨겁게 토론하고, 잡담하고, 발표하기&lt;/h2&gt;
&lt;p&gt;카테부에 참여하기 전에는, 제가 가지고 있는 생각에 대한 자신감이 많이 없는 편이었습니다. &lt;strong&gt;옛날에는 제가 무지하다는 것을 남에게 들키는게 정말 무서웠고, 타인의 의견과 갈등이 생기는게 정말 싫어서 항상 토론을 회피했거든요.&lt;/strong&gt; 하지만, 카테부에서의 저는 확연히 달라졌습니다. 카테부는 뜨거운 토론, 잡담을 중요시하는데요, &lt;strong&gt;갈등 해결 경험 또한 중요한 소프트 스킬&lt;/strong&gt; 중 하나라고 생각하는 문화가 녹아있습니다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6795ad8fe72babef23c476d4fa67764d/db7ce/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70.5521472392638%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAD9klEQVR42iWSa0zTBxRHuxebBBWQDYExEaY8VKYDIghzWwQC1IFAobRFEQotCBRoEaUUCsJAtMhD3o8WkDfIawKa6eaTZRmabKjolixsi2hmzLJkflmynP2zfTjJ/XBz7i/3XpGjiyvve3kKeOG2bQcePv747g1jZ0AonrsCsXd0xXuPPzWmWgb7O/hicoCZiX4mx3qZHLUwO97HtFBfMLdQW2NE9IqVNetsHdjwtgubXLbi7O6Db1AUUVItkfGZePsGssXTV5AGII6TkKJMQ6XOIDcvB3WWmvgECUEhwbhv9+T1dTaIYiQSdvgH8Y6bF3bOHlhtdMLK1kXAGdEbNrxmbU9WgZbOnk4slh56e820tbfS1NxI7dlaig16oqJj2OjgyHq7TYjMQtRuSzsd5k5qztWjr6gmU2vgkEJDSKSc7R9+gpP7LjZv8WK3MDg8IgpZkpx8TS7FRTpK9UXotBq8du5kva09orQ0OdKUFBQZalT5WpQ5WjKPV3H8zDD6+klOt81Q1TCIvqqFY9pyoTeLyENJhEbG4Ou3FzcPTxwcnXjLZgOvvmmNKDMrFUV6BklKFcnqHHwDD/CeTxCVA0s0jtzj5qVbLIwtMDU4zdz4HFdmr3JxdIbsPC0nik9Soi+gzJBHaUkuqvRERCp1CnKlUpBlkpZTwJ7gMDw+2I958SVTS3+z+u2PPFn6mbXvfuLZ4n1ePFzj5tRVbDe/y+69/sTGhlJ3rpj5+TZ6OzX/C5NShZSCME6ahDg8gk8/U9B+409G7vzFyu0HPBZYubXMo9vLPF3+lctj8/j47SNMHIlCHseF/lPMXWmlpTlZEKqOIEs9giQ5GTuXbbhu9eZAlISu638w/M1LVhZXeHTnoSC+z/LN7wXhb8wLQhd3TyIOiinQpNNkyqGjuxK94TCijHQ5iqPJxMtkyNKVqAsLycrPo3HhF8bv/sPqvVWe/LDG85XfefbgKS8eP2fEPIqzmzviGDFFugwUiR9TkCdFp5MJQmUSssNyYqWJVJg+p6m/R7hoBUbL19SP3mW4Z5zzdV2UG8+QqzlBavoxYhOkiKMPkp2txFRzgmGLkUsTVf8hUisTkcgSCA4LR2vUc7b9PIbKCqITZPiFhPFRWLSQJAGZPAVNlobqkjLqjEZqS3ScLdPQXV/ITH8Z80MVTAxUCjtMjWd/eDjeAfvIM5zE1NFMcXkpJeXVnDF10NncR2+LhbGOLqa7GrjcV8tXIzUsDJ7ixqSJL8fruDRsEl6qnouDJkQG3WGqDUeRJopJy1ZRVGEg/3g+La09DJkFWquY6Cpjts/ItdHTXB1tYGGojZmhTubGerg228f1OTOzIw10txj5F0tklIapka9tAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/6795ad8fe72babef23c476d4fa67764d/a6d36/image-3.png&quot;
        srcset=&quot;/static/6795ad8fe72babef23c476d4fa67764d/222b7/image-3.png 163w,
/static/6795ad8fe72babef23c476d4fa67764d/ff46a/image-3.png 325w,
/static/6795ad8fe72babef23c476d4fa67764d/a6d36/image-3.png 650w,
/static/6795ad8fe72babef23c476d4fa67764d/e548f/image-3.png 975w,
/static/6795ad8fe72babef23c476d4fa67764d/3c492/image-3.png 1300w,
/static/6795ad8fe72babef23c476d4fa67764d/db7ce/image-3.png 2404w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;figcaption&gt; 카카오테크 부트캠프, 매주 오후 2시에 진행되는 Tech Talk 기술 발표 세미나 참여 &lt;/figcaption&gt;
&lt;p&gt;동일한 학습 주제, 미션, 프로젝트 기능 구현을 해결하는데에 뜨거운 잡담을 통해 다양한 시야가 확장되는 것을 자주 경험했습니다. 혼자서 공부하고, 프로그래밍 하다 보면 자신만의 틀에 박힌 생각에 갇히게 되는 경우가 많거든요. 하지만 &lt;strong&gt;서로 다른 의견을 가지고 있는 사람과 뜨겁게 의견을 주고받다보면 전혀 생각치 못한 획기적인 방법을 깨닫고, 다양한 시야가 확장된다&lt;/strong&gt;는 것을 알 수 있었습니다. 더 빠르게 성장하는 학습 방법은 &lt;strong&gt;더 능동적으로 최대한 많이 토론하고, 잡담하는 것입니다.&lt;/strong&gt; 저희 크루들은 서로가 다른 의견을 가지고 있을 때 그 자리를 회피하기보다는 오히려 의견 교환에 적극 참여하는 경우가 대부분입니다. &lt;strong&gt;저 또한 갈등이 싫어 토론과 잡담을 항상 회피하는 사람이었으나, 지금은 오히려 다양한 의견 교환을 위해 여러 스터디, 기술 발표 세미나에 적극 참여중입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;한 예로, 이번주에 &lt;strong&gt;10분 카카오 Tech Talk 기술 발표 세미나&lt;/strong&gt; 에 참여해 열심히 발표를 했던 기억이 남네요. 일명, 카부톡 발표 세미나는 매주 월~수요일 오후 2시에 각 과정별로 크루들이 기술적 경험을 공유하는 발표 세미나입니다. 제가 알고있는 지식을 발표 세미나에서 공유하고, 생각치 못했던 질문들과 토론을 주고받으면서 함께 성장할 수 있는 세미나입니다.&lt;/p&gt;
&lt;p&gt;이번 주차 발표자로 참여하면서, 이 당시 또한 하지만 서로 다른 의견을 가지고 있는 사람과 의견을 주고받으며 자연스럽게 시야가 확장되는 것을 경험했습니다. 책 &lt;strong&gt;&apos;함께 자라기&apos;&lt;/strong&gt; 에서 강조하는 함께 성장하기가 이제는 무엇인지 조금은 알 것 같아요. 맥락은 조금 다르겠지만, 카테부에서 오프라인으로 다른 사람들과 물리적으로 모여있는 공간에서 얻을 수 있는 가장 좋은 점이 이렇게 뜨겁게 토론하고, 잡담하며 함께 성장할 수 있는 것이 아닐까 싶습니다. (카테부 다시 한 번 땡큐 😉)&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;카카오테브 부트캠프 교육과정은 타인의 생각을 수동적으로 받아들이던 과거의 나로부터 탈피하여, 나만의 생각을 만들어가는 과정이라는 생각이 많이 듭니다. 제일 중요한 것은 타인의 생각을 맹목적으로 따르지 않기인 것 같습니다. 나만의 색깔과 개성, 학습법을 찾아가게 해준 카테부 모든 분들에게 항상 감사할 따름입니다. 다른 모든 분들도 동의되지 않은 권위에 굴복하지 않기를 바라며, 글을 마치겠습니다 😁&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Grafana, Prometheus, SpringBoot Actuator 기반 모니터링 환경 구축하기]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/infra/monitoring/grafana-prometheus/</link><guid isPermaLink="false">https://haon.site/haon/infra/monitoring/grafana-prometheus/</guid><pubDate>Wed, 27 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;최근에 서비스를 개발하면서 클라우드 팀과 모니터링을 논의할 때, 스프링에서 메트릭 수집방법과 데시보드 구성 방식을 잘 몰라서 어려움을 겪었다. 간단한 실습을 해보며, 전반 흐름에 대해서 얇게 이해해보도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;모너티링-구축-배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A8%EB%84%88%ED%8B%B0%EB%A7%81-%EA%B5%AC%EC%B6%95-%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;모너티링 구축 배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;모너티링 구축 배경&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/da83ebcc-e772-4e81-9f47-01b5eecf6499/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;micrometer-추상화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#micrometer-%EC%B6%94%EC%83%81%ED%99%94&quot; aria-label=&quot;micrometer 추상화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Micrometer 추상화&lt;/h2&gt;
&lt;p&gt;사실 모너터링 툴은 &lt;code class=&quot;language-text&quot;&gt;Prometheus&lt;/code&gt; 외에도 굉장히 다양합니다. 저도 지금 직접 모니터링 환경을 구축하면서 느끼듯이, 모든 개발자 및 인프라팀은 상황 및 기호에 따라 다양한 모너터링 툴을 변경해야는 상황이 발생하는데, &lt;code class=&quot;language-text&quot;&gt;마이크로미터&lt;/code&gt; 가 &lt;code class=&quot;language-text&quot;&gt;추상화&lt;/code&gt;를 제공하여 다양한 모너터링 구현체를 지원한다. 개발자는 마이크로미터가 정한 표준 방법으로 &lt;code class=&quot;language-text&quot;&gt;메트릭(측정 지표)&lt;/code&gt; 를 전달하고, 사용하는 모너터링 툴에 알맞는 구현체 (여기선 프로메테우스) 를 선택하면 된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;메트릭metric-수집-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%94%ED%8A%B8%EB%A6%ADmetric-%EC%88%98%EC%A7%91-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;메트릭metric 수집 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메트릭(Metric) 수집 아키텍처&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1ea9e7c3-aca0-4ed2-9c35-1b475fd52267/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Spring Actuator 는 마이크로미터가 수집하는 CPU, JVM, 커넥션등의 수많은 &lt;code class=&quot;language-text&quot;&gt;메트릭(지표)&lt;/code&gt; 를 편리하게 사용할 수 있는 기능을 제공해준다. 지금부터 알아볼 Actuaor, Grafana, Prometheus 의 기능을 요약해보면 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spring Actuator : 메트릭 정보를 쉽게 수집할 수 있게 정보를 제공&lt;/li&gt;
&lt;li&gt;Prometheus : 마이크로미터로 부터 주기적으로 메트릭 정보를 PULL 받아서 저장&lt;/li&gt;
&lt;li&gt;Grafana : 프로메테우스가 수집한 정보를 활용하여 사용자에게 보기좋은 정보를 제공해주는 DashBoard&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SpringBoot Actuator 와 마이크로부터로 부터 메트릭 정보가 자동으로 생성되며, 프로메테우스 구현체가 읽을 수 있는 메트릭 포맷으로 변환된다. 그러면 프로메테우스는 일정 주기로 메트릭을 주기적으로 수집해서, 수집한 메트릭을 내부 DB 에 저장한다. 그러면 그라파냐 데시보드를 통해 프로메테우스에 수집된 메트릭 수집정보를 시각자료로써 손쉽게 조회 가능하다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;spring-actuator&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#spring-actuator&quot; aria-label=&quot;spring actuator permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Spring Actuator&lt;/h2&gt;
&lt;p&gt;그럼 지금부터 메트릭 수집 환경을 구축해봅시다. 우선 엑츄에이터의 의존성을 추가해주자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;implementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;actuator&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;actuator-엔드포인트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#actuator-%EC%97%94%EB%93%9C%ED%8F%AC%EC%9D%B8%ED%8A%B8&quot; aria-label=&quot;actuator 엔드포인트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Actuator 엔드포인트&lt;/h3&gt;
&lt;p&gt;라이브러를 추가한뒤 서버를 실행시키고 &lt;code class=&quot;language-text&quot;&gt;/actuator&lt;/code&gt; 로 접속해보면 Actuator 가 제공하고 있는 엔드포인트 목록들을 확인할 수 있다. 엔드포인트란 쉽게말해 Actuator 를 통헤 애플리케이션의 내부 정보를 외부에 유출시키는 것을 의미한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;_links&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;self&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:8080/actuator&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;templated&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;health&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:8080/actuator/health&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;templated&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;health-path&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:8080/actuator/health/{*path}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;templated&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;마이크로미터-프로메테우스-구현체&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EB%AF%B8%ED%84%B0-%ED%94%84%EB%A1%9C%EB%A9%94%ED%85%8C%EC%9A%B0%EC%8A%A4-%EA%B5%AC%ED%98%84%EC%B2%B4&quot; aria-label=&quot;마이크로미터 프로메테우스 구현체 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마이크로미터 프로메테우스 구현체&lt;/h3&gt;
&lt;p&gt;위와 같이 Actuator 가 기본적으로 유출시키는 엔드포인트에 이어서, Prometheus에서 사용되는 메트릭에 대한 엔드포인트도 외부에 유출 시켜야한다. 이를위해 우선 Prometheus 에 대한 의존성이 필요하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;runtimeOnly &apos;io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;micrometer&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;micrometer&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;registry&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;prometheus&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 의존성은 애플리케이션에 &lt;code class=&quot;language-text&quot;&gt;마이크로미터 프로메테우스 구현체&lt;/code&gt; 라이브러를 추가하는 것이다. 프로메테우스는 JSON 형식의 메트릭 데이터를 이해할 수 없다. 따라서 프로메테우스가 이해할 수 있는 메트릭 포맷으로 변경해야하는데, 이 때문에 마이크로미터를 사용해서 프로메테우스가 이해할 수 있는 포맷으로 손쉽게 변형 가능하도록 위 구현체를 활용하는 것이다.&lt;/p&gt;
&lt;h3 id=&quot;prometheus-엔드포인트-유출&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#prometheus-%EC%97%94%EB%93%9C%ED%8F%AC%EC%9D%B8%ED%8A%B8-%EC%9C%A0%EC%B6%9C&quot; aria-label=&quot;prometheus 엔드포인트 유출 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Prometheus 엔드포인트 유출&lt;/h3&gt;
&lt;p&gt;이어서, 아래와 같이 application.yml 파일에 설정을 추가해서 외부에 엔드포인트를 유출시킨다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;management:
  endpoint:
    &lt;span class=&quot;token keyword&quot;&gt;shutdown&lt;/span&gt;:
      enabled: &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  endpoints:
    web:
      exposure:
        include: prometheus&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;정상적으로 외부에 유출시켰다면 아래와 같은 결과를 조회할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token string&quot;&gt;&quot;prometheus&quot;&lt;/span&gt;: {
  &lt;span class=&quot;token string&quot;&gt;&quot;href&quot;&lt;/span&gt;: &lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:8080/actuator/prometheus&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;templated&quot;&lt;/span&gt;: &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;prometheus&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#prometheus&quot; aria-label=&quot;prometheus permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Prometheus&lt;/h2&gt;
&lt;p&gt;앞선 과정을 통해 actuator 엔트포인트로 프로메테우스가 이해할 수 있는 metric 을 생성하고 있으니, 이제 &lt;strong&gt;프로메테우스가 해당 metric 을 주기적으로 수집할 수 있도록 설정 해보겠습니다.&lt;/strong&gt; &quot;prometheus.yaml&quot; 파일을 생성하고 아래와 같이 내용을 구성해주자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;global&lt;/span&gt;:
  scrape_interval: &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;m   &lt;span class=&quot;token comment&quot;&gt;// 기본값:1m (1분)&lt;/span&gt;

scrape_configs:
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; job_name: prometheus
    static_configs:
      &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; targets: &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;http://localhost:8080&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 또는 &apos;host.docker.internal:8080&apos;&lt;/span&gt;
        metric_path: &lt;span class=&quot;token string&quot;&gt;&apos;/actuator/prometheus&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;scrape_interval&lt;/code&gt; 은 메트릭을 수집할 주기를 명시하는 것으로 위에서는 10초의 주기로 메트릭을 수집하도록 설정했다. &lt;code class=&quot;language-text&quot;&gt;job_name&lt;/code&gt; 은 말그대로 job 이름으로 임의의 이름을 사용했으며, &lt;code class=&quot;language-text&quot;&gt;metric_path&lt;/code&gt; 는 메트릭을 수집할 경로로써, 앞서 유출시켰던 엔트포인트를 명시해주었다. &lt;code class=&quot;language-text&quot;&gt;targets&lt;/code&gt; 은 메트릭을 수집한 애플리케이션 경로를 명시하는 것이다.&lt;/p&gt;
&lt;p&gt;이때 저는 &lt;code class=&quot;language-text&quot;&gt;host.docker.internal&lt;/code&gt;를 사용하여 도커 인스턴스 내부에서 호스트의 포트에 접속해보자. 로컬이 아닌 환경이라면 이에 맞게 입력하며 된다.&lt;/p&gt;
&lt;h3 id=&quot;도커를-이용한-prometheus-설치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%BB%A4%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-prometheus-%EC%84%A4%EC%B9%98&quot; aria-label=&quot;도커를 이용한 prometheus 설치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도커를 이용한 Prometheus 설치&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ docker pull prom&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;prometheus

docker run \
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token number&quot;&gt;9090&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9090&lt;/span&gt; \
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;v &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;상위경로&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;prometheus&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yml&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;prometheus&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;prometheus&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yml \
    prom&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;prometheus&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;{%상위 경로%}에는 위에서 입력한 prometheus.yml의 상위 경로를 작성해준다. 이로써 도커 기반의 프로세스 실행에 성공하면 &lt;code class=&quot;language-text&quot;&gt;9090 포트&lt;/code&gt; 로 접속해보자. 그러면 아래와 같이 프로메테우스 대시보드를 조회할 수 있게된다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/9c747b61-9258-4231-9737-33838e7884fb/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;프로메테우스가 수집한 여러 메트릭 중 하나를 확인해보자. 예를들어 &lt;code class=&quot;language-text&quot;&gt;http_server_requests_seconds_max&lt;/code&gt; 메트릭을 입력하면 아래와 같이 Graph로 시각화하여 메트릭을 조회가 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c2ed85bf-ce21-4f25-ae35-48406ce9d69d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;grafana&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#grafana&quot; aria-label=&quot;grafana permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Grafana&lt;/h2&gt;
&lt;p&gt;앞서 말했듯이, 프로메테우스가 메트릭에 대한 시각화를 제공하지만 그라파나를 이용하여 더욱 강력한 메트릭 시각화 할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ docker pull grafana&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;grafana
$ docker run &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;name grafana &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;d &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt; grafana&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;grafana&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;docker run을 성공적으로 실행하면 &lt;code class=&quot;language-text&quot;&gt;3000번 포트로&lt;/code&gt; 로 그라파나에 접속할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/fc3550a3-5474-4245-bff3-74cb8454cf8c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Grafana에 접속하면, 위와 같은 로그인 페이지에 접속됩니다. 초기 계정은 admin, admin 이고, 처음 로그인하면 비밀번호를 변경하는 창이 나온다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0f96c89c-b662-4ddd-9e2d-f343402042cb/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;데이터소스 추가를 위해 그라파나 첫 페이지에서 &lt;code class=&quot;language-text&quot;&gt;DATA SOURCE&lt;/code&gt;를 클릭해줍시다. 여기서 앞서 구축했던 프로메테우스를 연동하는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1e5c42cb-4b76-4469-a331-70009d09e462/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Data Source&lt;/code&gt; 이름을 지정해주고, URL에 &lt;code class=&quot;language-text&quot;&gt;http://host.docker.internal:9090/&lt;/code&gt; 를 입력해주자. (또는 &lt;code class=&quot;language-text&quot;&gt;http://localhost:9090&lt;/code&gt; 을 입력) Data Source 설정에 성공할 경우 성공을 알리는 녹색 상태창이 나오게 된다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/955afa0e-2ba4-482d-8ea7-fd5c82286d72/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이제 최종적으로 &lt;code class=&quot;language-text&quot;&gt;Import&lt;/code&gt; 를 클릭하여 대시보드를 생성 해보자. 스프링부트 메트릭을 보여주는 유명한 대시보드로 &lt;code class=&quot;language-text&quot;&gt;JVM dashboard&lt;/code&gt; 가 있습니다. 몰론 우리가 직접 데시보드를 구성하는 것도 가능하지만, 지금은 유명한 프리셋을 사용한다. 다양한 대시보드는 Grafana Dashboards에서 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e4d6f4eb-dfdc-4331-8d47-0fad21828082/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Import via grafana.com&lt;/code&gt; 에다 &lt;code class=&quot;language-text&quot;&gt;h‘https://grafana.com/grafana/dashboards/4701-jvm-micrometer/’ &lt;/code&gt; 를 넣어주고 &lt;code class=&quot;language-text&quot;&gt;Load&lt;/code&gt; 를 클릭합시다. 그러고 마지막으로 &lt;code class=&quot;language-text&quot;&gt;Import&lt;/code&gt; 를 클릭하면 아래와 같은 깔끔하고 멋진 데시보드가 조회되는 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;grafana-dashboard&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#grafana-dashboard&quot; aria-label=&quot;grafana dashboard permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Grafana Dashboard&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a09ed09c-8ee7-4cfb-859c-874af9781758/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이렇게 멋진 그라파나 대시보드를 만들어보았다. 스크린샷보다 더 많은 메트릭이 있으므로 스크롤하여 확인해봐도 좋습니다. 기본 범위는 24시간으로 설정되어있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/spring-boot-actuator-prometheus-grafana-set-up/&quot;&gt;https://hudi.blog/spring-boot-actuator-prometheus-grafana-set-up/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://covenant.tistory.com/244&quot;&gt;https://covenant.tistory.com/244&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://inma.tistory.com/163&quot;&gt;https://inma.tistory.com/163&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hays99.tistory.com/278&quot;&gt;https://hays99.tistory.com/278&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://meetup.nhncloud.com/posts/237&quot;&gt;https://meetup.nhncloud.com/posts/237&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lollaziest.tistory.com/186&quot;&gt;https://lollaziest.tistory.com/186&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devnata.tistory.com/147&quot;&gt;https://devnata.tistory.com/147&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@moey920/%EC%95%88%EC%A0%95%EC%A0%81%EC%9D%B8-%EC%9A%B4%EC%98%81%EC%9D%84-%EC%99%84%EC%84%B1%ED%95%98%EB%8A%94-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81.-%ED%94%84%EB%A1%9C%EB%A9%94%ED%85%8C%EC%9A%B0%EC%8A%A4%EC%99%80-%EA%B7%B8%EB%9D%BC%ED%8C%8C%EB%82%98&quot;&gt;https://velog.io/@moey920/안정적인-운영을-완성하는-모니터링.-프로메테우스와-그라파나&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[어노테이션 기반 DI 컨테이너 프레임워크 구현하기]]></title><description><![CDATA[본 포스팅에서 실제 구현한 실습 구현 코드는 DI 컨테이너 구현하기 PR 에서 확인할 수 있다. 왜 DI 가 필요한가? DI…]]></description><link>https://haon.site/haon/spring/di-container-implementation/</link><guid isPermaLink="false">https://haon.site/haon/spring/di-container-implementation/</guid><pubDate>Sun, 24 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;본 포스팅에서 실제 구현한 실습 구현 코드는 &lt;a href=&quot;https://github.com/Durable-developers/di-framework/pull/1&quot;&gt;DI 컨테이너 구현하기 PR&lt;/a&gt; 에서 확인할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;왜-di-가-필요한가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-di-%EA%B0%80-%ED%95%84%EC%9A%94%ED%95%9C%EA%B0%80&quot; aria-label=&quot;왜 di 가 필요한가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 DI 가 필요한가?&lt;/h2&gt;
&lt;p&gt;DI 의 필요성에 대해 정리하기 전에 먼저 &lt;code class=&quot;language-text&quot;&gt;의존관계(Dependency)&lt;/code&gt; 에 대해 정리해볼 필요가 있다. 의존관계란 한 객체에서 모든 일을 처리하기 힘들기 때문에, 무게가 무거워지기 때문에 내가 해야할 일을 다른 객체에게 위임하면서 발생한다.&lt;/p&gt;
&lt;p&gt;가령 레이어드 아키텍처에서 UserController 가 모든 회원가입 로직을 담당할 수 있음에도 UserService, UserRepository 객체를 필요로 하고, 역할과 책임을 떠넘기게 된다. 이때 의존관계가 발생하는 것이다. &lt;strong&gt;내가 가지고 있는 역할과 책임을 다른 객체에게 위임하는 순간 의존성이 발생하는 것이다.&lt;/strong&gt; 지금껏 스프링부트 프레임워크를 사용할 때 DI, 싱글톤에 대한 내용은 수 없이 들어왔다. 만약 DI 컨테이너 개념이 도입되지 않았다면, 의존관계에 있는 객체를 사용하기 위해 객체를 매번 new 키워드로 직접 생성하고 사용하는 방식으로 구현했을 것이다.&lt;/p&gt;
&lt;p&gt;이번 포스트에선 실습 수행 구현 코드를 어떻게 구현했는지 단순히 나열하지 않고, 왜 DI 컨테이너가 필요하고 어떤 방식으로 발전되었는지를 상세히 설명하는 것을 목표로 한다.&lt;/p&gt;
&lt;h3 id=&quot;di-란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#di-%EB%9E%80&quot; aria-label=&quot;di 란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DI 란?&lt;/h3&gt;
&lt;p&gt;DI 하면 가장 먼저 떠오르는 디자인 패턴이 &lt;code class=&quot;language-text&quot;&gt;싱글톤(SingleTon)&lt;/code&gt; 임을 스프링부트 프레임워크를 한 번이라도 공부해봤다면 알 것이다. 싱글톤으로 구현되지 않은 프레임워크라면 가령 Service 레이어 오브젝트에서 Dao, Repository 객체를 사용하기 위해 객체를 매번 직접 생성해야한다. 하지만 이 방식은 유연한 개발, 확장성을 제한하기 때문에, &lt;strong&gt;인스턴스를 생성하는 책임과 사용하는 책임을 분리하자는 것이 DI 의 핵심이자 등장 이유이기도 하다.&lt;/strong&gt; 즉, Service 는 레포지토리에 대한 인스턴스를 생성하는 책임을 없애고, 단순히 사용만 함으로써 유연성을 높이는 것이 DI 접근 방식이다.&lt;/p&gt;
&lt;p&gt;정리하자면, DI 란 객체간의 의존관계에 대한 결정권을, 의존관계를 가지는 객체가 가지는 것이 아니라 외부의 제 3자가 담당하도록 역할을 떠넘김으로써 더 유연한 구조로 개발하는 기법이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step1-static-키워드-기반-싱글톤-컨테이너&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step1-static-%ED%82%A4%EC%9B%8C%EB%93%9C-%EA%B8%B0%EB%B0%98-%EC%8B%B1%EA%B8%80%ED%86%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88&quot; aria-label=&quot;step1 static 키워드 기반 싱글톤 컨테이너 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;step1. static 키워드 기반 싱글톤 컨테이너&lt;/h2&gt;
&lt;h3 id=&quot;static-기반의-안티패턴-컨테이너&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#static-%EA%B8%B0%EB%B0%98%EC%9D%98-%EC%95%88%ED%8B%B0%ED%8C%A8%ED%84%B4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88&quot; aria-label=&quot;static 기반의 안티패턴 컨테이너 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;static 기반의 안티패턴 컨테이너&lt;/h3&gt;
&lt;p&gt;한편 &lt;a href=&quot;https://haon.blog/java/singleton/&quot;&gt;자바의 싱글톤(SingleTon) 패턴 구현 방법 6가지, Bill Pugh Solution&lt;/a&gt; 에서도 설명했듯이 싱글톤은 안티패턴이라고도 불리는 패턴이다. 객체지향적이지 못하다는 의견이 분분하다. 이때 말하는 싱글톤은 &lt;code class=&quot;language-text&quot;&gt;static&lt;/code&gt; 키워드를 기반으로 구현한 경우에 해당한다.&lt;/p&gt;
&lt;p&gt;static 키워드를 사용하면 변화에 유연하게 대응하기 힘들다. static 메소드를 사용하는 부분에서 요구사항이 변경된다면 모든 코드도 변경되어야 한다. 즉 static 키워드에 기반하여 컨테이너를 구현할 경우 DIP, OCP 등의 원칙을 위반하게 된다.&lt;/p&gt;
&lt;p&gt;static 기반의 싱글톤 컨테이너 구현은 아래와 같은 단점이 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;(1) 테스트의 어려움 : 싱글톤 패턴으로 구현된 클래스와 의존관계를 가지는 경우 해당 클래스와 강한 의존관계를 가지기 때문에 테스트하기 어렵다.&lt;/li&gt;
&lt;li&gt;(2) 상속 불가능 : getInstance( ) 와 같은 메소드로 정적 팩토리 메소드를 만들고 생성자를 Private 으로 감추기 떄문에, 싱글톤으로 구현된 클래스를 상속할 수 없다.&lt;/li&gt;
&lt;li&gt;(3) OCP, DIP 위반 : 유연한 확장이 불가능하디. 클래스간의 강한 의존관계가 발생하여, 한 객체의 변화가 발생할 경우 다른 의존 객체에서도 변화가 발생한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;예를들어 아래와 같은 UserController, UserService, UserDao 가 있다고 해보자. 둘 다 static 기반의 싱글톤으로 구현된 클래스다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserController&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AbstractConntoller&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; userDao &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; userDao&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userDao &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userDao&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; userDao&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userService &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      userService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userDao&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; userDao&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt; jdbcTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userDao &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            userDao &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; userDao&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;INSERT INTO USERS VALUES (?, ?, ?, ?)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        jdbcTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같은 코드는 테스트하기 쉬울까? 위 코드의 모든 로직들이 원활히 수행되는지 알기위해 테스트 코드를 작성하는 것을 가정해보자. 이때 나는 데이터베이스에 의존하지 않고 테스트를 진행하고 싶다. 즉, 데이터베이스와의 의존관계 없이 독립적으로 테스트를 진행하고 싶다.&lt;/p&gt;
&lt;p&gt;하자만 막상 UserDao 코드를 보면 데이터베이스와의 의존관계가 발생하고 있다. 따라서 UserDao 가 데이터베이스와 의존관계를 가지지 않도록 코드를 변경해야지 데이터베이스에 의존하지 않는 테스트가 가능하다. 이를위해 코드를 어떻게 수정해야할까?&lt;/p&gt;
&lt;h3 id=&quot;고민1-userdao-를-상속한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%A0%EB%AF%BC1-userdao-%EB%A5%BC-%EC%83%81%EC%86%8D%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;고민1 userdao 를 상속한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;고민1. UserDao 를 상속한다.&lt;/h3&gt;
&lt;p&gt;UserDao 를 상속한 클래스를 하나 만들어서 메소드를 오버라이드 하면 될 것이다. 하지만 UserDao 에 대한 생성자를 싱글톤 패턴을 적용하면서 private 으로 구현했기 때문에 상속이 불가능하다.&lt;/p&gt;
&lt;h3 id=&quot;고민2-생성자를-protected-로-변경한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%A0%EB%AF%BC2-%EC%83%9D%EC%84%B1%EC%9E%90%EB%A5%BC-protected-%EB%A1%9C-%EB%B3%80%EA%B2%BD%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;고민2 생성자를 protected 로 변경한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;고민2. 생성자를 protected 로 변경한다.&lt;/h3&gt;
&lt;p&gt;그렇다면 private 메소드를 protected 로 변경하는 것을 생각해볼 수 있다. 하지만 이는 싱글톤 패턴 구현 방식에 어긋난다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step2-인터페이스를-통한-유연한-확장으로-개선&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step2-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%9C%A0%EC%97%B0%ED%95%9C-%ED%99%95%EC%9E%A5%EC%9C%BC%EB%A1%9C-%EA%B0%9C%EC%84%A0&quot; aria-label=&quot;step2 인터페이스를 통한 유연한 확장으로 개선 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;step2. 인터페이스를 통한 유연한 확장으로 개선&lt;/h2&gt;
&lt;p&gt;위 고민1, 고민2 으로는 싱글톤 구현, 유연한 확장에 제한이 있다. 이를 해결하기 위한 해결안은 인터페이스에 있다.&lt;/p&gt;
&lt;h3 id=&quot;고민3-mocking모킹-을-통한-의존성-해소&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%A0%EB%AF%BC3-mocking%EB%AA%A8%ED%82%B9-%EC%9D%84-%ED%86%B5%ED%95%9C-%EC%9D%98%EC%A1%B4%EC%84%B1-%ED%95%B4%EC%86%8C&quot; aria-label=&quot;고민3 mocking모킹 을 통한 의존성 해소 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;고민3. Mocking(모킹) 을 통한 의존성 해소&lt;/h3&gt;
&lt;p&gt;객체 간의 의존관계에 대한 강한 의존성을 줄이기 위해 인터페이스를 적용해보자. 기존 UserDao 에 대한 추상화 타입인 인터페이스를 하나 적용하면 유연한 확장이 가능해질 것으로 기대된다.&lt;/p&gt;
&lt;p&gt;이에 대한 개선된 코드는 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MockUserDao&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; users &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Maps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newHashMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// In-Memory DataBase&lt;/span&gt;

  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    users&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;기존 UserDao 클래스 이름을 JdbcUserDao 로 변경하고, 이에 대한 추상화 인터페이스를 UserDao 로 구현했다. 또한 테스트 작성을 위한 Mock 객체를 위처럼 MockUserDao 로 구현할 수 있다. MockUserDao 는 Map 을 활용한 인메모리 데이터베이스를 활용하기 때문에, 실제 데이터베이스와 의존성이 발생하지 않았다.&lt;/p&gt;
&lt;p&gt;이로써 데이터베이스와 의존관계를 가지지 않음을 확신을 가지며 아래 테스트 코드를 수행한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDaoTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MockUserDao&lt;/span&gt; userDao&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Before&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    userDao &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    userService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userDao&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;deleteUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    userDao&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    userService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deleteUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;예상처럼 테스트 코드는 원활히 수행될 것이다. 그렇다면 위처럼 인터페이스 기반 테스트 코드는 앞서 언급한 3가지 단점을 보완했을까? 우선 &lt;code class=&quot;language-text&quot;&gt;(2) 상속의 어려움&lt;/code&gt; 을 먼저 생각해본다면, 이는 다행히 해결되었다고 볼 수 있을것이다. Dao 테스트 작성을 위해 Dao 를 상속한 메소드가 필요했는데, 이를 대체할 모킹(Mocking) 클래스를 생성했기에 가능하다.&lt;/p&gt;
&lt;p&gt;하지만 &lt;code class=&quot;language-text&quot;&gt;(1) 테스트의 어려움&lt;/code&gt; 이 온전히 해결되었다고 볼 수 있을까? 아직 모호하다. 테스트를 위해 Mock 객체를 매번 구현해야 하는 것은 꽤 많은 비용이 들고 번거로운 작업이다. &lt;strong&gt;즉, 순수히 싱글톤 패턴으로 구현하면 테스트에 어려움이 발생한다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step3-싱글톤-패턴을-제거한-di&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step3-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4%EC%9D%84-%EC%A0%9C%EA%B1%B0%ED%95%9C-di&quot; aria-label=&quot;step3 싱글톤 패턴을 제거한 di permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;step3. 싱글톤 패턴을 제거한 DI&lt;/h2&gt;
&lt;p&gt;위처럼 싱글톤 패턴으로 구현할 경우 많은 문제점을 결코 해결할 수 없음을 살펴봤다. 싱글톤 패턴을 활용하는 경우 싱글톤 패턴으로 구현된 클래스와 의존관계를 가지는 경우 해당 클래스와 강한 의존관계를 가지기 떄문에 테스트하기 어렵다.&lt;/p&gt;
&lt;p&gt;또한 어떤 형태로던 억지로 테스트가 가능하겠지만, 위처럼 매번 Mock 객체를 구현해야 하는 것은 많은 비용이 발생한다. 몰론 Junit 차원에서 &lt;code class=&quot;language-text&quot;&gt;Mockito&lt;/code&gt; 를 제공하긴 하지만, 매번 모킹을 활용한다는 그 자체의 행위는 좋아보이지 않는다. 모킹은 구현체의 내부 구현에 대해 잘 알아얄하며, 내부 구현 변경에 민감하다는 점에서 꼭 필요한 경우를 제외하곤 사용을 지양하는 것이 좋을 것이다.&lt;/p&gt;
&lt;h3 id=&quot;legacyhandlermapping-에-di-적용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#legacyhandlermapping-%EC%97%90-di-%EC%A0%81%EC%9A%A9&quot; aria-label=&quot;legacyhandlermapping 에 di 적용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LegacyHandlerMapping 에 DI 적용&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;HandlerMapping&lt;/code&gt; 에 대한 구현 코드는 &lt;a href=&quot;https://haon.blog/spring/annotation-mvc-framework/&quot;&gt;어노테이션 기반 Spring MVC 프레임워크 구현하기&lt;/a&gt; 를 참고하자 🙂&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;그렇다면 싱글톤 패턴을 적용하지 않는다면 어떻게 인스턴스 하나만을 생성해 인스턴스를 재사용할 수 있을까? 이는 기존 &lt;code class=&quot;language-text&quot;&gt;HandlerMapping&lt;/code&gt; 을 리팩토링 함으로써 구현할 수 있다. 기존에 HandlerMapping 인터페이스의 구현 클래스인 &lt;code class=&quot;language-text&quot;&gt;LegacyHandlerMapping&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;AnnotationHandlerMapping&lt;/code&gt; 내부에선 컨트롤러에 대해 싱글톤 패턴을 적용하지 않고 싱글톤과 동일한 효과를 낼 수 있었다. (각 컨트롤러에 대한 인스턴스를 1개만 존재하도록 유일성을 보장할 수 있었으니.)&lt;/p&gt;
&lt;p&gt;이것이 가능한 이유는 서블릿 컨테이너가 DispatcherServlet 을 초기화하는 시점에 컨트롤러 인스턴스를 생성한 후 재사용 가능하도록 구현했기 떄문이다. 이와 동일한 방식으로 인스턴스를 관리한다면 굳이 싱글톤 패턴을 적용하지 않으면서 인스턴스 하나를 재사용할 수 있게된다.&lt;/p&gt;
&lt;h3 id=&quot;싱글톤-제거-및-리팩토링-코드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%B1%EA%B8%80%ED%86%A4-%EC%A0%9C%EA%B1%B0-%EB%B0%8F-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%BD%94%EB%93%9C&quot; aria-label=&quot;싱글톤 제거 및 리팩토링 코드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;싱글톤 제거 및 리팩토링 코드&lt;/h3&gt;
&lt;p&gt;UserService 에 대한 싱글톤 패턴을 제거해보자. 싱글톤 패턴을 위한 코드를 모두 제거하고 생성자를 public 으로 전환한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; userDao&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;USerDao&lt;/span&gt; userDao&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userDao &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userDao&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;다음은 UserController 에 대해 싱글톤을 제거했다. 아래처럼 getInstance 를 제거하고, 생성자를 public 으로 전환했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserController&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AbstractController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;마지막으로 각 컨트롤러를 생성할 때 UserService, UserDao 를 DI 로, 즉 HandlerMpaping 으로 전달하도록 수정한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LegacytHandlerMapping&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerMapping&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; mappings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;initMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; userDao &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcUserDao&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userDao&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    mappings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/user/signup&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignUpController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    mappings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/user/login&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoginController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이로써 싱글톤 패턴을 사용하지 않으면서 인스턴스 하나를 사용하도록 했다. 하지만 아쉬운점은,
컨트롤러 하나에서 시작해 꼬리를 꼬리를 물고 의존관계를 가지는 구조로 iniMapping() 에서 모두 구현해야한다. 만약 의존관계가 깊어진다면 initMapping() 객체간의 의존관계를 매핑해주는 작업이 매우 복잡해지기 떄문에 이 코드 또한 개선이 필요하다. 이 내용은 추후 별도로 다루겠다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step4-어노테이션-기반-di-컨테이너-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step4-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EA%B8%B0%EB%B0%98-di-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;step4 어노테이션 기반 di 컨테이너 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;step4. 어노테이션 기반 DI 컨테이너 구현&lt;/h2&gt;
&lt;p&gt;다음으로 HandlerMapping 의 또 다른 구현체인 어노테이션 기반 &lt;code class=&quot;language-text&quot;&gt;AnnnotationHandlerMapping&lt;/code&gt; 에 대해서도 DI 컨테이너로 개선했다.&lt;/p&gt;
&lt;p&gt;기존의 어노테이션 기반 MVC 프레임워크는 자바 리플렉션을 활용해 @Controller 어노테이션이 설정되어 있는 클래스를 찾아 인스턴스를 생성하고, URL 매핑 작업을 자동화했다. 같은 방법으로 각 클래스에 대한 인스턴스 생성 및 의존관계 설정을 어노테이션을 자동화했다.&lt;/p&gt;
&lt;p&gt;어노테이션은 각 클래스 역할에 맞도록 &lt;code class=&quot;language-text&quot;&gt;@Service&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@Repository&lt;/code&gt; 어노테이션을 추가로 설정했다. 이 3개의 설정으로 생성된 각 인스턴스간의 의존관계는 &lt;code class=&quot;language-text&quot;&gt;@Inject&lt;/code&gt; 어노테이션을 사용한다. 이 &lt;code class=&quot;language-text&quot;&gt;@Inject&lt;/code&gt; 는 스프링부트에서 제공하는 &lt;code class=&quot;language-text&quot;&gt; @Autowired&lt;/code&gt; 와 비슷한 역할을 수행한다. DI 컨테이너에 생성된 각 인스턴스는 스프링부트 프레임워크와 마찬가지로 &lt;code class=&quot;language-text&quot;&gt;빈(Bean)&lt;/code&gt; 오브젝트로 부르곘다.&lt;/p&gt;
&lt;h3 id=&quot;beanfactory&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#beanfactory&quot; aria-label=&quot;beanfactory permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BeanFactory&lt;/h3&gt;
&lt;p&gt;빈(Bean) 인스턴스를 생성하는 클래스다. &lt;code class=&quot;language-text&quot;&gt;instantiateClass()&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;instantiateConstructor()&lt;/code&gt; 두 메소드의 재귀호출을 통해 복잡한 의존관계에 있는 빈을 생성하는 과정을 완료할 수 있다. &lt;code class=&quot;language-text&quot;&gt;instantiateClass()&lt;/code&gt; 을 보면 알 수 있듯이, Bean 인스턴스를 생성하기 위해 재귀 함수로 구현했다. &lt;code class=&quot;language-text&quot;&gt;@Inject&lt;/code&gt; 어노테이션이 설정되어 있는 생성자를 통해 Bean 객체를 생성한다. 그런데 이 생성자의 인자로 전달할 빈도 다른 빈과 의존관계에 있다. 이와 같이 꼬리에 꼬리를 물고 Bean 사이에서 의존관계가 발생할 수 있다. 다른 Bean 과 의존관계를 가지지 않는 Bean 을 찾아 인스턴스를 생성할 때 까지 재귀를 실행하는 방식으로 구현했다.&lt;/p&gt;
&lt;p&gt;재귀함수의 시작은 &lt;code class=&quot;language-text&quot;&gt;instantiateClass()&lt;/code&gt; 에서 시작하게 되는 것이다. &lt;code class=&quot;language-text&quot;&gt;@Inject&lt;/code&gt; 어노테이션이 설정되어 있는 생성자가 존재하면 &lt;code class=&quot;language-text&quot;&gt;instantiateConstructor()&lt;/code&gt; 메소드를 통해 인스턴스를 생성하고, 존재하지 않을 경우 기본 생성자로 인스턴스를 생성한다. &lt;code class=&quot;language-text&quot;&gt;instantiateConstructor()&lt;/code&gt; 메소드는 생성자의 인자로 전달할 빈이 생성되어 &lt;code class=&quot;language-text&quot;&gt;Map&amp;lt;Class&amp;lt;?&gt;, Object&gt;&lt;/code&gt; 에 이미 존재하면 해당 빈을 활용하고, 존재하지 않을 경우 &lt;code class=&quot;language-text&quot;&gt;instantiateClass()&lt;/code&gt; 메소드를 통해 빈을 생성한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BeanFactory&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BeanFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; preInstanticateBeans&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; beans &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Maps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newHashMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BeanFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; preInstanticateBeans&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;preInstanticateBeans &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; preInstanticateBeans&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@SuppressWarnings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;unchecked&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; requiredType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; beans&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requiredType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; clazz &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; preInstanticateBeans&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;beans&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clazz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;instantiateClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clazz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;instantiateClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; clazz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; bean &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; beans&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clazz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bean &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; bean&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Constructor&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; injectedConstructor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BeanFactoryUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInjectedConstructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clazz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;injectedConstructor &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            bean &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BeanUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;instantiate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clazz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            beans&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clazz&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bean&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; bean&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Constructor : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; injectedConstructor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        bean &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;instantiateConstructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;injectedConstructor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        beans&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clazz&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bean&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; bean&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;instantiateConstructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Constructor&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; constructor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; pTypes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; constructor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getParameterTypes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; args &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Lists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newArrayList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; clazz &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; pTypes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; concreteClazz &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BeanFactoryUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findConcreteClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clazz&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; preInstanticateBeans&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;preInstanticateBeans&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;concreteClazz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clazz &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;는 Bean 이 아닙니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; bean &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; beans&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;concreteClazz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bean &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                bean &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;instantiateClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;concreteClazz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bean&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BeanUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;instantiateClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;constructor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getControllers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; controllers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Maps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newHashMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; clazz &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; preInstanticateBeans&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Annotation&lt;/span&gt; annotation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; clazz&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAnnotation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Configurable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;annotation &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                controllers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clazz&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; beans&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clazz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; controllers&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;beanscanner&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#beanscanner&quot; aria-label=&quot;beanscanner permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BeanScanner&lt;/h3&gt;
&lt;p&gt;다음은 &lt;code class=&quot;language-text&quot;&gt;@Controlller&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@Service&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@Repository&lt;/code&gt; 로 설정되어 있는 빈을 찾는 &lt;code class=&quot;language-text&quot;&gt;BeanScanner&lt;/code&gt; 아래와 같이 구현했다. 또한 AnootationHandlerMapping 이 BeanFactory 를 사용할 수 있도록 개선했다. @Controller 로 설정한 빈을 조회할 수 있는 기능을 &lt;code class=&quot;language-text&quot;&gt;getControllers()&lt;/code&gt; 가 담당하게 했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BeanScanner&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Reflections&lt;/span&gt; reflections&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BeanScanner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; basePackage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        reflections &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Reflections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;basePackage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@SuppressWarnings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;unchecked&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getTypesAnnotatedWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Repository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@SuppressWarnings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;unchecked&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getTypesAnnotatedWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Annotation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; annotations&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; preInstantiatedBeans &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Sets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newHashSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Annotation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; annotation &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; annotations&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            preInstantiatedBeans&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reflections&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTypesAnnotatedWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;annotation&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; preInstantiatedBeans&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;annotationhandlermapping&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#annotationhandlermapping&quot; aria-label=&quot;annotationhandlermapping permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AnnotationHandlerMapping&lt;/h3&gt;
&lt;p&gt;마지막으로 리팩토링된 AnnotationHandlerMapping 이다. BeanFactory 를 초기화한 후 @Controller 로 설정한 빈을 사용하도록 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnnotationHandlerMapping&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerMapping&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AnnotationHandlerMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; basePackage&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HandlerKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerExecution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; handlerExecutions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Maps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newHashMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnnotationHandlerMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; basePackage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;basePackage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; basePackage&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;BeanScanner&lt;/span&gt; scanner &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BeanScanner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;basePackage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;BeanFactory&lt;/span&gt; beanFactory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BeanFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scanner&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        beanFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; controllers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; beanFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getControllers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Method&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; methods &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getRequestMappingMethods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;controllers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keySet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Method&lt;/span&gt; method &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; methods&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;RequestMapping&lt;/span&gt; rm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAnnotation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;register handlerExecution : url is {}, method is {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            handlerExecutions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createHandlerKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rm&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerExecution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;controllers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getDeclaringClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Initialized AnnotationHandlerMapping!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerKey&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createHandlerKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RequestMapping&lt;/span&gt; rm&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@SuppressWarnings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;unchecked&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Method&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getRequestMappingMethods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; controlleers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Method&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; requestMappingMethods &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Sets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newHashSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; clazz &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; controlleers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            requestMappingMethods
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ReflectionUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAllMethods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clazz&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReflectionUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withAnnotation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; requestMappingMethods&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerExecution&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; requestUri &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestURI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RequestMethod&lt;/span&gt; rm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUpperCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;requestUri : {}, requestMethod : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; requestUri&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rm&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; handlerExecutions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requestUri&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rm&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;어노테이션-적용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%A0%81%EC%9A%A9&quot; aria-label=&quot;어노테이션 적용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어노테이션 적용&lt;/h3&gt;
&lt;p&gt;@Controller 에 이어서 @Service, @Repository 어노테이션을 적용한 예시 코드는 아래와 같다. 우리가 항상 스프링부트 프레임워크를 사용할 때 표시했던 어노테이션과 아주 유사한 구조를 지니며, 기능 역시 똑같이 동작한다 🙂&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TestService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TestRepository&lt;/span&gt; testRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Inject&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TestService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TestRepository&lt;/span&gt; testRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;testRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; testRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@Repository&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TestRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이렇게 이전에 구현했던 어노테이션 기반 MVC 프레임워크 구현 코드와 DI 컨테이너 코드를 통합했다. 지금까지 구현했던 실습 미션 중에 가장 어려웠던 내용이 아닌가 싶다 🥲 아직 100% 완벽히 내 것으로 만들었다는 기분은 아니기에, 그리고 매우 유익한 내용이기 떄문에 향후 다시 한번 제대로 정리해볼까 한다. 서블릿 컨테이너, JSP 내용부터 전반 내용을 상세히 정리해 봐야겠다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[지금부터 내가 가져야 할 학습 방향성과 태도]]></title><description><![CDATA[…]]></description><link>https://haon.site/회고/attitude/</link><guid isPermaLink="false">https://haon.site/회고/attitude/</guid><pubDate>Sat, 23 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;카카오에서 주관하는 개발자 양성 과정에 참여한지 많은 시간이 지났다. 나는 훌륭한 카카오 코치님들, 최고의 동료들과 함께 성장하고 있다. 이 몰입 과정이 정말 끝나지 않았으면 하는 바람이지만, 수료까지 많은 시간이 남지 않았다. 수료까지, 아니 취업까지 난 어떻게 성장해야할까? 평소처럼 기술적인 내용을 다루지 않고, 정말 마음 편하게 짧은 회고를 해볼까 한다. 주말이기도 하고 😉&lt;/p&gt;
&lt;h2 id=&quot;불안함과-초조함-걷어내기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%88%EC%95%88%ED%95%A8%EA%B3%BC-%EC%B4%88%EC%A1%B0%ED%95%A8-%EA%B1%B7%EC%96%B4%EB%82%B4%EA%B8%B0&quot; aria-label=&quot;불안함과 초조함 걷어내기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;불안함과 초조함 걷어내기&lt;/h2&gt;
&lt;p&gt;내 스스로 느끼기에, 타인도 느끼기에 가장 안좋은 단점은 쉽게 불안함과 초조함을 느낀다는 점이다. 안좋은 습관도 생겼다. 자꾸 불안함을 느낄 때 마다 머리를 꼬는 버릇도 생겼다. 예전에 회고했기를, 의식적인 노력이 필요하다고 했지만 이 노력을 하지 않은것도 정말 오랜 시간이 지났다. &lt;a href=&quot;https://haon.blog/%ED%9A%8C%EA%B3%A0/unrest-behavior/&quot;&gt;개발자로 살아가면서 불안과 가면 증후군, 메타인지 학습법을 가꾸는 방법&lt;/a&gt; 에서 회고했듯이, DMN 은 의식을 의도적으로 잠시 다른 곳으로 분산키기거나, 잠시 일상 생활에서 벗어나는 행위를 통해 극복해야 없어질 수 있다. DMN 이 켜지는 것은 성장통으로 받아들이되, 활성 빈도가 높아질수록 일상에 큰 악영향을 끼친다는 점을 다시 꼭 알아두자.&lt;/p&gt;
&lt;p&gt;자주 생기는 불안함은 일상생활과 취업에 큰 영향을 끼치고 있었다. 불안함을 이겨내지 못한체 평일은 몰론 주말에 새벽 2시가 넘어서까지 노트북을 내려놓지 못한다. 충분한 휴식이 있어야만 평소 일상생활에 일에 집중할 수 있고, 더 빠르게 성장할 수 있다. 이를 작업하면서도 생각하고 있지만, 마음처럼 그는 쉽게 되지 않는다.&lt;/p&gt;
&lt;p&gt;또한 불안함이 만땅인 상태에서 충동적으로 지금 문제를 급히 해결하려 시도할 때가 많다. 이는 이력서를 제출하면서 많이 느꼈던 점이다. 불합격 통보를 받고, 충분한 생각과 고민, 그리고 휴식을 거치지 않은체 곧바로 입사 지원을 하고있다. 제 3자의 입장에서 봤을때도 이 불안함은 좋지 못할 것이다. 내가 다시 한번 인지해야 할 내용은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;불안함에 가득차있을 때는 의식적인 노력이 필요하는 점을 다시 꼭 기억하자. 일정한 극복 패턴이 필요하다.&lt;/li&gt;
&lt;li&gt;불안함에 가득차 있을때 결정을 내렸던 행동 중 단 한번도 좋은 결과물이 나온적이 없다.&lt;/li&gt;
&lt;li&gt;불안함으로 인해 충분한 휴식을 취하지 않았을 때 항상 일주일동안의 작업량은 매우 극명하게 낮다.&lt;/li&gt;
&lt;li&gt;강제로 휴식을 취해야는 날을 주 1회이상 만든다. 이 또한 의식적인 노력이다.&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;주변에 성공하는 사람들 모두가 내가 생각한 것 보다 훨씬 많은 실패를 겪었다. 서강대 출신 30만 구독 유튜버도 58번의 서류 탈을 통보받았다.&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;h3 id=&quot;훌륭한-퀄리티를-보장하는-휴식은-무엇일까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%9B%8C%EB%A5%AD%ED%95%9C-%ED%80%84%EB%A6%AC%ED%8B%B0%EB%A5%BC-%EB%B3%B4%EC%9E%A5%ED%95%98%EB%8A%94-%ED%9C%B4%EC%8B%9D%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C&quot; aria-label=&quot;훌륭한 퀄리티를 보장하는 휴식은 무엇일까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;훌륭한 퀄리티를 보장하는 휴식은 무엇일까?&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f269684e01ec6dd97c3959b0f594c5fa/69476/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.50920245398773%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAADdUlEQVR42hWPe0zUBQCAf1bK1lYOneYirRSmqRSjrHgUgrzvLiHi0XjIAXc8OggOQt5w3IHHPTg4wBMQdqA0jjCgQ2ihcMQJ9liuWvTYbNNyWX+0uYA7lPyi/79v+z4hfv4B2d9ukPn1BsEfutnS6OIR3QaH9b8QcmaWcMMcIdoZsix29FMLNNqXiNCM4Vtt5+WPVkl0uHj98gpetjUkM6sIj6nWeNLg4lG1iy0aN9sM63iY3YhLdNQqMlHmpFMsl5Kakkx+1ikKczJJSksnKldJ+NIDAufXiVi4j2jqDmfmf0cQGtxsbXKzs83FrrNrbO9ewXPgH/ZYb/Hm5G0yFv8g7MqfxFz9i8LF31A47yK74SLHakcxvkis04Vk4h5y20+IdYsInps1T/W6ifv8303wIcfn1gmZd5P5w0Oyv7nP8MxNzM5b1C7/Te3NFaSOiyiWJmmvzGdQmUiqsY3U3qvkD19H1jOK4Keaxmt0lecvfIr/uJME5z0UP29QetvFlN2BU57NSE0CupoUzrSU8UF3MqmaVzG3VlBnaCbDYEHn+JWiswOo6+IR5A1q4szdBDaFk6KNJMvUSvHIFI1jGm7o9ZzTSEnsOkKaNoYCi4z06QQ01mjybN9xotuAqOcSgeY7SI3tyMtLEOZsp7B1v4veLCKn9G0y2gxIhmY4bfuMobYKci/7EL/0LKnXfJHMvoh8dAeTvVLU58ZJqtJQXpNKuaqQqrJ0EvK0CGMDicxOn6a5SY74fROSEQfxFj0xlrtk9AXTZDpEp+EYRVpvGgd9mOwKoEMbR7slkpT6So6L/YgQBVGvyMCkrkIIyPNHbJnnra7rSPvGae/qoX9zTTmST+6lFygtjSWq8TUOGLdyTevLQpcEvSkDq9EHVccJjlbnEZPrR0PlO+QVpiAkv/cGrWYZbR2J1F+IxtQro9gWyfmRg+gH9xFtPkpSvz+KT/ZTcTEKhSOUuCtHeKV/DwUD3pR0SJGrigirr2dvQSFCc5ov481iDMqTGOtiaOlW0Ha+hbLmEPI6d3DgYw+yTbuZKY1A1u7DS8tPEPT9do59sRPRxEH6xkTUzIoIHk8mpkOG0KIOpb8zEVXZSWpKlQxZh6kerGTvxP/CM+j6wpBbgwg17uewYRsRX+4matkLyY/PEfDVLrztnuybfBxfpwexC0/zHy5JVlN8MW0cAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/f269684e01ec6dd97c3959b0f594c5fa/a6d36/image.png&quot;
        srcset=&quot;/static/f269684e01ec6dd97c3959b0f594c5fa/222b7/image.png 163w,
/static/f269684e01ec6dd97c3959b0f594c5fa/ff46a/image.png 325w,
/static/f269684e01ec6dd97c3959b0f594c5fa/a6d36/image.png 650w,
/static/f269684e01ec6dd97c3959b0f594c5fa/69476/image.png 926w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;나에게 더 일에 몰입하기 위한 휴식 방법에 대한 고민이 많았다. 취미를 만들기 위해 여러 고민이 많았다. 일과 시간이 끝나면 저녁을 먹고 밖에나가 몇 시간동안 뛰어보기도 했고, 어릴 때 즐겨했던 마인크래프트 게임을 다운로드 받아서 잠시 추억을 회상해보기도 했다. 하지만, 게임을 즐겨했던 것도 잠시일 뿐, 옛날처럼 그다지 내게 큰 재미를 가져다주지는 못했다. 운동을 나가서 열심히 조깅하는 것은 도움이 된 것은 분명하지만, 완전히 심신의 안정감을 가져다주는 것에는 도달하지 못했다. &lt;del&gt;(마인크래프트 8,900원에 유료 구매한거 매우 후회중이다!)&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/89c24a6422c799706dc9b18e97ee34da/914ae/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.239263803680984%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAACNklEQVR42mWS+0/SURjG+Z/6C/q5FCGWkq0sq7W1/CFN5xxOXaXTnJWZumUpKl4RQVjcBOSrcktRV9oNCQuywomXUkv36UCulv3w7Hnfnb3P+5znHJmpT4FlMA/LkDrDY/0qUedlavPAadLnRzHWr2BY+5vto+fxuUsJ+yqZndYgSw+Hfc3M+VsISfV4nt1gPtgm0I7fXcNob5YYVP4VEzD0KmlqVKPtLMDv1bAUvkXAW8GUq1wICidBbz2SrUxsqhZ1HZKjHOtIATbDRSGQg1GnZFSnEJwjXCu4W5tFbf01XKYGJsfKMQ0U4TCX4rSXITMP5hLw3GbCWsKUUyNcNuCyFAlnJzPu9D2n6GjPF6zE0p+NZC3ENv6YlbeL/Ix6CN0rpfr4MV6YyggHK5GZhIP0cHq7USfH0HNC9NmZfHSdalofnePBw3yedpxlQqpjLfWaPSC5HGJ7Xs/WeBcR9zDJQAdLodq0oMjmT0bKQygyuTXfz0VTrUJqPUP0SS5xZxPxxQDfEwvsR+3sLhk4eG9jZ3WWzc8R9vc2DwWPvqKAvltOVU0OziYVO5YK1nw6do3FbHXnk+q7xBdHI+sLJiJzQTaSq3xNvGL5Y+R/QWOvHLO4rst2lZmuQrBqOEitkPywyPrQdTb6LrNtLCHmaCcVf4N7REvspQ8OvuGVWv4VTOdo1auZ9tzEP1CM1HZB/LMqPoW07AxdYXeyhfVYmMi8nR8bCUg8ZzM2g8czSTT2Du/4HX4BasgTNdZstZ0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/89c24a6422c799706dc9b18e97ee34da/a6d36/image-1.png&quot;
        srcset=&quot;/static/89c24a6422c799706dc9b18e97ee34da/222b7/image-1.png 163w,
/static/89c24a6422c799706dc9b18e97ee34da/ff46a/image-1.png 325w,
/static/89c24a6422c799706dc9b18e97ee34da/a6d36/image-1.png 650w,
/static/89c24a6422c799706dc9b18e97ee34da/914ae/image-1.png 860w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;좋은 휴식에 대한 조건은 보통 아래 3가지와 같다고 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;편안&lt;/strong&gt;: 휴식이 끝났을 때 심신의 안정을 얻을 수 있어야 한다. 휴식을 했는데도 긴장이 풀리지 않거나 되려 더 자극 받게 되는 방법들(뉴스, 공포 영화, 리그오브레전드 등의 자극적인 게임) 은 편안하지 않다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;도파민 분출과 몰입&lt;/strong&gt;: 나 또한 이에 매우 공감되는 내용인데, 우리 뇌는 무언가에 몰입할 떄 도파민이 분출된다. 반면 인스타, 쇼츠등의 의식적이지 않고 행복하지 않은 것은 좋은 휴식이 아니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;거리두기&lt;/strong&gt;: 앞서 회고한 내용과 동일하다. 강제로 일에서 벗어나는 것이다. 진정한 휴식은 &quot;일과 얼마나 거리를 두었는가&quot; 이다. 업무 외적인 시간에도 업무를 생각해야 업무 능률이 더 향상될 것 같지만, 사실 뇌는 충분한 휴식을 가진 뇌가 더 효율적으로 일한다고 한다. 나 또한 매우 공감하는 내용이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;앞서 회고한 내용들이 대다수 위 내용에 포함된 내용들이다. &lt;strong&gt;진정한 휴식이라면 편안해야 하고, 내가 몰입할 수 있여야하며, 업무와 전혀 관련없는 것이어야 한다.&lt;/strong&gt; 앞으로 나는 최소 주 1회이상 업무와 전혀 연관없는 취미를 가져볼까 한다. 또한 내가 재밌게 몰입할 수 있는 취미를 해보면서, 실제로 충분한 휴식이 내 성장을 얼마나 부스트 할 수 있을지 실험해볼까 한다. 글을 적다보니, 앞으로 어떤 것을 취미로 가질지 고민이 생긴다. 막연하게 당장 떠오르는 취미는 아래와 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;글쓰기 (현실적으로 가장 좋아하는 것. 지금도 글을 너무 재밌게 쓰고 있다. 😂)&lt;/li&gt;
&lt;li&gt;레고 아이쇼핑 및 간단한 조립 (정말 내가 좋아했던 레고를 다시 해볼까 생각한다. 다만 너무 비싸서, 비싼건 아이쇼핑만 하고 가격이 그나마 괜찮은 건 조립해보기로.)&lt;/li&gt;
&lt;li&gt;맛집과 좋은 카페 찾아다니기 (안그래도, 내일 판교 주변에 있는 백현동 카페거리에 가볼까 한다.)&lt;/li&gt;
&lt;li&gt;요리 만들기 (요즘 좋은 취미가 생긴 것 같다. &lt;del&gt;백종원님 레시피 보면서 요리 만드는게 생각보다 너무 재밌다!&lt;/del&gt;)&lt;/li&gt;
&lt;li&gt;복싱 (현실적으로 판교에서는 하기 힘들지만, 김포로 다시 돌아다면 시작할지도?)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;유튜브만 보지 말고, 주 1회 더 생산적으로 도파민이 분출될만한 취미를 찾아 나서보자.&lt;/p&gt;
&lt;h3 id=&quot;블로그와-코딩테스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%94%EB%A1%9C%EA%B7%B8%EC%99%80-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;블로그와 코딩테스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;블로그와 코딩테스트&lt;/h3&gt;
&lt;h4&gt;블로그&lt;/h4&gt;
&lt;p&gt;최근 10월 말~11월 초중순 사이, 괜히 바쁘다는 핑게로 글을 거의 발생하지 못했다. 1차 데모데이 마감기한이 얼마 남지 않았기 떄문에, 사실 그 당시 글을 발생하지 못했던 것은 어쩌면 당연하다. 핑계가 아닐 수 있다. 하지만, 글쓰기를 통해 내 성장을 이끌어내지 못했던 것은 객관적으로 자명한 사실이다.&lt;/p&gt;
&lt;p&gt;나는 블로깅을 위해 아래 재밌는 실험을 해볼까 한다.&lt;/p&gt;
&lt;p&gt;블로그 작성 루틴에 대한 실험이다. 나는 글을 한번 작성할 떄 제대로 이해했는지 알기 위해서 쓰지만, 간혹 남에게 더 잘보여주고 싶은 괜한 욕심도 생겨서 장문의 글을 작성할 떄도 있다. 글의 길이로 인해 포스팅 작성이 간혹 부담스럽게 느껴져서 시작을 안할떄도 있다. 그래서 나는, 한가지 실험을 해볼까한다. &lt;strong&gt;글의 퀄리티, 길이를 신경쓰지 않고 주 3회 이상의 포스팅을 강제로 발행하는 것이다.&lt;/strong&gt; 블로그를 남에게 보여주는 것에 포커싱을 맞춰야 하는 것이 아니라, 내 학습 수단을 위한 것임을 잊지 말도록 해야한다. 우선 위 방식대로 몇주간 강제로 글을 발행해본 뒤, 기존 방식과 강제적인 글 발행 방식 중에 어떤 것이 더 내 성장을 부스팅하는지 실험해보고자 한다.&lt;/p&gt;
&lt;h4&gt;코딩테스트&lt;/h4&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/38185de3bddb903ad63650945eb85305/0940f/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.71165644171779%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAABfklEQVR42q1S2W7CMBDk/z+F30BUqBJ9AMKZBHKQ2MFxLnLQHNPFBYoqpKotG612vHZmZ9fu4c7arkVH33+shydb76ooqzP0WR8DOVDrvyq9KSyaAnqhg1XsOS133ZNafvoMr+B4PCKOY2RZpmKSJCiKgnCCMJSIoghSfsY0TcEYB+ccQgj6N7+7lEuv2nyO4fAFs5mG0egV4/EblssVFoslNE1TeDKZYj5fwLYdFafTGVarNYIguIytOxN+MnueB1034Lp7OI4LwzCxXm9IZXk7/LtbLkvkeYmU5GfkbdvdiK7+3R7t3QgPNCdn72Pvc7geUwXOXlYnlGWF0+mdcEW5CnlxydO6advHCn1+ICKu3HY9sEBgZ7uwCOtbi3I+dNPG2thiY+6wtVyVDw4hFSiQZkfqqv0iZMEBpuUoMmNnY0tkO3uvouUQqWmpomFELyDNIGSEQEjVzbm4zwPUdX33bPIcoYwR0+EoTiHCCFyEhBPESQoZJWia9sdL+QAfnT6kCOm4xAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/38185de3bddb903ad63650945eb85305/a6d36/image-2.png&quot;
        srcset=&quot;/static/38185de3bddb903ad63650945eb85305/222b7/image-2.png 163w,
/static/38185de3bddb903ad63650945eb85305/ff46a/image-2.png 325w,
/static/38185de3bddb903ad63650945eb85305/a6d36/image-2.png 650w,
/static/38185de3bddb903ad63650945eb85305/e548f/image-2.png 975w,
/static/38185de3bddb903ad63650945eb85305/0940f/image-2.png 1154w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;코딩 테스트가 가장 심각한 내 단점이다. 사실 지난번에 &lt;strong&gt;여러 차례 회사에서 경력직 채용 서류 합격 통보를 받았음에도 불구하고, 결국 코딩테스트에서 탈락&lt;/strong&gt;이 되버렸다. 내가 현실적으로 가장 몰입해야 할 것은 코딩테스트이다.&lt;/p&gt;
&lt;p&gt;코딩테스트 또한 강제성을 부여해볼까 한다. &lt;strong&gt;하루 2문제, 주 12문제 이상의 알고리즘 문제를 강제로 푸는 것이다.&lt;/strong&gt; 또한 여기에 조건을 붙여보고자 한다. 그 당일 문제가 (내 주관적인 기준하에) 쉽다고 판단되면, 2문제 이상이 아닌 여러 문제를 풀어야 그 날의 할당량이 인정되는 방식이다. 또한 알고리즘 또한 글쓰기를 통해 &lt;strong&gt;생각을 정교화(elaboration)&lt;/strong&gt; 해볼까한다. 지금까지 알고리즘 풀이를 마구잡이로 블로그에 작성한다면 글을 남발한다는 생각 떄문에 작성하지 않았다. 이젠, 알고리즘 또한 중요성을 꺠달았기에 생각을 확실히 정교화해볼까 한다. 세컨드브레인 또는 새로운 기술 블로그를 개설하여 풀이 과정을 설명하는 방식으로 공부해볼까 한다. 단, 메타인지 활성화를 위해 확실히 개념을 정교화 싶은 이론은 현재 메인 블로그에 글을 작성하는 것을 고려해보자. 어떤 방식이 나에게 최적으로 성장을 부스팅할지 확정할 수 없기 떄문에, 우선 글을 작성한다는 것 그 하나만은 확실히 결정하도록 한다. 어떤 방식으로 글을 작성할지만 달라질 듯 하다.&lt;/p&gt;
&lt;p&gt;다시 좋은 기회를 잃고싶지 않다면, 정신차리고 코딩테스트에 집중하자. 현실적으로 이제 내년 상반기 취업을 위해 준비할 수 있는 마지막 기회임을 절대 잊지말자. 내가 하고싶은 주제만 하면 안된다. 현실을 바라보자.&lt;/p&gt;
&lt;h4&gt;내가 쌓아온 정량적 지표들 정리하기, 모범 케이스 찾아보기&lt;/h4&gt;
&lt;p&gt;다시 한번 이력서를 점검하는 시간도 가져볼까 한다. 모범 사례들을 다시 한번 참고하여, 내 활동을 더 응집하여 표현해볼까 한다. 우선 당장에는, 다음주 카카오 Tech Talk 발표를 준비하고, 교육과정에서 활동한 내용들을 별도 깃허브 레포지토리를 생성하여 아카이브 해볼까 한다.&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;글을 적으니 역시 생각이 정리된다. 더 빠르게 성장을 부스트하기 위해, 내 생각을 잠시 정리해보는 시간을 가져보았다. 앞으로도 더 빠르게 성장할 수 있는 내가 되기위해, 꾸준히 노력해보자. 불안함의 안개를 걷어내기 위해 의식적인 노력을 하고, 충분한 휴식을 가져보자. 블로그와 코딩테스트, 이력서를 계획한대로 발전시켜보자.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[[가상 면접 사례로 배우는 대규모 시스템 설계 기초] 제 10장. 알림 시스템 설계]]></title><description><![CDATA[…]]></description><link>https://haon.site/virtual-interview/chap04/</link><guid isPermaLink="false">https://haon.site/virtual-interview/chap04/</guid><pubDate>Fri, 22 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;1단계-문제-이해-및-설계-범위-확정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1%EB%8B%A8%EA%B3%84-%EB%AC%B8%EC%A0%9C-%EC%9D%B4%ED%95%B4-%EB%B0%8F-%EC%84%A4%EA%B3%84-%EB%B2%94%EC%9C%84-%ED%99%95%EC%A0%95&quot; aria-label=&quot;1단계 문제 이해 및 설계 범위 확정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1단계. 문제 이해 및 설계 범위 확정&lt;/h2&gt;
&lt;p&gt;하루에 백만건 이상의 알림을 처리하는 확장성 높은 시스템을 구축하는게 쉬운 과제가 아니다. 알림 시스템이 어떻게 구현되는지에 대한 깊은 이해가 필요한 작업이다. 이에 관한 무제가 면접에 출제될 때는 보통 정해진 정답이 없고, 문제 자체가 모호하게 주어지는 것이 일반적이므로, 적절한 질문을 통해 요구사항이 무엇인지 지원자 스스로 알아내야 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;지원자 : 이 시스템은 어떤 종류의 알림을 지원해야 하는가?&lt;/li&gt;
&lt;li&gt;면접관 : 푸시 알림, SMS 메시지, 그리고 이메일이다.&lt;/li&gt;
&lt;li&gt;지원자 : 실시간 시스템이어야 하는가?&lt;/li&gt;
&lt;li&gt;면접관 : 연성 실시간(soft real-time) 시스템이라고 가정한다. 알림은 가능한 빨리 전달되어야 하지만, 시스템에 높은 부하가 걸렸을 때 약간의 지연은 무방하다.&lt;/li&gt;
&lt;li&gt;지원자 : 어떤 종류의 단말을 지원해야 하는가?&lt;/li&gt;
&lt;li&gt;면접관 : ios 단말, 안드로이드 단말, 그리고 랩탑/데스크탑을 지원해야 한다.&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;면접관 : 천만 건의 모바일 푸시 알림, 백만 건의 SMS 메시지, 5백만 건의 이메일을 보낼 수 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;2단계-개략적-설계안-제시-및-동의-구하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2%EB%8B%A8%EA%B3%84-%EA%B0%9C%EB%9E%B5%EC%A0%81-%EC%84%A4%EA%B3%84%EC%95%88-%EC%A0%9C%EC%8B%9C-%EB%B0%8F-%EB%8F%99%EC%9D%98-%EA%B5%AC%ED%95%98%EA%B8%B0&quot; aria-label=&quot;2단계 개략적 설계안 제시 및 동의 구하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2단계. 개략적 설계안 제시 및 동의 구하기&lt;/h2&gt;
&lt;p&gt;ios, 및 안드로이드 푸시 알림, SMS 메시지, 그리고 이메일을 지원하는 알림 시스텥ㅁ의 개략적 설계안을 만들어보자. 이를 위해 아래 내용들을 다룬다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;알림 유형별 지원 방안&lt;/li&gt;
&lt;li&gt;연락처 정보 수집 절차&lt;/li&gt;
&lt;li&gt;알림 전송 및 수신 절차&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;알림-유형별-지원-방안&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%8C%EB%A6%BC-%EC%9C%A0%ED%98%95%EB%B3%84-%EC%A7%80%EC%9B%90-%EB%B0%A9%EC%95%88&quot; aria-label=&quot;알림 유형별 지원 방안 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;알림 유형별 지원 방안&lt;/h3&gt;
&lt;h4&gt;ios 푸시 알림&lt;/h4&gt;
&lt;p&gt;ios 에서 푸시 알림을 보내기 위해서는 3가지 컴포넌트가 필요하다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 알림 제공자 : 알림 요청을 만들어 애플 푸시 알림 서비스(APNS) 로 보내는 주체이다. 알림 요청을 만들려면 다음과 같은 데이터가 필요하다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;단말 토큰 : 알림 요청을 보내는 데 필요한 고유 식별자이다.&lt;/li&gt;
&lt;li&gt;페이로드 : 알림 내용을 담은 JSON 딕셔너리이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; APNS : 애플이 제공하는 원격 서비스이다. 푸시 알림을 ios 장치로 보내는 역할을 담당한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; ios 단말 : 푸시 알림을 수신하는 사용자 단말이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;안드로이드 푸시 알림&lt;/h4&gt;
&lt;p&gt;안드로이드 푸시 알림도 비슷한 절차도 전송된다. APNS 대신 FCM(Firebase Cloud Messaging) 을 사용한다는 점만 다르다.&lt;/p&gt;
&lt;h4&gt;SMS 메시지&lt;/h4&gt;
&lt;p&gt;SMS 메시지를 보낼 때는보통 트월리오, 넥스모 같은 제 3 사업자의 서비스를 많이 이용한다. 이런 서비스는 대부분 상용 서비스라서 이용 요금을 내야한다.&lt;/p&gt;
&lt;h4&gt;이메일&lt;/h4&gt;
&lt;p&gt;대부분의 회사는 고유 이메일 서버를 구축할 역량은 갖추고 있다. 그럼에도 많은 회사가 상용 이메일 서버를 이용한다. 그 중 유명한 서비스로 센드그리드, 메일침프가 있다. 전송 성공률도 높고, 데이터 분석 서비스도 제공한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0cf7b28572b62eb0c2aa46daf56e2a25/3d4b6/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.282208588957054%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABnElEQVR42o2T2W6DQAxF+f8P62NVqYuUAGHfAkkg7DDg+rohJU2q9mEEYzzHd66NNo4jPVp5cabDKacky/h5omifUpxm1DTNNWcYhrtz2m/A9HAUwM71BOZFEZmORzUDp2minmFKqcdAfFCrIJJRPckODPQpTPasrJX4qSgE7oUR6ZZzp1JbSx8uVbG6vqMgTmhr2RSlKXVdJznnspJCGdsAxX3f3wJNx/26Dl8tOx6pqmvxrTifKWSgF8X8Xgqw7Xoqq5p8jgWses+2tG13C0zYp5g9svyAjnnOwIZyhgFicLHNzibk1G3Lh1vx83Wjk2G79L41ZD/P8/WWGrqGhUDfDzTxdWE64B+GSe+6SW9bXYpgOUFIOheSp+3QzLlrH7XFs/FHUwBEh3E9NALqkAdbYvY0ZGXwF1aszz8cGxgNBU/PL2R5vig75YWMElS53GE0Razi7zXbtEDvgBgfqMFQm5cZLOuKGo6VVUU2e235vhTcMWyzs6TzSv2h8HtwlXS34xi8hWd4KrxzM7BX//lTlrlcG77sH60F+AlkxJreBjs5FAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/0cf7b28572b62eb0c2aa46daf56e2a25/a6d36/image.png&quot;
        srcset=&quot;/static/0cf7b28572b62eb0c2aa46daf56e2a25/222b7/image.png 163w,
/static/0cf7b28572b62eb0c2aa46daf56e2a25/ff46a/image.png 325w,
/static/0cf7b28572b62eb0c2aa46daf56e2a25/a6d36/image.png 650w,
/static/0cf7b28572b62eb0c2aa46daf56e2a25/3d4b6/image.png 712w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;연락처-수집-정보-절차&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%B0%EB%9D%BD%EC%B2%98-%EC%88%98%EC%A7%91-%EC%A0%95%EB%B3%B4-%EC%A0%88%EC%B0%A8&quot; aria-label=&quot;연락처 수집 정보 절차 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;연락처 수집 정보 절차&lt;/h3&gt;
&lt;p&gt;알림을 보내려면 모바일 단말 토큰, 전화번호, 이메일 주소 등의 정보가 필요하다. 사용자가 우리 앱을 설치하거나 처음으로 회원가입 하면 API 서버는 해당 사용자의 정보를 수집하여 데이터베이스에 저장한다.&lt;/p&gt;
&lt;p&gt;데이터베이스 스키마 설계도 잘 고려해보자. user, device 라는 테이블을 설계하고, user 와 device 테이블을 1:n 관계로 매핑한다. 그리고 user 테이블에는 이메일 주소와 전화번호를 저장하고, device 테이블에는 단말 토큰을 저장한다. 한 사용자가 여러 단말을 가질 수 있고, 알림은 모든 단말에 전송되어야 한다는 점을 고려하였다.&lt;/p&gt;
&lt;h3 id=&quot;알림-전송-및-수신-절차&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%8C%EB%A6%BC-%EC%A0%84%EC%86%A1-%EB%B0%8F-%EC%88%98%EC%8B%A0-%EC%A0%88%EC%B0%A8&quot; aria-label=&quot;알림 전송 및 수신 절차 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;알림 전송 및 수신 절차&lt;/h3&gt;
&lt;p&gt;우선 개략적인 설계안부터 살펴보고, 점차 최적화 해나가보자.&lt;/p&gt;
&lt;h4&gt;개략적 설계안 (초안)&lt;/h4&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9a0535a87748863bff61b854d57091bb/88745/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.852760736196316%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABlUlEQVR42o1SiU7CUBDs/3+LH6DGRKNIMBwlhZa2AgUKFJDSgys9YNxdLKCJxiaT99q3OzszfUqWZfgLeZ5js91iPJtjQph9LBFvNsizXNYkSb7VK2mSyiZJT2vKeypK6V1WOredAUYTD9WWjr47xmLpozdy0TQsPL3VzvUMxQ8jPFaqGIyn2O33ogjHIzJe6QnjtZy53gyq0UFA9XNSaTtDtO2uIIxjaqEeUq3wxJvbBxjdPsIohjdfyPQOvTMsUsc1Zs/Bfaks1oektkZqVcOUPnfqEeFBlCoBkfD05SrAmrJyxhO0LFsaSjUVdn8g2envPVRUTWxa9I2VcU2zY4mTIm/FD0LJQSMSlh6RxSLg4+GAFVlU9Q7uXsqoNDXBlF0MXSJ2aIAJn8ScM4zXa8wpZCYrSHjS9V/uDkeoEynnxuTs5rXRhGbaqLcNlGkfEQ/nqOT5haSYUoAzOXypfK42pPF9MBKL/AP52nBMfK12uz0JIIX/uYfczBZV3RRyHlKcFbjcwx+qfgO7OF2N7Kz+GkWGn70k8xd2DkahAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/9a0535a87748863bff61b854d57091bb/a6d36/image-1.png&quot;
        srcset=&quot;/static/9a0535a87748863bff61b854d57091bb/222b7/image-1.png 163w,
/static/9a0535a87748863bff61b854d57091bb/ff46a/image-1.png 325w,
/static/9a0535a87748863bff61b854d57091bb/a6d36/image-1.png 650w,
/static/9a0535a87748863bff61b854d57091bb/e548f/image-1.png 975w,
/static/9a0535a87748863bff61b854d57091bb/3c492/image-1.png 1300w,
/static/9a0535a87748863bff61b854d57091bb/88745/image-1.png 2148w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1~N까지의 서비스 : 이 서비스 각각은 MSA 일 수도 있고, CronJob 일 수도 있고, 분산 시스템 컴포넌트 일 수도 있다. 사용자에게 납기일을 알리고자 하는 과금 서비스, 배송 알림을 보내려는 쇼핑몰 웹 사이트 등이 그 예시이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;알림 시스템 : 알림 시스템은 알림 송.수신 처리의 핵심이다. 우선 1개 서버만 사용하는 시스템이라고 가정해보자. 이 시스템은 서비스 1~N에 알림 전송을 위한 API 를 제공해야 하고, 제 3자 서비스에 전달할 알림 페이로드(payload) 를 만들어 낼 수 있어야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;제 3자 서비스 (서드파티 서비스) : 이 서비스들은 사용자에게 알림을 실제로 전달하는 역할을 한다. 제 3자 서비스와의 통합을 진행할 때 유의할 것은 확장성이다. 쉽게 새로운 서비스를 통합하거나 기존 서비스를 제거할 수 있어야 한다는 뜻이다. 또 하나 고려해야 할 점은, 어떤 서비스는 다른 시장에서는 사용 못 할 수도 있다는 점이다. 따라서 중국 시장에서는 제이푸시, 푸시와이 같은 서비스를 사용해야만 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ios, 안드로이드, SMS, 이메일 단말 : 사용자는 자기 단말에서 알림을 수신한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;문제점&lt;/h4&gt;
&lt;p&gt;하지만, 위 설계에는 몇 가지 문제점이 존재한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;SPOF : 알림 서비스에 서버가 1대밖에 없다. 따라서 알림 서비스에 장애가 발생하면 전체 서비스의 장애로 이어진다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;규모 확장성 : 1대의 서비스로 푸시 알림에 관계된 모든 것을 처리하므로, 데이터베이스나 캐시 등 중요 컴포넌트의 규모를 개별적으로 늘릴 방법이 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;병목 발생 : 알림을 처리하고 보내는 것은 자원이 많이 필요한 작업이다. 예를들어 HTML 페이지를 만들고 서드파티 서비스의 응답을 기다리는 일은 시간이 많이 걸릴 가능성이 있는 작업이다. 따라서 모든 것을 한 서버로 처리하면 사용자 트래픽이 많이 몰리는 시간에는 시스템이 과부하 상태에 빠질 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;개략적-설계안-개선된-버전&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%9C%EB%9E%B5%EC%A0%81-%EC%84%A4%EA%B3%84%EC%95%88-%EA%B0%9C%EC%84%A0%EB%90%9C-%EB%B2%84%EC%A0%84&quot; aria-label=&quot;개략적 설계안 개선된 버전 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;개략적 설계안 (개선된 버전)&lt;/h3&gt;
&lt;p&gt;문제점을 아래와 같이 개선할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/61d974c5ffb32bc18bfad2acf1acaffa/807a0/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.533742331288344%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABsUlEQVR42nVSa3ObQAzk//+rznQmaTppbVOHh2OIwRhztsFg835sV9hkMm3yYZHQSas9nbS2bdEQ7WfoOvR9j178usZA2zEm8clOtZPV5CNF/5J1XYvqksF1HXieh+JygYoi5OkZGf1rnqOq6v/qtKZpkOcFxHb3zgMb1EVBog3mhoWFaSM6nmCsXRxJajsulqs11CmG428xNy3kzJda7Zxm+Pb0jFApFGWJNMuQEPswhOP5+DHX8cLiXaRGwhPzDP6b9BWbCNnsxcSB5L0QppT/a2mMh6E6YGHZ+Kkv4VBFEO6h2yuUVYlgH2FJP2Ij233D9+ffWG98zEn2x1qhrm831ESqThIhK+i7ros0STgzzo+ze6XK7HLFlgp1w4Tv+3ik6sfZApbzhteNhxnjAc/fZ1jzBcUOTYUVu8dxjK4sUF0z5NmZSJGoCLutj/h0xAPJZBTxOR2Vb4IdvCBERR5tevK+bbBl9ydeVxKaIkfLJi3jYmV15LEk1+cohEBm1sla3fGucEJ8PEDtQyS0NR9IipvmjtG/5Q3DjeBj7QTt4w5J0jCuzheLPi3xqPzzs78tVviZtA/nCwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/61d974c5ffb32bc18bfad2acf1acaffa/a6d36/image-2.png&quot;
        srcset=&quot;/static/61d974c5ffb32bc18bfad2acf1acaffa/222b7/image-2.png 163w,
/static/61d974c5ffb32bc18bfad2acf1acaffa/ff46a/image-2.png 325w,
/static/61d974c5ffb32bc18bfad2acf1acaffa/a6d36/image-2.png 650w,
/static/61d974c5ffb32bc18bfad2acf1acaffa/e548f/image-2.png 975w,
/static/61d974c5ffb32bc18bfad2acf1acaffa/3c492/image-2.png 1300w,
/static/61d974c5ffb32bc18bfad2acf1acaffa/807a0/image-2.png 1652w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 개선된 점&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;데이터베이스와 캐시를 알림 시스템의 주 서버에서 분리하였다.&lt;/li&gt;
&lt;li&gt;알림 서버를 증설하고 자동으로 수평 확장(Scale Out) 이 이루어질 수 있도록 개선하였다.&lt;/li&gt;
&lt;li&gt;메시지 큐를 이용해 시스템 컴포넌트 사이의 강한 결합을 끊었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h4&gt;각 컴포넌트의 역할&lt;/h4&gt;
&lt;p&gt;위와 같은 설계안에서 각 컴포넌트는 어떠한 역할을 수행할까?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1부터 N까지의 서비스 : 알림 시스템 서버의 API 를 통해 알림을 보낼 서비스들&lt;/li&gt;
&lt;li&gt;알림 서버
&lt;ul&gt;
&lt;li&gt;알림 전송 API : 스팸 방지를 위해 보통 사내 서비스 또는 인증된 클라이언트만 이용 가능하다.&lt;/li&gt;
&lt;li&gt;알림 검증(validation) : 이메일 주소, 전화번호 등에 대한 기본적 검증을 수행한다.&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;메시지 큐 : 시스테 컴포넌트 간 의존성을 제거하기 위해 사용한다. 다량의 알림이 전송되어야 하는 경우를 대비한 버퍼 역할도 한다. 본 설계안에서는 알림의 종류별로 별도의 메시지 큐를 사용하였다. 따라서 서드파티 서비스 중 하나가 장애가 발생하더라도 다른 종류의 알림 기능은 정상 동작한다.&lt;/li&gt;
&lt;li&gt;작업 서버 : 메시지 큐에서 전송할 알림을 꺼내서 서드파티 서비스로 전달하는 역할을 담당하는 서버&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;API 요청 플로우&lt;/h4&gt;
&lt;p&gt;위 컴포넌트들이 어떻게 협력하여 알림을 전송하게 될까?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API 를 호출하여 알림 서버로 알림을 보낸다.&lt;/li&gt;
&lt;li&gt;알림 서버는 사용자 정보, 단말 토큰, 알림 설정 같은 메타데이터를 캐시나 데이터베이스에서 가져온다.&lt;/li&gt;
&lt;li&gt;알림 서버는 전송할 알림 종류에 알맞는 이벤트를 만들어서 해당 이벤트를 위한 큐에 넣는다. 가령 ios 푸시 알림 이벤트는 ios 푸시 알림 큐에 넣어야 한다.&lt;/li&gt;
&lt;li&gt;작업 서버는 메시지 큐에서 알림 이벤트를 꺼내고, 서드파티 서비스로 전송한다.&lt;/li&gt;
&lt;li&gt;서드파티 서비스는 사용자 단말로 알림을 전송한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;3단계-상세-설계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3%EB%8B%A8%EA%B3%84-%EC%83%81%EC%84%B8-%EC%84%A4%EA%B3%84&quot; aria-label=&quot;3단계 상세 설계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3단계. 상세 설계&lt;/h2&gt;
&lt;p&gt;지금까지 개략적 설계를 진행하면서 알림의 종류, 연락처 정보 수집 절차, 그리고 알림 송수신 절차에 대해 알아보았다. 이제 아래 내용들을 더 자세히 알아보자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;안정성&lt;/li&gt;
&lt;li&gt;추가로 필요한 컴포넌트 및 고려사항 : 알림 템플릿, 알림 설정, 전송률 제한, 재시도 매커니즘, 보안, 큐에 보관된 알림에 대한 모니터링과 이벤트 추적 등&lt;/li&gt;
&lt;li&gt;개선된 설계안&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;안정성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%88%EC%A0%95%EC%84%B1&quot; aria-label=&quot;안정성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;안정성&lt;/h3&gt;
&lt;p&gt;분산 환경에서 운영될 알림 시스템을 설계할 때는 안정성을 확보하기 위한 사항 몇가지를 반드시 고려해야한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 데이터 손실 방지 : 알림이 소실되면 안된다. 알림이 지연되거나 순서가 틀려도 괜찮지만, 알림 자체가 어딘가에서 유실되면 곤란해진다. 이 요구사항을 만족하려면 알림 시스템은 알림 데이터를 데이터베이스에서 보관하고 재시도 메커니즘을 시도해야한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이를 위해 알림 로그 데이터베이스를 별도로 개설하는 것이 좋다. ios 단말을 예로들면, ios 푸시 알람 큐로 부터 작업 서버가 메시지를 전달받으면, 해당 메시지가 유실되는 일이 없도록 알림 로그 데이터베이스에 로그를 관리해야 할 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 알림 중복 전송 방지 : 같은 알림이 여러번 반복되는 것을 완전히 막는 것은 가능하지 않다. 대부분의 경우 알림은 단 1번만 전송되겠지만, 분산 시스템의 특성상 간혹 같은 알림이 중복되어 전송되기도 할 것이다. 그 빈도를 줄이려면 중복을 탐지하는 메커니즘을 도입하고, 오류를 신중히 처리해야 한다. 다음은 간단한 중복 방지 로직의 사례이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;보내야 할 알림이 도착하면 그 이벤트 ID 를 검사하여, 이전에 본 적이 있는 이벤트인지를 검사한다. 중복된 이벤트라면 버리고, 그렇지 않으면 알림을 발송한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;추가로-필요한-컴포넌트-및-고려사항&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B6%94%EA%B0%80%EB%A1%9C-%ED%95%84%EC%9A%94%ED%95%9C-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%B0%8F-%EA%B3%A0%EB%A0%A4%EC%82%AC%ED%95%AD&quot; aria-label=&quot;추가로 필요한 컴포넌트 및 고려사항 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;추가로 필요한 컴포넌트 및 고려사항&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;알림 설정 : 사용자는 이미 너무 많은 알림을 받고 있어서 쉽게 피곤함을 느낀다. 따라서 많은 웹사이트와 앱에서는 사용자가 알림 설정을 상세히 조정할 수 있도록 하고있다. 이 정보는 알림 설정 테이블에 보관되며, 이 테이블에는 아마 다음과 필드들이 필요할 것이다.
&lt;ul&gt;
&lt;li&gt;user_id&lt;/li&gt;
&lt;li&gt;channel (varchar) : 알림이 전송될 채널. 푸시 알림, 이메일, SMS 등&lt;/li&gt;
&lt;li&gt;opt_in (boolean) : 해당 채널로 알림을 받을 것인지의 여부&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이와 같은 설정을 도입한 뒤에는 특정 종류의 알림을 보내기 전에 반드시 해당 사용자가 해당 알림을 켜 두었는지 확인해야 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;전송률 제한 : 사용자에게 너무 많은 알림을 보내지 않도록 하는 1가지 방법은, 한 사용자가 받을 수 있는 알림의 빈도를 제한하는 것이다. 이것이 중요한 이유는, 알림을 너무 많이 보내기 시작하면 사용자가 알림 기능을 아예 꺼버릴 수도 있기 떄문이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;재시도 방법 : 서드파티 서비스가 알림 전송에 실패하면, 해당 알림을 재시도 전용 큐에 넣는다. 같은 문제가 계속해서 발생하면 개발자에게 통지(alert) 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;큐 모니터링 : 알림 시스템을 모니터링 할 때 중요한 메트릭(metric) 하나는 큐에 쌓인 알림의 개수이다. 이 수가 너무 크면 작업 서버들이 이벤트를 빠르게 처리하고 있지 못하다는 뜻이다. 그런 경우에는 작업 서버를 Scale Out 하는 것이 바람직할 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;개선된-최종-설계안&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%9C%EC%84%A0%EB%90%9C-%EC%B5%9C%EC%A2%85-%EC%84%A4%EA%B3%84%EC%95%88&quot; aria-label=&quot;개선된 최종 설계안 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;개선된 최종 설계안&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/459c0f251a9d144ce423c4c53d041897/c830c/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.668711656441715%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACDElEQVR42mVTa3OqUAz0//+mfuiXdq63tj4RHwgibxFQ5C26N4nFsbfM7AzkJHuSzdJr2xb/Y3+I4IcHVFWFMI4JCU7nM0r6jpKjoK5rXK/XX7W9y+WCDhzgQscP4BJiKtS2JvSdhQOR8kWqpmOyWMHbh6jqCs/1jF+Eyekk77fbTTrIyprQoGru53y2tR3soxhplhFpjfaZsGkaMC6EsixxTFPUHKPEvKyguTHGeoCNG0mMSVkOHjnLcxRUw0QdT48TOi1YoyzLwU9T5JguaDxFQXFOYdou0jhCkiQybl4USM8ZkRY/R+ZbuCsRlQL24QQjOGEf+FDWG7x+jLEyHQzmK5i7HQzSdKguZVmW52OoqCRTKk01TDiiw7+TmbQfRhEU3cLnXBPC6UrDh7KEZnuY0PtivYZFnX4pCxl7ulxjTPXcsRDyyJbrYalv5RbDsqkrDavNBp7nQdUN6I6PIEnxPpphRuOvqcPX/gAzukCn/E9ljjPJ9CBki3ASi3wgfTiJVowwoLhhwPYCHGnzL299DIYjscxwroqdmLQ/nkqnXP9YCoNJfGp9Trrxli+80apEUxZoshQ26TdTF9DMHf6MJmTuu9lZf56Of4IftmmvrRj6/WuEgPRhHzbfdqDbiMgi7VTExyMc6p4JOq8yHlt+/m344CYJvPH71iUu3gthOq74jp/O5IKnX+8fiB6NzXFgU9MAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/459c0f251a9d144ce423c4c53d041897/a6d36/image-3.png&quot;
        srcset=&quot;/static/459c0f251a9d144ce423c4c53d041897/222b7/image-3.png 163w,
/static/459c0f251a9d144ce423c4c53d041897/ff46a/image-3.png 325w,
/static/459c0f251a9d144ce423c4c53d041897/a6d36/image-3.png 650w,
/static/459c0f251a9d144ce423c4c53d041897/e548f/image-3.png 975w,
/static/459c0f251a9d144ce423c4c53d041897/3c492/image-3.png 1300w,
/static/459c0f251a9d144ce423c4c53d041897/c830c/image-3.png 1754w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;초안에 비해 많은 컴포넌트와 기능이 추가되었다. &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 알림 서버에 전송률 제한 기능이 추가 되었으며, 전송 실패에 대응하기 위해 재시도 기능이 추가되었다. &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 전송에 실패한 알림은 다시 큐에 너호 지정된 횟수만큼 재시도한다. &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 전송 템플릿을 사용하여 알림 생성 과정을 단순화하고 알림 내용의 일관성을 유지한다. &lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 모니터링과 추적 시스템을 추가하여 시스템 상태를 확인하고 추후 시스템을 개선하기 쉽도록 하였다.&lt;/p&gt;
&lt;h2 id=&quot;더-학습해볼-주제-및-궁금점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%EC%A3%BC%EC%A0%9C-%EB%B0%8F-%EA%B6%81%EA%B8%88%EC%A0%90&quot; aria-label=&quot;더 학습해볼 주제 및 궁금점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 주제 및 궁금점&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;메시지 전송 실패율을 낮추기 위해 재시도 메커니즘을 도입하는데, 이를 정확히 어떻게 구현할 수 있는가? (지금드는 해법은, Infrastructure 레이어에서 서드파티 호출시 예외가 터지면 @Retryable 로 재시도하는 것이다.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;왜 메시지 큐는 각 알림별로 도입해야하는가? 카프카처럼 중앙화된 메시지 큐 1대로 처리할 수는 없는가?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;전송률 제한은 구체적으로 어떻게 구현할 수 있는 것인가?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;모니터링 시스템을 통해 메트릭을 수집하는데, 이를 통해 어떻게 재시도 로직을 처리한다는 것인지 연관성에 대한 이해도가 더 필요하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;데이터베이스 서버는 다중화가 필요없는가?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[데이터베이스 분산 스토리지를 위한 파티셔닝과 샤딩]]></title><description><![CDATA[데이터 분산 환경의 필요성 높은 가용성과 확장성을 확보하기 위해 다양한 인덱스 생성, N+1 문제 해결등 여러 성능 튜닝을 시도할 수 있다. 하지만, 결국 단일 데이터베이스 하나에서 병목이 발생하면 시스템 전체에 성능 저하가 발생한다. 즉, SPOF…]]></description><link>https://haon.site/database/partitioning-sharding/</link><guid isPermaLink="false">https://haon.site/database/partitioning-sharding/</guid><pubDate>Thu, 21 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;데이터-분산-환경의-필요성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B6%84%EC%82%B0-%ED%99%98%EA%B2%BD%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1&quot; aria-label=&quot;데이터 분산 환경의 필요성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터 분산 환경의 필요성&lt;/h2&gt;
&lt;p&gt;높은 가용성과 확장성을 확보하기 위해 다양한 인덱스 생성, N+1 문제 해결등 여러 성능 튜닝을 시도할 수 있다. 하지만, 결국 단일 데이터베이스 하나에서 병목이 발생하면 시스템 전체에 성능 저하가 발생한다. 즉, &lt;strong&gt;SPOF(Single Point Of Failure)&lt;/strong&gt; 로 가 발생하여 &lt;strong&gt;HA(High Availability)&lt;/strong&gt; 를 확보할 수 없다. 이를위해 우리 모행 서비스에선 데이터베이스 레플리케이션을 통한 부하 분산 환경을 구축한 적이 있다. 이와 관련한 내용은 &lt;a href=&quot;https://haon.blog/database/replication-theory/&quot;&gt;고가용성과 확장성을 위한 데이터베이스 레플리케이션(DB Replication)&lt;/a&gt; 에서 다룬적이 있다.&lt;/p&gt;
&lt;p&gt;그런데, 이 레플리케이션 방법 외에도 다양한 방식으로 데이터를 분산 저장할 수 있다는 사실을 알게 되었다. 이번 포스팅에선 데이터를 분산화하여 저장하는 방법 중 파티셔닝과 샤딩에 대해 학습하도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;데이터베이스-파티셔닝&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%8C%8C%ED%8B%B0%EC%85%94%EB%8B%9D&quot; aria-label=&quot;데이터베이스 파티셔닝 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 파티셔닝&lt;/h2&gt;
&lt;p&gt;파티셔닝은 데이터베이스내의 &lt;strong&gt;큰 테이블이나 인덱스를 여러개의 작은 테이블(파티션) 으로 분할&lt;/strong&gt;하는 기법이다. 이때 큰 테이블 여러 작은 단위로 쪼갰을 때, 이 작은 하나의 단위를 &lt;strong&gt;파티션(Partition)&lt;/strong&gt; 이라고 한다.  큰 테이블을 여러 테이블로 나누어 저장하기 떄문에 쿼리 성능이 개선될 수 있다. 이때, 데이터는 물리적으로 여러 테이블에 분산하여 저장된다. 파티셔닝은 MySQL 에서 InnoDB 와 NDB 에서 지원하고 있지만, MyISAM 은 지원하지 않는다.&lt;/p&gt;
&lt;p&gt;파텨셔닝은 테이블을 수직 또는 수평으로 분할하는가에 따라 &lt;strong&gt;수직 파티셔닝&lt;/strong&gt;과 &lt;strong&gt;수평 파티셔닝&lt;/strong&gt;으로 구분된다. 테이블을 수직과 수평으로 분할한다는 것이 무슨 뜻일까?&lt;/p&gt;
&lt;h3 id=&quot;수직-파티셔닝-vertical-partitioning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%98%EC%A7%81-%ED%8C%8C%ED%8B%B0%EC%85%94%EB%8B%9D-vertical-partitioning&quot; aria-label=&quot;수직 파티셔닝 vertical partitioning permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;수직 파티셔닝 (Vertical Partitioning)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bd8d0e4d7c1d5d63f73ba3160d18ac38/71c1d/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.963190184049076%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABiElEQVR42p2TW2/TMBiG+/9/BDdcccEP4I6rSWNUWtkaWoTY2qRLUydxzk7iw4MbbRUIVd2wZdmy/T3+Dq9nXGjajpRasu+fGN1w6TqzcwfOd+M08+VXPn7+wPtP77i5vWa0Gufc24FHWJhtSbKE++Cezc9frBbfWEdr/5T9H6ChbEtUo3gMd+RJTraNEUXqceb1wK7rECJFZIJHsaPoCvbxHJmsEPuEtEtf5+FLXvJccre45cdqxcP6CitvYIhwhQ81D6bsYv0w42UPnbVYPWCzDU6G2HxOn4eYYTxisIdrhsMCU0uGhy80RcbYFN7QnAEaTV/lpELQNhW5rPgeBFRlQd/WjKolTVOapkbKnGWwpPSpQfcnbfwDtENL0ymGUVNVFZvtllb1E5C+pqkrhq5FtQ1Puwit/P6ozgCtQTcSVfmQfCjKe1uImN6vjR6x3rj1VX45k4fYz9nkyHnZOPv3OGbP2ZMCwihCpBmxr/jBq2EbRnRKnQo7441tEsOzIo6ASR1//JzfLualvBbzYSMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/bd8d0e4d7c1d5d63f73ba3160d18ac38/a6d36/image-4.png&quot;
        srcset=&quot;/static/bd8d0e4d7c1d5d63f73ba3160d18ac38/222b7/image-4.png 163w,
/static/bd8d0e4d7c1d5d63f73ba3160d18ac38/ff46a/image-4.png 325w,
/static/bd8d0e4d7c1d5d63f73ba3160d18ac38/a6d36/image-4.png 650w,
/static/bd8d0e4d7c1d5d63f73ba3160d18ac38/e548f/image-4.png 975w,
/static/bd8d0e4d7c1d5d63f73ba3160d18ac38/3c492/image-4.png 1300w,
/static/bd8d0e4d7c1d5d63f73ba3160d18ac38/71c1d/image-4.png 1536w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;수직 파티셔닝은 한 테이블을 수직 방향으로 쪼개어, 한 테이블의 각 행을 작은 테이블 여러개에 분산시키는 방법이다. 즉, 테이블을 컬럼 기준으로 분할하여 여러개의 작은 테이블로 분할한다.&lt;/p&gt;
&lt;h4&gt;장점&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;자주 사용하는 컬럼만으로 구성한 작은 테이블&lt;/strong&gt;을 만들어서 성능을 개선할 수 있다. 만약 한 테이블을 SELECT 하면 모든 컬럼들을 메모리에 올리게 될 것이다. 이 때문에 필요없는 컬럼까지 올라가서 한 번에 읽을 수 있는 ROW 의 수가 감소하게 된다. 따라서 수직 파티셔닝을 통해 필요한 컬럼만 올린다면, 훨씬 많은 수의 ROW 를 메모리에 올릴 수 있으니 성능상 이점이 된다. 또한 같은 타입의 데이터가 저장되기 떄문에 그룹화 되어서, 데이터의 응집성을 높일 수 있게된다.&lt;/p&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;p&gt;데이터를 찾는 과정이 복합해져서 &lt;strong&gt;Latency 가 증가한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;수평-파티셔닝-horizontal-partitioning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%98%ED%8F%89-%ED%8C%8C%ED%8B%B0%EC%85%94%EB%8B%9D-horizontal-partitioning&quot; aria-label=&quot;수평 파티셔닝 horizontal partitioning permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;수평 파티셔닝 (Horizontal Partitioning)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3d36497ede1baf955865df61bd8a1c46/e2d3b/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.09815950920245%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB0UlEQVR42q2TW2vbQBBG8/9/SaFPhVJoCbShodCYENf1rU5i1451sSxrdd+VtNrTjUycPLROGzowjGZnOfpmhj3hP9vJsaKqFAPviiv/grPFaZe/CNiaFmlKbu+uefvxDe+/vOP1h1d8m/bJ6xTdajD/AKxNhS88IhFZF/T7fbI8YzgeMVvOUOZBqTkObHWLiSUqFOyWDtKJEHOXn30L8QThbI1YeVRRDJGF1vovFbY1IgvJS0mUJEwnlxRKslgu8LdrKm1h9ueYIwq11oxGQ772LhkMv9MbDgjCOSr6TCl6lOE5ZSspTW05bdds5xZqnoAfFdqzUioS54Z8t6Vwz9HBGTTJvi5dCE6t/Gh/PbV5I/+gUNdU6Y62UZSrAdV2QRGMycI1pjWH4RsxplhfoMscMfxE4c8RG5fEv+sYB2BbxOzCkCSOmcxuWbt2ESuX+WyKUfmhBS0zPM8nTROG02sc12MymXD7Y3JQu1eoMmSeoooMZzknsS1ncUQS2LZ09TiVMradbKlzQRo4qDSkiLddfLi3B7Z2Y7bdzu8LTdV9m/vcLuCpbTYbHMchCIIu+r7ParWy85fPP73fWdM0KKWo67qLVVUhLez+/EXA597yL2HyPWcj02TZAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/3d36497ede1baf955865df61bd8a1c46/a6d36/image-5.png&quot;
        srcset=&quot;/static/3d36497ede1baf955865df61bd8a1c46/222b7/image-5.png 163w,
/static/3d36497ede1baf955865df61bd8a1c46/ff46a/image-5.png 325w,
/static/3d36497ede1baf955865df61bd8a1c46/a6d36/image-5.png 650w,
/static/3d36497ede1baf955865df61bd8a1c46/e548f/image-5.png 975w,
/static/3d36497ede1baf955865df61bd8a1c46/3c492/image-5.png 1300w,
/static/3d36497ede1baf955865df61bd8a1c46/e2d3b/image-5.png 1466w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;수평 파티셔닝은 말 그대로 수평으로 파티션을 생성한다. 즉, &lt;strong&gt;하나의 테이블의 각 행을 다른 테이블에 분산하는 방법&lt;/strong&gt;이다. 이 방법은 아래에서 학습해볼 &lt;strong&gt;샤딩(Sharding)&lt;/strong&gt; 과 비슷한 점이 많다. 스키마를 복제한 후 샤드키를 기준으로 데이터를 분할하는 방식으로 동작한다. 다시말해, 스키마가 같은 두 데이터를 여러 작은 테이블에 분산 저장한다.&lt;/p&gt;
&lt;p&gt;예를들어 위와 같은 User 테이블에서 유저 PK 를 샤드키로 사용하여 샤딩하기로 했다면, 0 ~ 999번 PK 를 가진 유저 정보는 샤드1에 저장하고, 1000 ~ 1999번 PK 에 대한 유저 정보는 샤드2에 저장한다.&lt;/p&gt;
&lt;h4&gt;장점&lt;/h4&gt;
&lt;p&gt;한 테이블의 데이터들을 &lt;code class=&quot;language-text&quot;&gt;개수&lt;/code&gt; 를 기준으로 나누어 분할하기 떄문에 데이터 개수가 작아진다. 따라서 인덱스의 개수도 작아지고, 성능이 개선된다.&lt;/p&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;p&gt;마찬가지로 데이터를 찾는 과정이 복합해져서 Latency 가 증가한다. 또한 여러 데이터베이스 서버들 중에 한 서버에 장애가 발생하면 데이터 무결성이 깨질 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;파티셔닝의-분할-기준범위&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%ED%8B%B0%EC%85%94%EB%8B%9D%EC%9D%98-%EB%B6%84%ED%95%A0-%EA%B8%B0%EC%A4%80%EB%B2%94%EC%9C%84&quot; aria-label=&quot;파티셔닝의 분할 기준범위 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파티셔닝의 분할 기준(범위)&lt;/h2&gt;
&lt;p&gt;DBMS 는 파티셔닝에 대해 다양한 데이터 분할 기법을 제공한다. 분할은 &lt;strong&gt;분할 키(Partitioning Key)&lt;/strong&gt; 를 사용한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e26bac50eb23d44704c96d15ccbeb0e6/82c1e/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.71165644171779%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAABhklEQVR42n2S266CMBBF+f+v0jdeSTRBEBUVLxHvoCLonKxJxvRw8DRpSlu6Zmbv8aQ1mqaR5/Mpm81G9vu9nt3vd0mSRI7Ho+7f77dkWfa5Z2/Dc2F1XUscx7JcLmW1WukD4GEYKsCAt9tNFouFBnVhncDRaCTb7VaBl8tFqqpSGI+LotAK2E+nU51lWX4Hks1gMNDolHg4HOTxeMh8Ppf1ei2n0+nzH8GZ7eG19aNMIMAoje/ZbKZZG/C/8QGiBZHNFFaTgXJ3u52cz2e9QwYCtfX7A+QRpqARj9w7nEbD8XgsURSpQV1Qz7UdkYMgULBlxzcyYFae5wpCUzJFa4J8BQ6HQ+n3+zKZTLRksgJEydYmtBQGUQVgOuErEId7vZ46/Hq91BTO0jTVIIAwBxCZE6Rt1C8gP/u+r6Ub0BwGaP3HShCAnRm6AzPMYaDsbSUwRlgPEpC7TiAP0AjRKYOVXmTFDIxhcubuKd3N0nObGseY1+tVJz/aNz1oZwRkZXJO1gb8AQnNNakV2iMZAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e26bac50eb23d44704c96d15ccbeb0e6/a6d36/image-6.png&quot;
        srcset=&quot;/static/e26bac50eb23d44704c96d15ccbeb0e6/222b7/image-6.png 163w,
/static/e26bac50eb23d44704c96d15ccbeb0e6/ff46a/image-6.png 325w,
/static/e26bac50eb23d44704c96d15ccbeb0e6/a6d36/image-6.png 650w,
/static/e26bac50eb23d44704c96d15ccbeb0e6/e548f/image-6.png 975w,
/static/e26bac50eb23d44704c96d15ccbeb0e6/3c492/image-6.png 1300w,
/static/e26bac50eb23d44704c96d15ccbeb0e6/82c1e/image-6.png 1398w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;list-partitioning-목록-분할&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#list-partitioning-%EB%AA%A9%EB%A1%9D-%EB%B6%84%ED%95%A0&quot; aria-label=&quot;list partitioning 목록 분할 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;List Partitioning (목록 분할)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;데이터 값이 특정 목록에 포함된 경우&lt;/strong&gt; 데이터를 분리한다. 분포도가 비슷하며, 많은 SQL 에서 해당 컬럼에 조건이 많이 들어오는 경우에 유용하게 사용된다. 위처럼 특정 지역별로 데이터를 분할할 때 사용할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;range-partitioning-범위-분할&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#range-partitioning-%EB%B2%94%EC%9C%84-%EB%B6%84%ED%95%A0&quot; aria-label=&quot;range partitioning 범위 분할 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Range Partitioning (범위 분할)&lt;/h3&gt;
&lt;p&gt;데이터를 특정 범위를 기준으로 분할할 떄 사용한다. 연속적인 숫자나 날짜를 기준으로 파티셔닝 하는 방식이다. 위처럼 1 ~ 2월, 3 ~ 4월, 5 ~ 6월, .. 으로 데이터를 분리할 떄 사용할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;hash-partitioning-해시-분할&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hash-partitioning-%ED%95%B4%EC%8B%9C-%EB%B6%84%ED%95%A0&quot; aria-label=&quot;hash partitioning 해시 분할 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Hash Partitioning (해시 분할)&lt;/h3&gt;
&lt;p&gt;해시 함수를 사용하여 데이터를 분할하는 방식이다. &lt;strong&gt;특정 컬럼의 값을 해싱하여 저장할 파티션을 선택&lt;/strong&gt;한다. 데이터의 균등 분할하고 싶을 떄 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;공식문서에 따르면, 여러 컬럼으로 해싱하는 이 방법은 그리 권장하지 않는 방법이라고 한다. 만약 이 방법을 활용한다면, 유저 PK 처럼 유일성이 높고 데이터 분포가 고른 컬럼을 파티션 키로 선정해야 효과적이다.&lt;/p&gt;
&lt;h3 id=&quot;composite-partitioning-합성-분할&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#composite-partitioning-%ED%95%A9%EC%84%B1-%EB%B6%84%ED%95%A0&quot; aria-label=&quot;composite partitioning 합성 분할 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Composite Partitioning (합성 분할)&lt;/h3&gt;
&lt;p&gt;위 파티셔닝 기법들 중에 여러 방식을 혼합하여 사용하는 방식이다. 단, 파티션을 나누는 기준이 너무 많아지면 파티션 개수가 너무 많아지고, 인덱스의 경합이 너무 심해져서 잘 사용하지 않는 방법이라고 한다.&lt;/p&gt;
&lt;h2 id=&quot;데이터베이스-샤딩&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%83%A4%EB%94%A9&quot; aria-label=&quot;데이터베이스 샤딩 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 샤딩&lt;/h2&gt;
&lt;p&gt;샤딩은 동일한 스키마를 가지고 있는 여러 데이터베이스 서버들에 데이터를 &lt;strong&gt;작은 단위로 나누어 분산 저장하는 기법&lt;/strong&gt;이다. 이때, 작은 단위를 &lt;strong&gt;샤드(Shared)&lt;/strong&gt; 라고 한다. 어떻게 보면 샤딩은 수평 파티셔닝의 일종이다. 그러나 차이점은, 수평 파티셔닝의 경우 데이터를 동일한 서버에 저장하지만 샤딩은 서로 다른 서버에 분산하여 저장한다는 점이다. 따라서 쿼리 성능 개선 뿐만 아니라 부하 분산되는 효과도 얻을 수 있다. 즉, 샤딩은 데이터베이스의 &lt;strong&gt;Scale-Out&lt;/strong&gt; 이다.&lt;/p&gt;
&lt;p&gt;샤딩은 물리적으로 분산된 환경에서 사용되는 기법으로, 데이터베이스 차원이 아닌 애플리케이션 레벨에서 구현하는 것이 일반적이다. 다만, 샤딩을 플랫폼 차원에서 제공하는 시도가 많다고 한다.&lt;/p&gt;
&lt;h3 id=&quot;샤드-키-shared-key&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%A4%EB%93%9C-%ED%82%A4-shared-key&quot; aria-label=&quot;샤드 키 shared key permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;샤드 키 (Shared Key)&lt;/h3&gt;
&lt;p&gt;샤드 키란 분할 된 여러 샤드중에 어떤 샤드를 선택할지 결정할 떄 사용하는 Key 값이다. 또한 이 샤드키를 어떤 방식으로 생성할지에 따라서 샤딩의 방법이 구분된다. 샤딩의 종류에는 &lt;strong&gt;해시 샤딩(Hash Sharding), 동적 샤딩(Dynamic Sharding), 엔티티 그룹(Entity Group)&lt;/strong&gt; 등이 있다.&lt;/p&gt;
&lt;h3 id=&quot;hash-sharding&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hash-sharding&quot; aria-label=&quot;hash sharding permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Hash Sharding&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1b87e660e94904c08369dff7cc7194d5/21482/image-7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.89570552147239%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA+ElEQVR42p2T6w6CMAyFef9H1F9GTVQMdxi3rfLV1AgGoi45aVfOTruuRLKxQghq0zSVoihmsbUVrX0YhkHyPJckSSS+xWpBlmUyjuPvghyq61qrw7ZtqwnKsvxPkAo5jGBVVdI0jVbI/i9B7710XfcBKuXbV4I0HLJhCojrveyPsewON/Wn4JyzJcg1nXMv61wjrhvkfE3kdLmr/4y7GXdVsO97xdJfJt3ivATpDyPBvNEnLK+KQPBB28FhYvDg82j476IzQQi8JoK8LIBs/TIOcTiME/sPQcu+BsYE2HXfQRKs/UGRDTGZluDaVGO+VYRv1lpjs/kAwSevGOPoZKEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/1b87e660e94904c08369dff7cc7194d5/a6d36/image-7.png&quot;
        srcset=&quot;/static/1b87e660e94904c08369dff7cc7194d5/222b7/image-7.png 163w,
/static/1b87e660e94904c08369dff7cc7194d5/ff46a/image-7.png 325w,
/static/1b87e660e94904c08369dff7cc7194d5/a6d36/image-7.png 650w,
/static/1b87e660e94904c08369dff7cc7194d5/e548f/image-7.png 975w,
/static/1b87e660e94904c08369dff7cc7194d5/3c492/image-7.png 1300w,
/static/1b87e660e94904c08369dff7cc7194d5/21482/image-7.png 1350w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Hash Sharding 은 해시함수를 사용한 방식으로, PK 값을 나머지 연산하여 그 결과값으로 어떤 샤드에 들어갈지 결정하는 방식이다. 이 방식은 구현 자체가 샤드의 수만큼 해싱을 하면 되기 떄문에 간단하다.&lt;/p&gt;
&lt;p&gt;다만, &lt;strong&gt;총 데이터베이스 수가 고정적으로 정해져있을 때만 유용&lt;/strong&gt;하다. 데이터베이스 개수가 줄어들거나 늘어나면 해시 함수내의 modular 값도 변경해야한다. 따라서 데이터의 재정렬이 필요하다.&lt;/p&gt;
&lt;h3 id=&quot;range-sharding-dynamic-sharding&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#range-sharding-dynamic-sharding&quot; aria-label=&quot;range sharding dynamic sharding permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Range Sharding (Dynamic Sharding)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4043e50e5e3dc94b4ffd5d3ddd8bb752/e40ed/image-8.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 73.61963190184049%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAABOUlEQVR42qWT2YqDYAyFff/388IrQXBFpVr3LcP3wxFLZ6bDWEjTbCcnf1LPPnyez6elaWpJkljf95/SzfstuCyLlWVpvu9bXdcWBIGN4/h/wHmeHUCWZXYch9PTNN0DZMw8zx3boijuAz4eDwvD0OZptjiO742877tt2+ZYAQRLfH8GpHhd1xcBQKPzjt/FfwQUCxLR+o0fwKtfzGn2BkgQaZrGaXWFEUKhblAxMW/b9nwaB4gxDIM7XoJsEmDdYVVVbsscOBqWNGFZnFHXda6GKfB7IGMAIiYkXc8GmxgNaUIhDRByiL8BkkzRdTySYY8NkLQAAcKm/gRkZBL5i5EEMFpLYkSdDQ0QPZNqtTgHyBcBQKIochpbfhjwVrwxwPgkvC/HLv+5ZQzo000sxVR+afkl14kA/AJEaJdq6eHMwAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/4043e50e5e3dc94b4ffd5d3ddd8bb752/a6d36/image-8.png&quot;
        srcset=&quot;/static/4043e50e5e3dc94b4ffd5d3ddd8bb752/222b7/image-8.png 163w,
/static/4043e50e5e3dc94b4ffd5d3ddd8bb752/ff46a/image-8.png 325w,
/static/4043e50e5e3dc94b4ffd5d3ddd8bb752/a6d36/image-8.png 650w,
/static/4043e50e5e3dc94b4ffd5d3ddd8bb752/e548f/image-8.png 975w,
/static/4043e50e5e3dc94b4ffd5d3ddd8bb752/3c492/image-8.png 1300w,
/static/4043e50e5e3dc94b4ffd5d3ddd8bb752/e40ed/image-8.png 1378w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Range Sharding 은 Dynamic Sharding 이라고도 불린다. 이 샤딩은 PK 를 범위로 지정하여 샤드를 결정하는 방식이다. 예를들어 PK 값이 1 ~ 999 번인 데이터라면 1번 샤드에 저장하며, 1000 ~ 1999번인 데이터는 2번 샤드에 저장한다. Hash Sharding 대비 &lt;strong&gt;데이터베이스 증설 작업에 큰 비용이 소모되지 않는다.&lt;/strong&gt; 따라서 자주 데이터베이스가 증감할 수 있는 상황에선 Range Sharding 을 사용하는 것이 유리하다. 이 때문에 Range Sharding 은 Dynamic Sharding 이라고도 불린다.&lt;/p&gt;
&lt;p&gt;대신 단점은, &lt;strong&gt;특정 샤드에만 부하가 몰릴 위험이 있다.&lt;/strong&gt; 예를들어 최근 게시글 조회 API 가 있다고 해보자. 최근에 작성된 게시글은 마지막 샤드에만 위치하게 될텐데, 최근 게시들을 조회하기 위해 많은 사용자들의 요청이 마지막 샤드에만 몰릴 수 있다. 따라서 부하 분산을 위해 데이터가 몰리는 DB는 다시 재 샤딩(re-sharding)하고, 트래픽이 저조한 데이터베이스는 다시 통합하는 작업이 필요할 것이다.&lt;/p&gt;
&lt;h3 id=&quot;entity-group&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#entity-group&quot; aria-label=&quot;entity group permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Entity Group&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/febdc903b622f7ec3590303514994d0d/248b0/image-9.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.920245398773%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAACdElEQVR42l2S2U9aYRDF+X/bmvbBpH1oDGqt1bba1riWRIwat7pUUNxArRsKwmW5XJDlwmXVaosoKoK/fhcf2vRLzjeZTObkzMwx1Go1iqUSF5clCoUC+WyWXDpNPpOhkMtxen5OsVzm5rrEr5+n5PJ5Mtk86UyObC7Pqei5Kl5wVbqk9vCA4b5aRfF6UGamCH0dRO75gvKpC6W7C7m3h9CwiZh9DcuOzOc5H+2THozjEk3jPoxjXjqmvQxZ/bgkBahhqIngsiwSNr5GbW0mZmzipMVIuK2VaMc7op3tHHd38nJohycmhaemoIgBnpskGs1+Xo3KPBs4YnrlEP0ZquKT1teIDPSh9PehdX8g0d+L5HJzsLmJx+nEa1mga+KAtzOyQJDGCZX5rQB7B4ds7znZ2HWztC0JJjFyVczt3XTg9wfwp1LIC/MEP76v71ANhzlNa3isFmz2feKRoIBM47AH21EMLZ0lqmq4AzEWHcd/FXqEwsSWg6zXS9g8jE9XGY6wZbOhhkJI1kXGrIfsBjLYXTFemNxYdxUOXR62949w+v4nXF1BaTOSfNNMXiAljpJesRGZ/UZ0yYJ3aowW857YnUyD+YSGkTiDqzFmtqOMrwUZth6zaHc9Et7dVdCSSTS/jxOHHUUQhOZmkacmiQhlwWUrmYiCEtdYd4b4/kNmciPAyLKP0RU/c1shdiUVNaVRFY4x3N7ekhXe05VeVSoUb27q+C1wLTwqhRSx/AMc9g0S0TC1SpnbqyJ35SKV8iXVu2txi3vywp8V0W/QZepJIh4npSv9B3qeVNV6LZ5ICKgkkymSKR3aYxS5Xjs7O6uP/AfM3apZ4NkcUQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/febdc903b622f7ec3590303514994d0d/a6d36/image-9.png&quot;
        srcset=&quot;/static/febdc903b622f7ec3590303514994d0d/222b7/image-9.png 163w,
/static/febdc903b622f7ec3590303514994d0d/ff46a/image-9.png 325w,
/static/febdc903b622f7ec3590303514994d0d/a6d36/image-9.png 650w,
/static/febdc903b622f7ec3590303514994d0d/e548f/image-9.png 975w,
/static/febdc903b622f7ec3590303514994d0d/3c492/image-9.png 1300w,
/static/febdc903b622f7ec3590303514994d0d/248b0/image-9.png 1316w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위 샤딩 기법들이 key-value 타입의 NoSQL 에 더 적합한 방식이라면, 이 방식은 RDBMS 에서 더 유리한 방식으로 쓰인다. &lt;strong&gt;관계가 매핑되어 있는 엔티티들을 같은 샤드에 저장하도록 만든 방식이다.&lt;/strong&gt; 따라서 유저가 작성한 게시글이나 댓글을 같은 샤드에서 가지고 있도록 작성할 수 있다.&lt;/p&gt;
&lt;p&gt;따라서, 한 샤드내에서 강한 응집도를 가질 수 있다. 그 덕분에 동일한 그룹에 대한 SQL 문에 대해서 매우 빠른 응답 속도를 받을 수 있다. 하지만 다른 그룹에 대한 SQL 문에 대해서는 매우 비효율적인 방식이 될 것이다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://techblog.woowahan.com/2687/&quot;&gt;https://techblog.woowahan.com/2687/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aiday.tistory.com/123&quot;&gt;https://aiday.tistory.com/123&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://akku-dev.tistory.com/68&quot;&gt;https://akku-dev.tistory.com/68&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[[가상 면접 사례로 배우는 대규모 시스템 설계 기초] 제 7장. 분산 시스템을 위한 유일 ID 생성기 설계]]></title><description><![CDATA[이번 장에서는 분산 시스템에서 사용될 유일 ID 생성기를 설계해 볼 것이다. 아마, "auto increment…]]></description><link>https://haon.site/virtual-interview/chap07/</link><guid isPermaLink="false">https://haon.site/virtual-interview/chap07/</guid><pubDate>Thu, 21 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이번 장에서는 분산 시스템에서 사용될 유일 ID 생성기를 설계해 볼 것이다. 아마, &quot;auto increment 속성이 설정된 관계형 데이터베이스의 기본 키를 쓰면 되지 않을까? 라고 생각할 수 있다. 하지만 분산 환경에서 이 접근법을 통하지 않을 것이다. 데이터베이스 서버 1대로는 그 요구를 충족하지 못할 뿐더러, 여러 데이터베이스 서버를 쓰는 경우에는 지연시간을 낮추기가 무척이나 힘들기 때문이다.&lt;/p&gt;
&lt;h2 id=&quot;1단계-문제-이해-및-설계-범위-확정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1%EB%8B%A8%EA%B3%84-%EB%AC%B8%EC%A0%9C-%EC%9D%B4%ED%95%B4-%EB%B0%8F-%EC%84%A4%EA%B3%84-%EB%B2%94%EC%9C%84-%ED%99%95%EC%A0%95&quot; aria-label=&quot;1단계 문제 이해 및 설계 범위 확정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1단계. 문제 이해 및 설계 범위 확정&lt;/h2&gt;
&lt;p&gt;시스템 설계 면접 문제를 푸는 첫 단계는 &lt;strong&gt;적절한 질문을 통해 모호함을 없애고 설계 방향을 정하는 것이다.&lt;/strong&gt; 아래는 면접관과 지원자 사이에 오갈 수 있는 질문과 답변의 예시이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;지원자 : ID 는 어떠한 특성을 갖는가?&lt;/li&gt;
&lt;li&gt;면접관 : ID 는 유일해야 하고, 정렬 가능해야 한다.&lt;/li&gt;
&lt;li&gt;지원자 : 새로운 레코드에 붙일 ID 는 항상 1만큼 큰 값이어야 하는가?&lt;/li&gt;
&lt;li&gt;면접관 : ID 의 값은 시간이 흐름에 따라 커질 테지만, 언제나 1씩 증가한다고 할 수는 없다. 다만 확실한 것은, 아침에 만든 ID 보다는 저녁에 만든 ID 가 큰 값을 갖는다는 특징이다.&lt;/li&gt;
&lt;li&gt;지원자 : ID 는 숫자로만 구성되는가?&lt;/li&gt;
&lt;li&gt;면접과 : 그렇다.&lt;/li&gt;
&lt;li&gt;지원자 : 시스템 규모는 어느 정도인가?&lt;/li&gt;
&lt;li&gt;면접관 : 초당 10,000 ID 를 생성할 수 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;질문을 할 때는 요구사항을 이해하고 모호함을 해소하는 데 초점을 맞추어야 한다. 위와 같은 질문을 통해, 이번 문제에 대한 답안이 만족해야 할 요구사항을 정리해보면 아래와 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;ID 는 유일해야 한다.&lt;/li&gt;
&lt;li&gt;ID 는 숫자로만 구성되어야 한다.&lt;/li&gt;
&lt;li&gt;ID 는 64비트로 표현될 수 있는 값이어야 한다.&lt;/li&gt;
&lt;li&gt;ID 는 발급 날짜에 따라 정렬 가능해야 한다.&lt;/li&gt;
&lt;li&gt;초당 10,000개의 ID 를 만들 수 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;2단계-개략적-설계안-제시-및-동의-구하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2%EB%8B%A8%EA%B3%84-%EA%B0%9C%EB%9E%B5%EC%A0%81-%EC%84%A4%EA%B3%84%EC%95%88-%EC%A0%9C%EC%8B%9C-%EB%B0%8F-%EB%8F%99%EC%9D%98-%EA%B5%AC%ED%95%98%EA%B8%B0&quot; aria-label=&quot;2단계 개략적 설계안 제시 및 동의 구하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2단계. 개략적 설계안 제시 및 동의 구하기&lt;/h2&gt;
&lt;p&gt;분산 시스템에서 유일성이 보장되는 ID 를 만드는 방법에는 여러 가지가 있다. 우리는 다음과 같은 선택지를 살펴볼 것이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;다중 마스터 복제&lt;/li&gt;
&lt;li&gt;UUID&lt;/li&gt;
&lt;li&gt;티켓 서버&lt;/li&gt;
&lt;li&gt;트위터 스노플레이크 접근법&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;각각의 동작 원리와 장단점을 살펴보도록 하자.&lt;/p&gt;
&lt;h3 id=&quot;설계안1---다중-마스터-복제-multi-master-replication&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%A4%EA%B3%84%EC%95%881---%EB%8B%A4%EC%A4%91-%EB%A7%88%EC%8A%A4%ED%84%B0-%EB%B3%B5%EC%A0%9C-multi-master-replication&quot; aria-label=&quot;설계안1   다중 마스터 복제 multi master replication permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;설계안1 - 다중 마스터 복제 (Multi Master Replication)&lt;/h3&gt;
&lt;p&gt;다중 데이터베이스 서버 환경에서 auto increment 를 사용하는 방법이다. 단, auto increment 의 증가량이 1이 아니라 k인 점이 일반적인 방식과 차이가 있다. k는 총 데이터베이스의 서버 대수이다.&lt;/p&gt;
&lt;p&gt;예를들어 데이터베이스가 총 4대가 있다고 해보자. 1번 데이터베이스는 ID 값이 1, 5, 9, 13, ... 으로, 2번 데이터베이스는 2, 6, 10, 14, ... 으로, 3번 데이터베이스는 3, 7, 11, 15, ... 처럼 증가하도록 설계하면 된다.&lt;/p&gt;
&lt;p&gt;이 방법은 다중 시스템에서 유일한 ID 를 생성할 수 있으며, 데이터베이스 수를 늘려 초당 생성할 수 있는 ID 수를 늘릴 수 있다. 하지만, 아래의 단점이 존재한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;우선 확장성에 불리하다.&lt;/strong&gt; 데이터베이스 수가 늘거나 줄게되면 중간에 k 값이 변경되는데, 이에 대응하는 것이 어렵다. 이미 과거의 k 값을 기준으로 생성된 ID 와 중간부터 생성될 ID 를 생각하면 확장성이 쉽지 않아 보인다. 이런 특징으로 여러 데이터 센터에 걸쳐 규모를 확장하기 어려울 것이다. 즉, 데이터베이스 수가 끝까지 최초 k 로 고정된 상황에서 쓰기 적합해보이는데, 이는 데이터베이스 수가 무조건 항상 고정되어야 한다는 특징이 보장되어야 하므로 확장성에 불리하다.&lt;/p&gt;
&lt;p&gt;두번째로는 &lt;strong&gt;시간 흐름에 맞게 커짐을 보장하지 않는다&lt;/strong&gt;는 점이다. 요구사항 중 &quot;ID 는 발급 날짜에 따라 정렬 가능해야 한다&quot; 가 있었다. 그런데, 이 방법에서는 2번 ID 가 1번 ID 보다 먼저 생성될 수 있다. 1번 데이터베이스보다 2번 데이터베이스에서 먼저 요청이 실행되면 발생하는 문제이다. 즉, ID 가 시간적 순서에 따라 정렬되지 않으므로 요구사항을 위반하게 된다.&lt;/p&gt;
&lt;h3 id=&quot;설계안2---uuid&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%A4%EA%B3%84%EC%95%882---uuid&quot; aria-label=&quot;설계안2   uuid permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;설계안2 - UUID&lt;/h3&gt;
&lt;p&gt;UUI 는 정보를 유일허게 식별하기 위한 128bit 짜리 16진수이다. UUID 는 충돌 가능성이 아예 0% 라고는 못하지만, 사실상 충돌할 일이 없다고 보는것이 맞다. 만약 UUID 가 충돌 가능성이 50% 가 되기 위해선, 100년동안 초당 10억개의 ID 를 생성해내면 50% 가 된다고 한다. 즉, UUID 는 사실상 충돌할 일이 없다.&lt;/p&gt;
&lt;p&gt;이러한 특징으로 보아, UUID 를 유일 키로 사용하명 어떨까? 서버간 조율 과정도 필요 없으므로 동기화가 필요 없고, 각 서버는 자기가 알아서 쓸 ID 를 만드는 구조이므로 규모 확장이 쉽다.&lt;/p&gt;
&lt;p&gt;하지만, ID 는 128로 길다. 앞선 요구사항을 보면 최대 64비트로 제한된다. 또한 숫자로만 구성되어 있지도 않으며, 발급 날짜에 따라 정렬이 불가능하다.따라서 문제를 해결할 수 없다. 몰론 이러한 요구사항 및 제약사항이 없었더라면 UUID 는 꽤 괜찮은 선택지가 되었을 것이다.&lt;/p&gt;
&lt;h3 id=&quot;설계안3---티켓-서버&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%A4%EA%B3%84%EC%95%883---%ED%8B%B0%EC%BC%93-%EC%84%9C%EB%B2%84&quot; aria-label=&quot;설계안3   티켓 서버 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;설계안3 - 티켓 서버&lt;/h3&gt;
&lt;p&gt;아예 유일한 키를 생성하는 서버를 별도로 분리하는건 어떠할까? 플리커(Flicker) 라는 회사는 분산 기본 키(Distributed Primary Key) 를 생성하기 위한 방법으로 이를 택했다고 한다.&lt;/p&gt;
&lt;p&gt;웹 서버 여러대가 auto increment 로 ID 를 생성하는 &lt;strong&gt;티켓 서버를 중앙 집중형으로 하나만 사용&lt;/strong&gt;하는 것이다. 이 방법은 유일성이 보장되고, 숫자로만 구성된, 64비트로 표현 가능한, 발급 날짜에 따라 정렬 가능한 ID 를 생성할 수 있다. 성능만 갖춰진다면 초당 10,000개의 ID 를 생성할 수 있을 것이다. 구현도 굉장히 쉬울 것이다.&lt;/p&gt;
&lt;p&gt;하지만, 이 방법은 &lt;strong&gt;SPOF(Single Point Of Failure)&lt;/strong&gt; 가 된다. 티컷 서버를 중앙 집중형으로 단 1대만 사용한다. 따라서 티켓 서버 1대에서 장애가 발생하면 모든 서비스를 이용할 수 없게 된다. 이를 해결한다고 티켓 서버를 다중화해버리면, 애당초 해결하고자 했던 동기화 이슈가 다시 발생한다.&lt;/p&gt;
&lt;h3 id=&quot;설계안4---트위터-스로우플레이느-접근법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%A4%EA%B3%84%EC%95%884---%ED%8A%B8%EC%9C%84%ED%84%B0-%EC%8A%A4%EB%A1%9C%EC%9A%B0%ED%94%8C%EB%A0%88%EC%9D%B4%EB%8A%90-%EC%A0%91%EA%B7%BC%EB%B2%95&quot; aria-label=&quot;설계안4   트위터 스로우플레이느 접근법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;설계안4 - 트위터 스로우플레이느 접근법&lt;/h3&gt;
&lt;p&gt;앞선 방법들은 모두 요구사항을 충족하지 못한다. 트위터는 이 문제를 해결하기 위해 스노우플레이크라고 부르는 독자적인 ID 생성 기법을 만들어 해결했다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/56aae179b76dda9e6b99c9bdb5a4b6f4/7a18f/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.877300613496935%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAyklEQVR42pVS2QrDMAzL/39iL0rpQ19636eGBC5hdGwzmPhUbCVuWRZ0XYdfpW1b1HWNcRzlb9uGsixvDHccB+Z5xj+yrivO87x9YjAmwOu60DSNgrRN931XEW02m07TpOloM2eA3FSAnC7PcwRBgDAMdSZJgjiO5dNmjBpFEbIsU64oCvXxTNNUdVVVwXESu3UYBiltcmJc+XEOYD4BLN/3vaZ0n3j6xi1XtjV9eQQkNz4vxtU7oD3EV0Bbg2vzwUjLUw2/EC/2AV/nQ3BS2r3DRwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/56aae179b76dda9e6b99c9bdb5a4b6f4/a6d36/image.png&quot;
        srcset=&quot;/static/56aae179b76dda9e6b99c9bdb5a4b6f4/222b7/image.png 163w,
/static/56aae179b76dda9e6b99c9bdb5a4b6f4/ff46a/image.png 325w,
/static/56aae179b76dda9e6b99c9bdb5a4b6f4/a6d36/image.png 650w,
/static/56aae179b76dda9e6b99c9bdb5a4b6f4/e548f/image.png 975w,
/static/56aae179b76dda9e6b99c9bdb5a4b6f4/7a18f/image.png 1284w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;ID 는 총 5개의 절(section) 으로 나뉜다. 절 순서대로 간략히 설명해보자면 아래와 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 사인(sign) 비트 : 1비트를 할당한다. 현재는 사용되지 않는다. 나중을 위해 확보해둔 비트로, 임수롸 양수를 구변하는데 사용할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 타임스탬프(timestamp) : 41비트를 할당한다. Epoch Time 이후로 몇 밀리세컨드가 경과했는지 나타낸다. 41비트이므로 최대 69년의 시간을 표현할 수 있다. 일반적으로 Epoch Time 은 1970년 01월 01일 00시 00분 00초를 뜻한다. 그런데 트위터는 더 오래 이 ID 체계를 사용하기 위해 Epoch Time 으로 기원시각(297,616,116,518) 으로 보정해여 사용한다고 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 데이터센터 ID : 5비트를 할당한다. 2^5, 즉 32개의 데이터 센터를 사용할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 서버 ID : 5비트를 할당한다. 2^5, 즉 데이터 센터별 32개의 서버를 사용할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; 일련번호 : 12비트를 할당한다. 각 서버는 ID 를 생성할 때 마다 이 일련번호를 1씩 증가시킨다. 이 값은 1ms 가 경과할 때 마다 0으로 초기화된다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이는 요구사항을 모두 충족할까? 우선, ID 에 타임 스탬프 값을 넣어두기 때문에 시간순으로 정렬이 가능하다. 또한 데이터 센터 ID 와 서버 ID 를 사용하여 서로 다른 서버에서 같은 시각에 생성한 ID 도 충돌이 발생하지 않는다. 또한 내부적으로 일렵번호를 사용하여 같은 서버에서 생성한 ID 값 끼리도 충돌이 발생하지 않는다. 이 과정은 서버간 동기화가 필요 없기 때문에, 확장성 측면에서도 유리하다. ID 전체 길이도 딱 64비트라서 모든 요구사항을 만족한다.&lt;/p&gt;
&lt;h3 id=&quot;4단계-마무리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4%EB%8B%A8%EA%B3%84-%EB%A7%88%EB%AC%B4%EB%A6%AC&quot; aria-label=&quot;4단계 마무리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4단계. 마무리&lt;/h3&gt;
&lt;p&gt;이번 장에서는 유일성이 보장되는 ID 생성기 구현에 쓰일 수 있는 다양한 전략 4가지를 학습했다. 마무리 단계에선 면접관과 함께 아래와 같은 추가 논의를 진행해볼 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;시계 동기화 : 이번 설계를 진행하면서 우리는 ID 생성 서버들이 모두 같은 시계를 사용한다고 가정했다. 하지만 이런 가정은 하나의 서버가 여러 코어에서 실행될 경우 유효하지 않을 수 있다. 여러 서버가 물리적으로 독립된 여러 장비에서 실행되는 경우에도 마찬가지이다. (시계 동기화와 관련한 추가 학습이 필요하다.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;각 절(section) 의 길이 최적화 : 예를들어 동시성이 낮고 수명이 긴 애플리케이션이라면 일련번호 절의 길이를 줄이고 타임스탬프 절의 길이를 늘리는 것이 효과적일 수도 있을 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;고가용성(HA) : ID 생성기는 필수 불가결 컴포넌트이므로 아주 높은 가용성을 제공해야 할 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;생각해보면, 지금 모행 서비스에서 데이터베이스 레플리케이션을 도입하고 있는데 다중 데이터베이스 서버간의 유일 ID 생성과 관련해 생각하지 못했었다. 우리 서비스에서도 이와 관련한 추가 도입이 필요할 듯 하다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Redis Pub/Sub 을 사용한 분산 환경에서 로컬 캐시 동기화]]></title><description><![CDATA[💡 현재 포스트는 하모니 팀 기술 블로그 에 게시된 글 입니다. 분산 환경에서 로컬 캐시 동기화 문제 지난 스프링 로컬 캐시와 TTL 을 구현한 외부 서빙 API…]]></description><link>https://haon.site/spring/redis-pub-sub-local-cache-synchornization/</link><guid isPermaLink="false">https://haon.site/spring/redis-pub-sub-local-cache-synchornization/</guid><pubDate>Wed, 20 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/redis-pub-sub-local-cache-synchornization/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;분산-환경에서-로컬-캐시-동기화-문제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%84%EC%82%B0-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EB%A1%9C%EC%BB%AC-%EC%BA%90%EC%8B%9C-%EB%8F%99%EA%B8%B0%ED%99%94-%EB%AC%B8%EC%A0%9C&quot; aria-label=&quot;분산 환경에서 로컬 캐시 동기화 문제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;분산 환경에서 로컬 캐시 동기화 문제&lt;/h2&gt;
&lt;p&gt;지난 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/local-cache-custom/&quot;&gt;스프링 로컬 캐시와 TTL 을 구현한 외부 서빙 API 요청 최적화&lt;/a&gt; 에서 다루었듯이, 우리 모행 서비스에는 로컬 캐시를 사용하여 외부 서드파티 API 호출에 대한 응답을 캐싱해두고 재사용한다. 지금까지는 단일 서버로 동작하기 떄문에 캐싱에 별 문제가 없어보인다. 그런데 만약, 우리 서비스가 향후 Scale-Out된 분산 환경이라면 로컬 캐시에 어떤 문제가 발생할까?&lt;/p&gt;
&lt;p&gt;일반적으로 로컬 캐시는 구현이 간편하다. 스프링에서 제공해주는 &lt;code class=&quot;language-text&quot;&gt;@Cacheable&lt;/code&gt; 어노테이션만 간단히 명시하고, 기본 캐시 매니저인 &lt;code class=&quot;language-text&quot;&gt;ConcurrentMapCacheManager&lt;/code&gt; 를 제공받을 수 있다. 하지만, 만약 &lt;strong&gt;Scale-Out 된 분산 환경&lt;/strong&gt;이라면 각 애플리케이션 서버가 서로 다른 캐시 데이터를 갖는 &lt;strong&gt;데이터 일관성 문제&lt;/strong&gt;가 발생할 수 있다. 이를위해, 결국 Redis 와 Memcached 와 같은 글로벌 캐싱을 도입하여 한곳에서 중앙회된 캐시 데이터를 공유하는 방식으로 해결할 수 있다.&lt;/p&gt;
&lt;p&gt;그렇다면 글로벌 캐싱을 도입했을 때는 문제점이 없을까? 글로벌 캐시 또한 문제점이 정말 많다. 우선, 글로벌 캐시는 Redis 와 Memcached 와 같이 외부 캐시 저장소를 사용하기 때문에 &lt;strong&gt;네트워크 I/O 비용&lt;/strong&gt;이 발생한다. 또한 캐시에 데이터를 저장하고 꺼내올 때 &lt;strong&gt;직렬화와 역직렬화 비용&lt;/strong&gt;도 발생한다. 반면 로컬 캐시는 &lt;strong&gt;RAM Access 비용만 발생&lt;/strong&gt;하기 떄문에 글로벌 캐싱에 비해 매우 빠르다. 당연하게도, 직렬화와 역직렬화도 발생하지 않는다. 또한, 중앙화된 글로벌 캐싱을 사용한다면 캐시 서버가 &lt;strong&gt;SPOF(Single Point Of Failure)&lt;/strong&gt; 가 되므로 &lt;strong&gt;HA(High Availability)&lt;/strong&gt; 를 보장하지 못한다.&lt;/p&gt;
&lt;p&gt;다시 처음으로 돌아가보자. 로컬 캐시의 성능은 매우 빠르기에 도입하고 싶지만, 분산 환경에서 각 캐시 서버간의 동기화 문제를 피할 수 없다. 이를 해결하기 위해선 어떻게 해야할까? 이를 해결하기 위한 방법 중 하나로, 이번 포스팅에선 &lt;strong&gt;Redis Pub/Sub&lt;/strong&gt; 에 대해 간단히 학습해보도록 한다. 만약 모행 서비스가 Scale-Out 되어야 할 상황이라면, 이 &lt;strong&gt;Pub/Sub&lt;/strong&gt; 구조를 적극 고려해보도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;redis-pubsub&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-pubsub&quot; aria-label=&quot;redis pubsub permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis Pub/Sub&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d4616aba71344c3bcab8c09012343cca/26162/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.282208588957054%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACNElEQVR42o1Sy27aQBT137DokkV/hC07/gQJNvwDVaWqLYSqLVYWSdRiF0hok5K0CIgaYYPBPOzYgAv4MfbpzBBI0nTRa4/u1fXV8T1njgAa0YNnH9G2DuY2lt/OEPx27vv0JVEAN1zTTBDSehfCE6AdHiE8G+/e4uL5Mzg/mo/6uqWidnNIswb9doAwDLeA1+YFevZPLFwTjjfnzdVqBcuysHFdtK+u8OFFHt1uF47jYDqd8u+Ns68ovCmi/FHkmc1zwMb5KV4XXuGgVEDzsgmXgvT7fei6Ds/zkEwmIQgCEokE1us1VFWFu3Hhhxs47hweceEF7j3lheVAU4dQeipMw+RNy7Kx2Wx4ncvlEI/HkclkQChdy7a4htf6Jb73ZUxtnZ4RlXYrmzBdq7C9CZ0hCCKfNxeLBddkNpuhVquhXq9DkiS0Wi0YhoH5fA5ZllEqlSCWRYiiyGXYUm59wWHlPaoNGTfKLwoUcSAGyiKbzSIWiyGdTsP3fWiaxnNINyI+oXVAD9mZAkLvRkHlk4Tjo2N0Ol0+PBqNYJpb+qlUimvItGQyjMdjhCSiGrrQnA5l5VFu3t4pwiOr3P2GbRcEAXq9HsrlMvL5PIrFIhRF4bdsU437moKDo5eoncuo1E5g3hp3PqQgDw8LptFyucRgMEC73d7n4XDI7UFCwllUpTrq1VNInyVMJpOnG+6Cbcc8xyzELofdLvsZo8ysw2oGvFqv+BxbYOeKfwL+b/zNjAH+Aeqrcl1FNEvdAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d4616aba71344c3bcab8c09012343cca/a6d36/image.png&quot;
        srcset=&quot;/static/d4616aba71344c3bcab8c09012343cca/222b7/image.png 163w,
/static/d4616aba71344c3bcab8c09012343cca/ff46a/image.png 325w,
/static/d4616aba71344c3bcab8c09012343cca/a6d36/image.png 650w,
/static/d4616aba71344c3bcab8c09012343cca/e548f/image.png 975w,
/static/d4616aba71344c3bcab8c09012343cca/3c492/image.png 1300w,
/static/d4616aba71344c3bcab8c09012343cca/26162/image.png 2020w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Redis 는 위치럼 Pub/Sub 기능을 제공한다. Publisher 가 특정 채널은 구독하는 Subscriber 들에게 브로드캐스킹하는 방식이다. 이를 이용하면 여러 서버간의 로컬 캐시를 동기화할 수 있을 것이다. 특정 애플리케이션 서버의 캐시가 최신화되었을 때, 그 이벤트, 즉 메시지를 다른 모든 애플리케이션 서버에 브로드캐스트 하는 방식으로 동기화시킬 수 있다.&lt;/p&gt;
&lt;p&gt;이 Pub/Sub 구조에서는 모든 애플리케이션 서버가 Publisher 이자 Subscriber 로 동작한다. 즉, 캐시 갱신 이벤트가 발생한 애플리케이션 서버로 동작할 때는 Publisher 가 되며, 떄로는 캐시 갱신 메시지를 전달받는 입장도 되기 떄문에 Subscriber 가 되기도 한다. 그리고 &lt;strong&gt;한 서버의 로컬 캐시가 갱신되면 그 즉시 다른 서버에게 갱신 이벤트를 브로트캐스트 하는 방식&lt;/strong&gt;으로 동작한다. 브로드캐스트 할 때 갱신된 캐시의 키를 메시지로 전달한다.&lt;/p&gt;
&lt;p&gt;추가적인 상식으로만 알아두면 좋을 내용을 학습했다. Pub/Sub 에서 Redis 내부 각 채널은 이벤트를 저장하지 않는 비휘발성의 특징을 가진다는 점이다. 만약 채널에 메시지가 도착했다면, 해당 채널의 Subscriber 가 존재하지 않거나 또는 해당 Subscriber 서버에 장애가 터졌다면, 그 메시지는 휘발된다. 또한 Subscriber 는 동시에 여러 채널을 구독할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;redis-pubsub-과-message-queue&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-pubsub-%EA%B3%BC-message-queue&quot; aria-label=&quot;redis pubsub 과 message queue permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis Pub/Sub 과 Message Queue&lt;/h3&gt;
&lt;p&gt;더 찾아보니, Redis 의 메시지 브로트캐스트 방법에는 Pub/Sub 외에도 다양한 메시징 기법을 제공함을 알게 되었다. 여러 방법중 보편적으로 사용되는 메시징 기법에는 크게 &lt;strong&gt;Pub/Sub&lt;/strong&gt; 와 &lt;strong&gt;메시지 큐(Message Queue)&lt;/strong&gt; 이 있다. &lt;strong&gt;Message Queue&lt;/strong&gt;은 &lt;strong&gt;Point-To-Point Channel&lt;/strong&gt; 방식으로도 불린다. 오로지 한 Consumer 만 메시지를 수신받는 방식이다. 송신자와 수신자가 1:1로 대응되어서 데이터를 수신받는 구조이다. 또한 메시지는 큐에 저장한다. 앞서 설명했듯이 Pub/Sub 은 별다른 저장소 없이 메시지를 즉시 브로트캐스트 하지만, 메시지 큐는 큐 형태의 저장소에 메시지를 저장하므로, 메시지가 손실되지 않는다. 이러한 메시지 큐를 구현하기 위한 방식에는 Redis Lists, Redis Streams 등이 존재하는 듯 하다.&lt;/p&gt;
&lt;p&gt;반면 Pub/Sub 은 메시지를 발생하는 Publisher 와 Subscriber 가 &lt;strong&gt;1:n 의 구조&lt;/strong&gt; 를 취한다. 즉, Pub/Sub 와 Message Queue 의 차이점은 수신자가 1명인가, 또는 n명인가에 따라 다르며, 휘발성/비휘발성에도 차이점이 있다.&lt;/p&gt;
&lt;p&gt;정리하자면, Redis Pub/Sub 은 실시간 메시징 방식이며, 모든 Subscribe 에게 브로트캐스트 해야하는 상황에 적합하다. 반면 Message Queue 는 실시간이 보장되지 않더라도 높은 신뢰성을 보장하며, 1:1 관계로 특정 Consumer 에게만 브로트캐스트 하는 방식에 적합하다. 다만, 로컬 캐시는 다른 하나의 애플리케이션 서버만이 아닌, 채널을 구독하는 모든 로컬 캐시가 동기화되어야 한다. 따라서 Redis Message Queue 방식은 도입하기에 부적합하다.&lt;/p&gt;
&lt;h3 id=&quot;pubsub-과-producerconsumer-의-차이점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pubsub-%EA%B3%BC-producerconsumer-%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; aria-label=&quot;pubsub 과 producerconsumer 의 차이점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Pub/Sub 과 Producer/Consumer 의 차이점&lt;/h3&gt;
&lt;p&gt;또한 Pub/Sub 과 Producer/Consumer 는 서로 거의 비슷한 개념인 것 처럼 보이지만, 엄연히 다른 개념이다. Pub/Sub 모델은 특정 채널에 Publisher 가 메시지를 발행하면 그 채널을 구독하고 있는 모든 Subscriber 에게 메시지를 브로드캐스트한다. 즉, 모든 Subscribe 가 메시지를 수신한다.&lt;/p&gt;
&lt;p&gt;반면, Producer/Consumer 모델은 Producer 가 한번 발행한 메시지를 가정 먼저 Consume 한 Consumer 만 해당 메시지를 읽을 수 있다. 즉, 작업이 한번만 실행되도록 하고 싶을 떄 사용한다. 이는 앞서 말한 Message Queue 방식이 이 Producer/Consumer 로 동작한다.&lt;/p&gt;
&lt;h2 id=&quot;분산환경-로컬-캐시-동기화-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%84%EC%82%B0%ED%99%98%EA%B2%BD-%EB%A1%9C%EC%BB%AC-%EC%BA%90%EC%8B%9C-%EB%8F%99%EA%B8%B0%ED%99%94-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;분산환경 로컬 캐시 동기화 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;분산환경 로컬 캐시 동기화 구현&lt;/h2&gt;
&lt;p&gt;백문이불여일타. 지금부터 스프링 애플리케이션에서 Redis Pub/Sub 을 사용하여 로컬 캐시를 동기화하는 간단한 실습을 진행해보자. 이론적으로만 학습한 Redis Pub/Sub 을 스프링 애플리케이션에서 어떻게 사용하는지 간단히 파악하는 것이 목적으므로, 깊이 파고들지는 않도록 한다. Redis 와 Spring Data Redis 의 깊이있는 학습은 시간이 될 때 해보도록 한다. 실습 환경은 아래와 같이 구성했다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Redis 서버 1대를 띄웠다.&lt;/li&gt;
&lt;li&gt;스프링부트 애플리케이션 서버 4대를 띄웠다.&lt;/li&gt;
&lt;li&gt;캐시 매니저로 스프링 기본 캐시 매니저인 &lt;code class=&quot;language-text&quot;&gt;ConcurrentMapCacheManager&lt;/code&gt; 를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@NoArgsContructor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GeneratedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenerationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;캐시를 위한 캐시 매니저를 아래와 같이 등록해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@EnableCaching&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalCacheConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CacheManager&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cacheManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConccurrentMapCacheManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;redisconfig&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redisconfig&quot; aria-label=&quot;redisconfig permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedisConfig&lt;/h3&gt;
&lt;p&gt;Redis 를 위한 설정을 진행했다. &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; Lettuce 를 사용하기 위해 &lt;code class=&quot;language-text&quot;&gt;LettuceConnectionFactory&lt;/code&gt; 팩토리 메소드를 생성했다. 이렇게 주입된 스프링 빈 오브젝트는 Redis 와 연결을 한다. &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 그리고 &lt;code class=&quot;language-text&quot;&gt;RedisMessageListenerContainer&lt;/code&gt; 는 Subscriber 가 특정 채널을 구독하도록 만들며, 이따 살펴볼 Subscriber 클래스의 &lt;code class=&quot;language-text&quot;&gt;MessageListener&lt;/code&gt; 인터페이스를 구현한 클래스인 RedisSubscriber 를 메시지 리스너(메시지를 구독하는 자)로 등록하고 관리한다. 이들에 대한 더 자세한 이론은 추가적인 학습이 필요하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;${spring.data.redis.host}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; redisHost&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;${spring.data.redis.port}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; redisPort&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LettuceConnectionFactory&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;connectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LettuceConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;redisHost&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; redisPort&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisMessageListenerContainer&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;redisMessageListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LettuceConnectionFactory&lt;/span&gt; lettuceConnectionFactory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RedisMessageListenerContainer&lt;/span&gt; conatiner &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisMessageListenerConatiner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lettuceConnectionFactory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; container&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (3)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;redistTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connectioFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setKeySerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringRedisSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setValueSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Jackson2JsonRedisSerializer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;publisher-subscriber-정의&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#publisher-subscriber-%EC%A0%95%EC%9D%98&quot; aria-label=&quot;publisher subscriber 정의 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Publisher, Subscriber 정의&lt;/h2&gt;
&lt;p&gt;아직 Publisher, Subscriber 에 대한 정의가 없다. 앞서 Redis 에 메시지를 발행하기 위한 기본 커넥션을 설정을 진행했으니, 이젠 Pub, Sub 에 대한 행위(Action) 에 대해 정의 해보자.&lt;/p&gt;
&lt;h3 id=&quot;redispublisher&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redispublisher&quot; aria-label=&quot;redispublisher permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedisPublisher&lt;/h3&gt;
&lt;p&gt;메시지를 발행하는 주체, 즉 Publisher 에 대해 정의를 해준다. &lt;code class=&quot;language-text&quot;&gt;publish()&lt;/code&gt; 를 보면 외부로부터 전달받은 채널에 Book 타입의 메시지를 발행한다. 이때 채널명은 애플리케이션 Service Layer 로 부터 전달받도록 한다. 그리고 RedisTemplate 에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;convertAndSend()&lt;/code&gt; 를 사용하여 Book 타입의 메시지를 채널에 발행하도록 동작한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisPublisher&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisPublisher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ChannelTopic&lt;/span&gt; topic&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;convertAndSend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;topic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTopic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;실제로 아래 Service Layer 코드를 살펴보자. Book 을 업데이트하는 이벤트가 발생하면, 그 이벤트를 book-channel 이라는 Redis 채널에 발행한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... (Book 엔티티에 대한 업데이트 로직)&lt;/span&gt;
    redisPublisher&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ChannelTopic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;book-channel&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;redissubscriber&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redissubscriber&quot; aria-label=&quot;redissubscriber permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedisSubscriber&lt;/h3&gt;
&lt;p&gt;Subscriber 에 대한 동작을 정의해준다. 앞서 Publisher 가 book-channel 이라는 Redis 채널에 메시지를 발행했다면, 그 메시지를 Subscriber 가 어떻게 처리할 것인지 행위를 정의한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisSubScriber&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MessageListener&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CacheManager&lt;/span&gt; cacheManager&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisSubscriber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Redistemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
                           &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CacheManager&lt;/span&gt; cacheManager&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                           &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisMessageListenerContainer&lt;/span&gt; redisMessageListener&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cacheManager &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cacheManager&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;ChannelTopic&lt;/span&gt; bookUpdateChannel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChannelTopic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;book-channel&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        redisMessageListener&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMessageListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bookUpdateChannel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;MessageListener&lt;/code&gt; 인터테이스를 구현한 이 Subscriber 는 메시지 리스너(메시지를 전달받는 자) 로 등록되고, 관리된다. 앞서 &lt;code class=&quot;language-text&quot;&gt;RedisConfig&lt;/code&gt; 를 설명할 때, &lt;code class=&quot;language-text&quot;&gt;RedisMessageListenerContainer&lt;/code&gt; 를 스프링 빈으로 등록시 &lt;code class=&quot;language-text&quot;&gt;MessageListner&lt;/code&gt; 에 대한 구현체인 &lt;code class=&quot;language-text&quot;&gt;RedisSubscriber&lt;/code&gt; 를 메시지 리스너로 등록해서 사용할 수 있게 한다고 설명했었다. 이 구현체가 바로 위와 같이 정의되었다. 이 Subscriber 는 메시지가 채널에 발행될 떄 마다 &lt;code class=&quot;language-text&quot;&gt;onMessage()&lt;/code&gt; 에 정의한 대로 동작한다.&lt;/p&gt;
&lt;h2 id=&quot;onmessage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#onmessage&quot; aria-label=&quot;onmessage permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;onMessage&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;OnMessage()&lt;/code&gt; 는 메시지가 발행되었을 때 항상 자동으로 호출되는 메소드이다. 즉, &lt;strong&gt;발행된 메시지에 대한 행위(Action) 을 정의&lt;/strong&gt;한다. 필자는 위처럼 CacheManager 를 사용하여, 캐시에서 변경이 발생한 &lt;strong&gt;특정 캐시 데이터를 무효화(invalidte)&lt;/strong&gt; 하는 행위를 정의했다. 즉, 모든 Subscriber 서버의 로컬 캐시의 특정 캐시 데이터는 &lt;strong&gt;삭제(evict)&lt;/strong&gt; 된다.&lt;/p&gt;
&lt;h3 id=&quot;redismessagelistenercontainer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redismessagelistenercontainer&quot; aria-label=&quot;redismessagelistenercontainer permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedisMessageListenerContainer&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;RedisMessageListenerContainer&lt;/code&gt; 는 특정 채널을 구독하는 역할을 수행하도록 돕는다. MessageListner 구현체인 Subscriber 가 새로운 채널을 등록하고 메시지를 수신하도록 돕는다. 실제로 위처럼 book-channel 이라는 채널을 Subscriber 가 구독하도록 돕고있다.&lt;/p&gt;
&lt;h3 id=&quot;스프링-애플리케이션&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98&quot; aria-label=&quot;스프링 애플리케이션 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링 애플리케이션&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookRepository&lt;/span&gt; bookRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisPublisher&lt;/span&gt; redisPublisher&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookRepository&lt;/span&gt; bookRepository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisPublisher&lt;/span&gt; redisPublisher&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bookRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bookRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisPublisher &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisPublisher&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Cacheable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;readOnly &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findBookInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; bookRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findByName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ... (Book 엔티티 업데이트 로직)&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bookRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findByName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        book&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        redisPublisher&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ChannelTopic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;book-channel&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;findBookInfo()&lt;/code&gt; 는 특정 Book 을 가져오는 메소드이다. &lt;code class=&quot;language-text&quot;&gt;@Cacheable&lt;/code&gt; 을 통해 캐싱한다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;updateBook()&lt;/code&gt; 메소드는 Book 엔티티를 업데이트 하면서, &lt;code class=&quot;language-text&quot;&gt;redisPublisher&lt;/code&gt; 를 사용하여 book-channel 채널에 메세지를 발행한다. 발행한 메시지는 앞서 정의한 RedisSubscriber 가 수신하고, 캐시를 Evict 한다.&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;향후 모행 서비스의 규모가 더 커지고, &lt;strong&gt;SPOF(Single Point Of Failure)&lt;/strong&gt;, &lt;strong&gt;FA(Fault Tolerance)&lt;/strong&gt; 와 같은 문제가 발생할 것이 예상된다면 Redis Pub/Sub 을 사용한 로컬 캐시 동기화 구조를 적극 고려하고 있다. 글로벌 캐싱에 비해 여러 로컬 캐시를 통한 &lt;strong&gt;HA(High Availability)&lt;/strong&gt; 를 보장하는 방식이 더 적합하다고 생각하기에, 분산 환경에서도 로컬 캐시를 웆;하면서 동기화 문제를 해결할 예정이다.&lt;/p&gt;
&lt;h2 id=&quot;더-학습해야-할-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EC%95%BC-%ED%95%A0-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해야 할 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해야 할 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;li&gt;Kafka&lt;/li&gt;
&lt;li&gt;Spring Data Redis&lt;/li&gt;
&lt;li&gt;직렬화/역직렬화&lt;/li&gt;
&lt;li&gt;Producer/Consumer&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/local-cache-synchronization-with-redis-pub-sub/&quot;&gt;https://hudi.blog/local-cache-synchronization-with-redis-pub-sub/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@kyoungsu0717/Spring-%EA%B3%BC-Redis-%EA%B7%B8%EB%A6%AC%EA%B3%A0-Message-Queue&quot;&gt;https://velog.io/@kyoungsu0717/Spring-과-Redis-그리고-Message-Queue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/pub-sub-vs-message-queues&quot;&gt;https://www.baeldung.com/pub-sub-vs-message-queues&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/frientrip/pub-sub-%EC%9E%98-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90-de9dc1b9f739&quot;&gt;https://medium.com/frientrip/pub-sub-잘-알고-쓰자-de9dc1b9f739&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zkdlu.github.io/2020-12-29/redis04-spring-boot%EC%97%90%EC%84%9C-pub,sub-%EB%AA%A8%EB%8D%B8-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0/&quot;&gt;https://zkdlu.github.io/2020-12-29/redis04-spring-boot에서-pub,sub-모델-사용하기/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[[가상 면접 사례로 배우는 대규모 시스템 설계 기초] 제 3장. 시스템 설계 면접 공략법]]></title><description><![CDATA[시스템 설계 면접은 무엇을 평가할까? 시스템 설계 면접을 당황스러울 때가 많다. 널리 알려진 상품 X…]]></description><link>https://haon.site/virtual-interview/chap03/</link><guid isPermaLink="false">https://haon.site/virtual-interview/chap03/</guid><pubDate>Tue, 19 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;시스템-설계-면접은-무엇을-평가할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EB%A9%B4%EC%A0%91%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%84-%ED%8F%89%EA%B0%80%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;시스템 설계 면접은 무엇을 평가할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시스템 설계 면접은 무엇을 평가할까?&lt;/h2&gt;
&lt;p&gt;시스템 설계 면접을 당황스러울 때가 많다. 널리 알려진 상품 X (유튜브, 트위치, 인스타그램 등) 을 설계해보라는 식으로 막연한 문제가 나올때도 맣다.&lt;/p&gt;
&lt;p&gt;다행인 것은, 아무도 면접자에게 실제 시스테처럼 복잡한 요구를 하지는 않는다. 실세계에서 이용되는 시스템으 구조는 극도로 복잡하다. 그러하면, 왜 대체 시스템 설계 면접이 있는 것일까?
시스테 설계 면접은 2명의 동료가 모호한 문제를 풀기 위해 협력하여 그 해결책을 찾아내는 시뮬레이션이다. 이 문제에는 정해진 결말도 없고, 정답도 없다. 이 문제에는 정해진 결말도 없고, 정답도 없다. 최종적으로 도출될 설계안은 본인이 설계 과정에 들인 노력에 비하면 그다지 중요하지 않다.&lt;/p&gt;
&lt;p&gt;이 면접은 면접자의 설게 기술을 시연하는 자리익, 설게 과정에서 내린 결정들에 대한 방어 능력을 보이는 자리이며, 면접관의 피드백을 건설적인 방식으로 처리할 자질이 있음을 보이는 자리인 것이다.&lt;/p&gt;
&lt;p&gt;면접관의 입장에서는, 일차적 목표는 면접자의 능력을 평가하는 것이다. 가장 피하고 싶은 일은 면접이 매끄럽게 진행되지 않아서 충분한 시그널(signal) 을 수집하지 못한 바람에 평가 결과를 확정 지을 수 없는 상황일 것이다. 그렇다면 면접관이 시스템 설계 면접에서 찾고자 하는 것은 무엇인가?&lt;/p&gt;
&lt;p&gt;많은 사람들이 시스템 설계 면접은 지원자의 설계 능력이 기술적 측면을 평가하는 자리일 것이라 생각할 것이다. 사실은 그 이상이다. &lt;strong&gt;시스템 설계 면접이 잘 진행되면, 지원자가 협력에 적합한 사람인지, 압박이 심한 상황도 잘 해쳐 나갈 자질이 있는지, 모호한 문제를 건설적으로 해결할 능력이 있는지 등을 살펴볼 수 있다. 좋은 질문을 던질 능력이 있는지도 살펴볼 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;훌륭한 면접관은 &lt;strong&gt;부정적 신호(red flag) 도 놓치지 않는다.&lt;/strong&gt; 설계의 순수성에 집착한 나머지, 팀원과 타협하여 결정하는 것을 등한시하고 과도하게 오버 엔지니어링을 하게되는 개발자들이 현업에도 많다. 그런 엔지니어들은 과도한 엔지니어링의 결과로 시스템 전반의 비용이 올라간다는 사실을 알아채지 못하는 일이 많은데, 그 결과로 상당수 회사들은 값비싼 대가를 치르고 있다. 우리는 면접관에게 이런 엔지니어들과 같은 경향이 있다는 것을 보이면 안된다.
이것 이외의 부정적 신호로는 완고함, 편협함 같은 것들도 있다.&lt;/p&gt;
&lt;h2 id=&quot;효과적-면접을-위한-4단계-접근법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%9A%A8%EA%B3%BC%EC%A0%81-%EB%A9%B4%EC%A0%91%EC%9D%84-%EC%9C%84%ED%95%9C-4%EB%8B%A8%EA%B3%84-%EC%A0%91%EA%B7%BC%EB%B2%95&quot; aria-label=&quot;효과적 면접을 위한 4단계 접근법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;효과적 면접을 위한 4단계 접근법&lt;/h2&gt;
&lt;p&gt;시스템 설계 면접은 전부 제각각이다. 훌륭한 설계 면접은 정해진 결말도 없고 정답도 없다. 하지만 그 절차나 범위에는 공통적인 부분이 있다.&lt;/p&gt;
&lt;h3 id=&quot;1단계-문제-이해-및-설계-범위-확정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1%EB%8B%A8%EA%B3%84-%EB%AC%B8%EC%A0%9C-%EC%9D%B4%ED%95%B4-%EB%B0%8F-%EC%84%A4%EA%B3%84-%EB%B2%94%EC%9C%84-%ED%99%95%EC%A0%95&quot; aria-label=&quot;1단계 문제 이해 및 설계 범위 확정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1단계. 문제 이해 및 설계 범위 확정&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;바로 답부터 들이밀지 마라. 속도를 늦추고, 깊이 생각하고 질문하여 요구사항과 가정들을 정확히 파악하라.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;시스템 면접을 볼 떄는 생각없이 바로 답을 내서는 좋은 점수를 받기 어렵다. 요구사항을 완전히 이해하지 않고 답을 내놓은 행위는 아주 엄청난 감점 요소이다. 면접은 퀴즈 쇼가 아니며, 정답 따위는 없다는 걸 상기하자.&lt;/p&gt;
&lt;p&gt;그러니 &lt;strong&gt;바로 답부터 들이밀지 마라. 속도를 늦춰라. 깊이 생각하고 질문하여 요구사항과 가정들을 분명히 하라.&lt;/strong&gt; 이 단계의 중요성은 강조하고 또 강조해도 모자람이 없다. 엔지니어인 우리에게는 어려운 문제를 풀고 최종 설계를 바로 내놓고 싶은 욕구가 있다. 하지만 그러면 잘못된 시스템을 설계할 가능성이 높아진다. 엔지니어가 가져야 할 가장 중요한 기술 중 하나는 올바른 질문을 하는 것, 적절한 가정을 하는 것, 그리고 시스템 구축에 필요한 정보를 모으는 것이다.&lt;/p&gt;
&lt;p&gt;질문을 던지면 면접관은 우리가 질문에 대한 답을 바로 내놓거나, 아니면 우리 스스로 어떤 가정을 하기를 요구할 것이다. 후자의 경우(스스로 가정하기) 에는 그 가정을 화이트보드다 종이에 적어두어야 한다. 나중에 필요해질 때가 있어서다. 그렇다면 어떤 질문을 해야하나? &lt;strong&gt;요구사항을 정확히 이해하는데 필요한 질문을 하라.&lt;/strong&gt; 예를들어 아래와 같은 질문을 해볼 수 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 요구사항을 정확히 이해하기 위해 필요한 질문 예시&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;구체적으로 어떤 기능들을 만들어야 하나?&lt;/li&gt;
&lt;li&gt;제품 사용자 수는 얼마나 되나?&lt;/li&gt;
&lt;li&gt;회사의 규모는 얼마나 빨리 커지리랄 예상하나? 3달, 6달, 1년뒤의 규모는 얼마나 되리라 예상하는가?&lt;/li&gt;
&lt;li&gt;회사가 주로 사용하는 기술 스택은 무엇인가? 설계를 순화하기 위해 활용할 수 있는 기존 서비스로는 어떤 것들이 있는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;예제-1단계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%88%EC%A0%9C-1%EB%8B%A8%EA%B3%84&quot; aria-label=&quot;예제 1단계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;예제 (1단계)&lt;/h3&gt;
&lt;p&gt;뉴스 피드 시스템을 설계하라는 요구를 받았다고 해보자. 요구사항을 분명히 하기위한 질문을 던져야 할 것이다. 우리와 면접과 사이에 오갈 대화는 다음과 비슷할 것이다.&lt;/p&gt;
&lt;ul&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;면접관 : 5000명입니다.&lt;/li&gt;
&lt;li&gt;지원자 : 사이트로 오는 트래픽 규모는 어느 정도입니까?&lt;/li&gt;
&lt;li&gt;면접관 : DAU 기준 1천만명입니다.&lt;/li&gt;
&lt;li&gt;지원자 : 피드에 이미지나 비디오도 올라올 수 있나요? 아니면 포스트는 그저 텍스트입니까?&lt;/li&gt;
&lt;li&gt;면접관 : 이미지나 비디오 같은 미디어 파일로 포스트 할 수 있어야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;지금까지 우리가 면접관에게 던질 수 있는 질문 사례를 살펴보았다. 요구사항을 이해하고 모호함을 없애는 게 이 단계에서 가장 중요하다는 것을 명심하자.&lt;/p&gt;
&lt;h2 id=&quot;2단계-개략적인-설계안-제시-및-동의-구하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2%EB%8B%A8%EA%B3%84-%EA%B0%9C%EB%9E%B5%EC%A0%81%EC%9D%B8-%EC%84%A4%EA%B3%84%EC%95%88-%EC%A0%9C%EC%8B%9C-%EB%B0%8F-%EB%8F%99%EC%9D%98-%EA%B5%AC%ED%95%98%EA%B8%B0&quot; aria-label=&quot;2단계 개략적인 설계안 제시 및 동의 구하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2단계. 개략적인 설계안 제시 및 동의 구하기&lt;/h2&gt;
&lt;p&gt;이번 단계에서 초점을 맞춰야 할 것은 개략적인 설계안을 제시하고 면접관의 동의를 얻는 것이다. 이 과정은 면접관과 협력하며 진행하면 좋다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; &lt;strong&gt;설게안에 대한 최초 청사진(아키텍처 설계를 문서화한 기술 도면) 을 제시하고 의견을 구하라.&lt;/strong&gt; 면접관을 마치 팀원인 것처럼 대하라. 훌륭한 면접관들은 지원자들과 대화하고 설계 과정에 개입하기를 즐긴다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; &lt;strong&gt;화이트보드나 종이에 핵심 컴포넌트를 포함하는 다이어그램을 그려라.&lt;/strong&gt; 클라이언트(모바일/웹), API, 웹 서버, 데이터 저장소, 캐시, CDN, 메시지 큐 같은 것들이 포함될 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 이 최초 설계안이 시스템 규모와 관게된 제약사항들을 만족하는지를 개력적으로 계산해보아라. 계산 과정은 소리내어 설명하라. 아울러, 이런 개략적 추정이 필요한지는 면접관에게 미리 물어보도록 하자.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;가능하다면 시스템의 구체적 사용 사례도 몇 가지 살펴보자. 개력적 설계안을 잡아 나가는데 도움이 될 것이다. 미처 고려하지 못한 엣지 케이스를 발견하는데도 도움이 될 것이다.&lt;/p&gt;
&lt;p&gt;이 단계에서 &quot;API 엔드포인트나 데이터베이스 스키마도 보야아 하는가?&quot; 이는 질문에 따라 다르다. 가령 &quot;구글 검색 엔진을 설계하라&quot; 와 같은 큰 규모의 설계 문제라면 이 단계에서 다루기에는 지나치게 세부적인 내용일 것이다. 멀티 플레이어 포커 게임의 백엔드를 설게하라는 질문이라면 괜찮을 것이다. 면접관의 의견을 물어보아라.&lt;/p&gt;
&lt;h3 id=&quot;예제-2단계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%88%EC%A0%9C-2%EB%8B%A8%EA%B3%84&quot; aria-label=&quot;예제 2단계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;예제 (2단계)&lt;/h3&gt;
&lt;p&gt;&quot;뉴스 피드 시스템을 설게하라&quot; 라는 질문을 그대로 활용하여, 개략적 설계는 어떻게 만들어 내는지 살펴보겠다. 시스템이 실제로 어떻게 동작하는지 지금 당장 이해할 필요는 없다. (상세한 내용은 본 서적의 11장에서 다룬다고 한다.)&lt;/p&gt;
&lt;p&gt;개략적으로 살펴보자면 이 설계는 2가지의 처리 플로우(flow) 로 나누어 생각해볼 수 있다. 피드 발행과 피드 생성이 2가지이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;피드 발행 : 사용자가 포스트를 올리면 관련된 데이터가 캐시/데이터베이스에 기록되고, 해당 사용자의 친구 뉴스 피드에 뜨게된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;피드 생성(조회) : 어떤 사용자의 뉴스 피드는 해당 사용자 친구들의 포스트를 시간 역순으로(최신 포스트부터 오래된 포스트 순으로) 정렬하여 만든다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5e2c415dce948ba5080ee296538fa0f2/64639/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.41717791411043%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB2UlEQVR42oVTaa+cMAzc///X+qGq+tS+c2E5N9wEWG5cj8H0tapUaS0SJ57MjL2XeZ4JsW0rLctCj76ncRxlrWcLB/LTNMl6W1daOZCzTbPnj/sXLUrygmo+fHNv5Pgh2baVS/vlmWrb0Mhr7O9ZRm3XUZwk9PT6TpFJaBgGeeyCop43QWyoqq18v/54lugeDwFd14UafgBgA7P345jSvKTKWv4W9Hx15AvWFxSB1feXN0oLTjLALYzoy7cned2LYrZgEgXYB/GdiDb+bdR1D7qnGeVlJQoEsO8HSeAFvJ4VpTyAwrKqd0+ZWVaWuwounKdZWIPALYjEMoDNKnk9TN7WTQAhCWYDDOcISAb77ShEFFXFDFOxTHPSlOkwGwxjvuD4wcHGijSc2Wb3UAtBAKquXiB1Z5cxIl4U0cfNF3aRSenl6lJZ19I9k+XSnObougLCMy+M5Q7qJsY5JUfc/nfXYwazSHh1XBkbGA5AjAgYKiCaBN9DY8jnphkeI7FHAcO74cNEBhoAbhAyA3t6ixlTwOlgqA0Ew7btfns4DCP9/HB4SNOTAS6rX8gJ4F+S4a8oSHNKmMQJiEKYa/hQk59DB/+zZA0o0n/PH11Waf8C1CkYDtP/F78Ar6vppbiQOUgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/5e2c415dce948ba5080ee296538fa0f2/a6d36/image.png&quot;
        srcset=&quot;/static/5e2c415dce948ba5080ee296538fa0f2/222b7/image.png 163w,
/static/5e2c415dce948ba5080ee296538fa0f2/ff46a/image.png 325w,
/static/5e2c415dce948ba5080ee296538fa0f2/a6d36/image.png 650w,
/static/5e2c415dce948ba5080ee296538fa0f2/e548f/image.png 975w,
/static/5e2c415dce948ba5080ee296538fa0f2/3c492/image.png 1300w,
/static/5e2c415dce948ba5080ee296538fa0f2/64639/image.png 1568w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;3단계-상세-설계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3%EB%8B%A8%EA%B3%84-%EC%83%81%EC%84%B8-%EC%84%A4%EA%B3%84&quot; aria-label=&quot;3단계 상세 설계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3단계. 상세 설계&lt;/h2&gt;
&lt;p&gt;이 단계로 왔다면 우리는 면접관과 아래 목표들은 달성한 상태일 것이다.&lt;/p&gt;
&lt;ul&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&gt;이제 면접관과 해야할 일은 실제 대상 컴포넌트 사이의 우선순위를 정하는 것이다. 어떤 면접에서는 면접관이 우리에게 집중 했으면 하는 영역을 알려 주기도 한다. 반면 어떨 때는, 특히 선임급 개발자 면접이라면, 시스템의 성능 특성에 대한 질문을 던질 것이고, 그 경우 질문 내용은 시스템의 병목 구간이나 자원 요구량 추정치에 초점이 맞춰져 있을 것이다. &lt;strong&gt;대부분의 경우 면접관은 우리가 특정 시스템 컴포넌트들의 세부사항을 깊이 있게 설명하는 것을 보길 원한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;가령 출제된 문제가 단축 URL 생성기 설계라고 해보자. 그렇다면 면접관은 우리가 그 해시 함수의 설계를 구체적으로 어떻게 설계할 것인지 듣고 싶어 할 것이다. 반면 체팅 시스템에 관한 문제였다면, 어떻게 하면 레이턴시를 줄이고 사용자의 온/오프라인 상태를 표시할 것인지를 듣고자 할 것이다.&lt;/p&gt;
&lt;p&gt;면접 시에는 시간 관리에도 특별히 주의하자. &lt;strong&gt;사소한 세부사항을 설명하느라 정작 우리의 능력을 보일 기회를 놓쳐버리게 될 수도 있다.&lt;/strong&gt; 우리는 면접관에게 긍정적 신호를 전달하는 데 집중해야 한다. 불필요한 세부사항에 시간을 쓰지말자. 예를들어, 페이스북에서 뉴스 피드의 순위를 매기는 데 사용되는 EdgeRank 알고리즘에 대해 얘기하는 것은 바람직하지 않은데, 시간을 너무 많이 쓰게 되는데다 &lt;strong&gt;우리가 규모 확장 가능한 시스템을 설계할 능력이 있다는 것을 입증하는데는 도움이 되지 않는다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;예제-3단계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%88%EC%A0%9C-3%EB%8B%A8%EA%B3%84&quot; aria-label=&quot;예제 3단계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;예제 (3단계)&lt;/h3&gt;
&lt;p&gt;뉴스 피드 시스템의 개략적 설계를 마친 상황이라고 해보자. 그리고 면접관도 그 설계에 만족하고 있다고 해보자. 이제 2가지 중요한 용례를 보다 깊이 탐구해야 한다. (상세 설계도는 생략하겠다.)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 피드 발행&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 뉴스 피드 가져오기&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;4단계-마무리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4%EB%8B%A8%EA%B3%84-%EB%A7%88%EB%AC%B4%EB%A6%AC&quot; aria-label=&quot;4단계 마무리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4단계. 마무리&lt;/h3&gt;
&lt;p&gt;이 마지막 단계에서 면접관은 설계 결과물에 관련된 몇 가지 후속 질문을 던질 수도 있고, 우리 스스로 추가 논의를 진행하도록 할 수도 있다. 아래의 몇 가지 지침을 활용하도록 하자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;면접관이 시스템 병목구간, 혹은 좀 더 개산 가능한 지점을 찾아내라 주문할 수 있다. 거기에 대고 우리의 설계가 완벽하다거나 개선할 부분이 없다는 답은 하지 않도록 하자. 개선할 점은 언제나 있기 마련이다. 이런 질문은 우리의 비판적 사고 능력을 보이고, 마지막으로 좋은 인상을 남길 기회이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;우리가 만든 설계를 한번 다시 요약해주는 것도 도움이 될 수 있다. 우리의 해결책을 제시한 경우에는 특히 중요하다. 긴 면접이 끝난뒤에 면접관의 기억을 환기시켜주는 것은 효과가 있기 때문이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;운영 이슈도 논의할 가치가 충분하다. 메트릭은 어떻게 수집하고 모니터링 할 것인가? 로그는? 시스템은 어떻게 배포해나갈 것인가?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;미래에 닥칠 규모 확장 요구에 어떻게 대처할 것인지도 흥미로운 주제이다. 예를들어, 현재 설계로 백만 사용자는 능히 감당할 수 있다고 해보자. 천만 사용자를 감당하려면 어떻게 대처해야 할까?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;면접에서 해야할 것과 하며 안되는 것을 정리하면 아래와 같다.&lt;/p&gt;
&lt;h3 id=&quot;해야할-것&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B4%EC%95%BC%ED%95%A0-%EA%B2%83&quot; aria-label=&quot;해야할 것 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;해야할 것&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 질문을 통해 확인하라. 스스로 내린 가정이 옳다 믿고 진행하지 마라.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 문제의 요구사항을 이해하라.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 정답이나 최선의 답안 같은 것은 없다는 것을 명심하라. 스타트업을 위한 설계안과 수백만 사용자를 지원해야 하는 중견 깅버을 위한 설계안이 같을리 없다. 요구사항을 정확하게 이해했는지 다시 확인하라.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 가능하다면 여러 해법을 함께 제시하라.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; 개략적 설계에 면접관이 동의하면, 각 컴포넌트의 세부사항일 설명하기 시작하라. 가장 중요한 컴포넌트부터 진행하라.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(6)&lt;/code&gt; 면접관의 아이디어를 이끌어내라. 좋은 면접관은 우리와 같은 팀원처럼 협력할 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(7)&lt;/code&gt; 포기하지 말라.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;하지 말아야 할 것&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 전형적인 면접 문제들에도 대비하지 않은 상태에서 면접장에 기자 말라.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 요구사항이나 가정들을 분명히 하지 않은 상태에서 설계를 제시하지 말자.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 처음부터 특정 컴포넌트의 세부사항을 너무 깊이 설명하지 말자. 개략적 설계를 마친 뒤에 세부사항으로 나아가자.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 진행 중에 막혔다면, 힌트를 정하기를 주저하지 말자.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; 소통을 주저하지 말라. 침묵 속에 설계를 진행하지 말자.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(6)&lt;/code&gt; 설계안을 내놓은 순간 면접이 끝났다고 생각하지 말자. 면접관이 끝났다고 말하기 전까지는 끝난 것이 아니다. 의견을 일찍, 그리고 자주 구하라.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;시간-배분&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EA%B0%84-%EB%B0%B0%EB%B6%84&quot; aria-label=&quot;시간 배분 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시간 배분&lt;/h3&gt;
&lt;p&gt;45분의 시간이 주어진다고 가정하고, 각 단계에 어느정도 시간을 쓰는 것이 좋을까? 대략적인 추정치에 불과하니, 참고만 하자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1단계 (문제 이해 및 설계 범위 확정) : 3~10분&lt;/li&gt;
&lt;li&gt;2단계 (개략적 설계안 제시 및 동의 구하기) : 10~15분&lt;/li&gt;
&lt;li&gt;3단계 (상세 설계) : 10~25분&lt;/li&gt;
&lt;li&gt;4단계 (마무리) : 3~5분&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[스프링 페이지네이션에서 발생한 Latency 의 원인과 커버링 인덱스 생성을 통한 문제 해결기]]></title><description><![CDATA[💡 현재 포스트는 하모니 팀 기술 블로그 에 게시된 글 입니다. 이번 트러블슈팅 해결기는 지난 MySQL…]]></description><link>https://haon.site/database/pagnation-latency-db-covering-index/</link><guid isPermaLink="false">https://haon.site/database/pagnation-latency-db-covering-index/</guid><pubDate>Mon, 18 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://haon.blog/database/pagnation-latency-db-covering-index/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이번 트러블슈팅 해결기는 지난 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/index-range-scan-improvement/&quot;&gt;MySQL 인덱스 레인지 스캔을 통한 쿼리 성능 개선기&lt;/a&gt; 에 이어지는 내용입니다. 지난 인덱스 적용을 통한 쿼리 성능 개선시 발생한 문제점을 어떻게 또 다시 해결했는지를 다루는 것이므로, 지난 포스팅을 읽고오신다면 이해에 큰 도움이 될 것입니다! 😎&lt;/p&gt;
&lt;h2 id=&quot;문제-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%A0%9C-%EC%83%81%ED%99%A9&quot; aria-label=&quot;문제 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문제 상황&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8158ac55f938417cef1a0cb42797e8ac/46115/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 73.00613496932516%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAACXUlEQVR42m2USXPTQBSE/ec5cOM3QOUEB6rgwHbPwoVAFstO7MSxZS2xrF2jZabpN5ZwHDJVXSPJT5/6LeORMRrGGCtAI1p0mB93uD/b7c53DefbE/F+/FXDvehsfNO0KLIaZdagbTqMtAZU1aAsFWTFbgX3zxbryxir85hgUbLXT+o0hu/kNn52EeHtq184en2O+3GEkdEVtM7RaQkoBUn5VNgreEHy+4ZZFXSYoswjVEUM3WlxuMF0eoLx+Bi+f2UDr05nWNxOsY1vUFUrCzYm+Ce5b9u1jQ+CayTJLcGuLcHImC1fWjDlZf8w4Bd95FmIogzRdRmfFb1yAiWTiFn5UGplVdcu4/wdUOsINzdnmEzOEIaOdah1jOXSxXx+h1rVGJY0TmtpXkLAGmHgWCXJjND13qGqHuhy59AYn4p5za61LZq6ZnBDQLd/1jxaoLirmJlSy/8dOs7g8NECNdsvENFwLTDZ6zqkVjZ+s5kiTe8OHbaty6+uuXvWoXRaIKKG7jZebncBipQKOBUeinwB5/oEk/EJymIpRcEI2Pbj8fhkTGK+SCCHPo0qvH9zgTxRhNBx21mgNCXdzvDp4xF+fPmAPL23de6BwbO5E+CuZlK/IqtYZ0UQ66maHujZutXS5UpqGDx1+DJwSLEipChru6se2HWenYhD6QHoH5wEYyKCKtZNTkJBlwUdFog3Kbuac2bXPBXes9MTDkAZ1G1/5OJ+aD1k2QxRNGEXJ5yzKebTS3x+d4zx73O4D2Om6tl53L+XDCk/X/y3QE2HJVPLD1Xv9qYpmUWHl9ZfnQB/ZEWG0zwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/8158ac55f938417cef1a0cb42797e8ac/a6d36/image.png&quot;
        srcset=&quot;/static/8158ac55f938417cef1a0cb42797e8ac/222b7/image.png 163w,
/static/8158ac55f938417cef1a0cb42797e8ac/ff46a/image.png 325w,
/static/8158ac55f938417cef1a0cb42797e8ac/a6d36/image.png 650w,
/static/8158ac55f938417cef1a0cb42797e8ac/e548f/image.png 975w,
/static/8158ac55f938417cef1a0cb42797e8ac/46115/image.png 1290w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;지난 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/index-range-scan-improvement/&quot;&gt;MySQL 인덱스 레인지 스캔을 통한 쿼리 성능 개선기&lt;/a&gt; 에서 향후 출시될 플래너 API 를 위해 인덱스를 생성한 쿼리 튜닝을 시도했습니다. 실제로 그 결과 &lt;strong&gt;약 36.839초&lt;/strong&gt;가 걸리던 쿼리를 &lt;strong&gt;약 0.023초&lt;/strong&gt;로 크게 감축시키는 성능 개선을 성공적으로 이루어냈죠. 하지만 막상 API 를 테스트해보니, 기대와 달리 쿼리 소요시간이 간혹 길게 측정되는 문제가 발생했습니다.&lt;/p&gt;
&lt;h3 id=&quot;실제-api-호출-응답시간&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%EC%A0%9C-api-%ED%98%B8%EC%B6%9C-%EC%9D%91%EB%8B%B5%EC%8B%9C%EA%B0%84&quot; aria-label=&quot;실제 api 호출 응답시간 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실제 API 호출 응답시간&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3e00bc3fa6b6076e7ef6af2a404ab016/9937c/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 16.56441717791411%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAkElEQVR42j2NWxaDMAhE3Zg274cxJmrTdv8bmQL19IPDnYGByYaEdj7hYkZcN/RrwKcVaavYzwvKR6RS4XOBcgFl7+BMIM05e+e2dog/1X7gGi8SEbM2YF1bx6y0cMircKYHxgcp9pV1OMegBw0PYzHeHzpcMGkasLHQMS5FzN6ijISE9Y/VvSeZu//Zedn5AoXVbdu2nBvhAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/3e00bc3fa6b6076e7ef6af2a404ab016/a6d36/image-5.png&quot;
        srcset=&quot;/static/3e00bc3fa6b6076e7ef6af2a404ab016/222b7/image-5.png 163w,
/static/3e00bc3fa6b6076e7ef6af2a404ab016/ff46a/image-5.png 325w,
/static/3e00bc3fa6b6076e7ef6af2a404ab016/a6d36/image-5.png 650w,
/static/3e00bc3fa6b6076e7ef6af2a404ab016/e548f/image-5.png 975w,
/static/3e00bc3fa6b6076e7ef6af2a404ab016/9937c/image-5.png 1156w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Jmeter를 사용하여 성능 테스트를 수행한 결과, 저희의 기대처럼 쿼리 튜닝이 적용된 경우에는 실제 API를 요청시 Best Case 에서 29ms 가 측정되었습니다. 또한 100명이 동시 요청시에는 평균 응답속도가 552ms 가 측정되었습니다. 이로보아, 처음에는 스프링 애플리케이션 로직이 인덱스를 제대로 타는 것으로 판단하게 되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4756643443c898ab275a8171f0befa69/0940f/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 22.699386503067483%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAvUlEQVR42o1QSRLDIAzLv5qFPUBCVqD9/0dc2+l0cuihB83IkmUbGmUcSGNBuxHGOIH1kbkLEyKCsg61AGYM2HdxAunkW9Yt+9p5aPZcIcwLN6Rth+3MPHBaVubUTEHqEfriad15OGmU19bjMTOsx4kDS+HLBqWhkwq2XDjUCvnlj37gBTEl5metMGMtcFl+vsBjvsfsUSo0CkUqaCBB46VSG+bkiQ+nSwms2+ubyKPX3PWmxY3dIH7i7v3L37hktTimH0YKAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/4756643443c898ab275a8171f0befa69/a6d36/image-6.png&quot;
        srcset=&quot;/static/4756643443c898ab275a8171f0befa69/222b7/image-6.png 163w,
/static/4756643443c898ab275a8171f0befa69/ff46a/image-6.png 325w,
/static/4756643443c898ab275a8171f0befa69/a6d36/image-6.png 650w,
/static/4756643443c898ab275a8171f0befa69/e548f/image-6.png 975w,
/static/4756643443c898ab275a8171f0befa69/0940f/image-6.png 1154w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;하지만, 문제는 상황에 따라 위처럼 소요시간이 천차만별이라는 점입니다. Best Case 에서 29ms 라는 매우 빠른 소요시간을 보이던 성능이 간혹 위치럼 긴 Latency 가 발생하고 있습니다. 이 문제는 무엇이 문제였을까요? 이와 관련한 트러블슈팅 해결과 쿼리 성능 개선기를 공유해보고자 합니다 🙂&lt;/p&gt;
&lt;h2 id=&quot;-실마리-발견&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-%EC%8B%A4%EB%A7%88%EB%A6%AC-%EB%B0%9C%EA%B2%AC&quot; aria-label=&quot; 실마리 발견 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;💡 실마리 발견&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; created_at&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; start_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; created_at
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; trip_schedule
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-01-01&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-06-01&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; is_private &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; created_at &lt;span class=&quot;token keyword&quot;&gt;DESC&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;OFFSET&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;문제가 발생하는 원인을 찾기위해 이것저것을 여러 방식으로 시도해봤습니다. 단일 인덱스가 아닌 복합 인덱스를 통해 쿼리 성능 개선을 다시 시도해보기도 했고, &lt;code class=&quot;language-text&quot;&gt;BETWEEN&lt;/code&gt; 절에 들어가는 범위의 시작값과 끝값을 늘리고 줄여보면서 원인을 찾아보려고 시도했습니다.&lt;/p&gt;
&lt;h3 id=&quot;-복합-인덱스를-생성하지-않았기-떄문인가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-%EB%B3%B5%ED%95%A9-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%EC%83%9D%EC%84%B1%ED%95%98%EC%A7%80-%EC%95%8A%EC%95%98%EA%B8%B0-%EB%96%84%EB%AC%B8%EC%9D%B8%EA%B0%80&quot; aria-label=&quot; 복합 인덱스를 생성하지 않았기 떄문인가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;🤔 복합 인덱스를 생성하지 않았기 떄문인가...?&lt;/h3&gt;
&lt;p&gt;처음에는 복합 인덱스를 생성하지 않았기 떄문에 쿼리가 경우에 따라 소요시간이 천차만별인 줄 알았습니다. 지난 쿼리 성능 개선기 포스팅에서 다루었듯이, 저희는 아래처럼 WHERE 절에서 사용하는 &lt;code class=&quot;language-text&quot;&gt;created_at&lt;/code&gt; 에 대해서만 인덱스를 생성했습니다. 복합 인덱스로 생성하지 않았던 이유는 지난번에 자세히 다루었으므로 여기서는 생략합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; created_at&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; start_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; created_at
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; trip_schedule
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-01-01&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-06-01&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; is_private &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; created_at &lt;span class=&quot;token keyword&quot;&gt;DESC&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;OFFSET&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이번에는 WHERE 절에 사용되는 &lt;code class=&quot;language-text&quot;&gt;is_private&lt;/code&gt; 컬럼에 대해서도 함께 포함시킨 복합 인덱스를 생성하여 쿼리 성능 문제를 해결해보고자 했습니다. &lt;code class=&quot;language-text&quot;&gt;is_private&lt;/code&gt; 컬럼은 카디널리티 수치가 낮기 떄문에 인덱스에 포함시키지 않았던 것인데, 경우에 에따라 인덱스를 운이 좋게 잘 탄다면 성능 개선에 도움되지 않을까라는 기대를 해보았죠.&lt;/p&gt;
&lt;p&gt;하지만 이 방법 또한 문제의 원인이 아니였습니다. WHERE 절에 걸린 모든 인덱스에 대한 복합 인덱스를 생성하더라도 성능 개선은 매우 미미한 상태였죠. 대체 무엇이 문제였을까요?&lt;/p&gt;
&lt;h3 id=&quot;-offset-값이-클-때만-발생하는-문제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-offset-%EA%B0%92%EC%9D%B4-%ED%81%B4-%EB%95%8C%EB%A7%8C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EB%AC%B8%EC%A0%9C&quot; aria-label=&quot; offset 값이 클 때만 발생하는 문제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;🎯 OFFSET 값이 클 때만 발생하는 문제&lt;/h3&gt;
&lt;p&gt;이렇게 여러 방법을 시도해보던 중 재밌는 점을 하나 발견했습니다. 신기하게도 맨 마지막에 &lt;code class=&quot;language-text&quot;&gt;OFFSET&lt;/code&gt; 에 들어가는 값을 늘렸다가 줄여보니, &lt;code class=&quot;language-text&quot;&gt;OFFSET&lt;/code&gt; 값에 따라서 쿼리 소요시간이 매우 다양하게 측정된다는 점입니다.&lt;/p&gt;
&lt;p&gt;만약 &lt;code class=&quot;language-text&quot;&gt;OFFSET&lt;/code&gt; 값이 매우 작다면 앞선 테스트와 같이 50ms 의 소요시간이 측정되기도 했습니다. 반면 &lt;code class=&quot;language-text&quot;&gt;OFFSET&lt;/code&gt; 값이 매우 크다면, MySQL 실행계획을 통해 조회한 결과 지난번과 동일하게 36초가 넘는 소요시간이 비슷하게 측정되기도 하였습니다. 이로보아, 이번 쿼리 성능 저하의 주범은 OFFSET 과 관련이 있겠다는 강한 확신이 들었습니다. OFFSET 값에 따라서, 상황에 따라 테이블 풀 스캔과 비슷하게 쿼리가 인덱스를 잘 타지 않고 수행되고 있다는 강한 느낌이 들었습니다.&lt;/p&gt;
&lt;h3 id=&quot;페이지네이션을-사용하는-플래너-api&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%ED%94%8C%EB%9E%98%EB%84%88-api&quot; aria-label=&quot;페이지네이션을 사용하는 플래너 api permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;페이지네이션을 사용하는 플래너 API&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;OFFSET&lt;/code&gt; 은 어디서 사용되는 절일까요? 바로 페이지네이션 로직에서 사용되는 쿼리 절이랍니다. 저희 서비스에서 제공하는 플래너 API 는 페이지네이션을 사용합니다. JPA 에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;Pageable&lt;/code&gt; 을 사용하여 URL 쿼리 스트링으로 전달받은 값을 기반으로 &lt;code class=&quot;language-text&quot;&gt;OFFSET&lt;/code&gt; 을 설정하게 되었습니다. 추가적으로 &lt;code class=&quot;language-text&quot;&gt;PageableDefault&lt;/code&gt; 를 사용하여 LIMIT=30 이 쿼리에 나가도록 지정해주었습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/search/date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FindPlannerPublicForCreatedAtRangeResponses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findPublicSchedulesForCreatedAtRange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                                    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Authentication&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Accessor&lt;/span&gt; accessor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                    &lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestBody&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FindPublicSchedulesForRangeRequest&lt;/span&gt; findPlannerOrderByDateBetweenRequest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                    &lt;span class=&quot;token annotation punctuation&quot;&gt;@PageableDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;size &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pageable&lt;/span&gt; pageable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;plannerService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findPublicSchedulesForCreatedAtRange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;findPlannerOrderByDateBetweenRequest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pageable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드를 보시다시피 플래너 API 는 페이지네이션을 사용하며, 이 페이지네이션 로직은 &lt;code class=&quot;language-text&quot;&gt;OFFSET&lt;/code&gt; 쿼리절을 사용합니다. 실제로 아래와 같이 page 옵션을 뒤에 붙여서 쿼리가 나가도록 동작합니다. 이로써 결국 이번 쿼리 성능 발생 저하의 주범은 페이지네이션을 가능성이 반확정할 수 있었습니다. 왜 대체 페이지네이션이 문제였을까요?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;https&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ip_address&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;api&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;planner&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;search&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;date&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;page&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;-원인은-페이지네이션-안에-담긴-offset&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-%EC%9B%90%EC%9D%B8%EC%9D%80-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98-%EC%95%88%EC%97%90-%EB%8B%B4%EA%B8%B4-offset&quot; aria-label=&quot; 원인은 페이지네이션 안에 담긴 offset permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;🧵 원인은 페이지네이션 안에 담긴 OFFSET&lt;/h2&gt;
&lt;p&gt;우리가 보통 &lt;strong&gt;페이지네이션(Pagnation)&lt;/strong&gt; 처리를 위해 흔히 사용하는 LIMIT, OFFSET 방식은 어떻게 동작할까요? 페이지네이션은 맨 앞에서부터 순차대로 OFFSET 값 만큼의 모든 ROW 를 읽은 후, LIMIT 값 만큼의 ROW 를 가져오는 방식으로 동작합니다. 따라서 단순한 서비스에서는 성능상 큰 문제가 되지 않습니다. 하지만, 많은 ROW 가 저장되어 있는 상태에서 &lt;strong&gt;OFFSET 값을 매우 크게 설정하면 심각한 성능 저하&lt;/strong&gt; 문제가 발생하게 됩니다. 아래 예시를 살펴봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; PERSON &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;BETWEEN&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100000&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;OFFSET&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; PERSON &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;BETWEEN&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100000&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;OFFSET&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 2개의 쿼리는 모두 WHERE 절에서 클러스터링 인덱스가 자동 생성된 PK 값을 통해 범위 탐색을 시도하고 있습니다. 또한 LIMIT, OFFSET 절을 통한 페이지네이션 처리를 시도하고 있죠. 둘의 가장 큰 차이점은 OFFSET 값이 다르다는 점입니다. &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 의 경우 10번째 페이지부터 3개의 데이터를 읽어오기 위해 맨 앞에서부터 10개의 행을 거쳐서 이동해야합니다. 반면 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 의 경우 맨 앞에서부터 10,000개의 행을 거쳐서 이동해야합니다. 당연하게도, &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에 비해 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 가 읽어들이며 이동해야 하는 범위가 매우 크기 떄문에 성능이 더 나쁩니다. 앞서 설명했듯이, 페이징은 맨 앞에서부터 순차대로 모든 행을 읽고난 뒤에, 그제서야 LIMIT 만큼의 행을 가져오는 방식으로 동작하기 떄문이죠.&lt;/p&gt;
&lt;h3 id=&quot;random-io-가-발생하는-페이지네이션&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#random-io-%EA%B0%80-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98&quot; aria-label=&quot;random io 가 발생하는 페이지네이션 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Random I/O 가 발생하는 페이지네이션&lt;/h3&gt;
&lt;p&gt;이전 포스팅에서 다루었듯이, 저희는 &lt;code class=&quot;language-text&quot;&gt;created_at&lt;/code&gt; 에 대해서만 단일 인덱스를 생성하였습니다. 하지만 이렇게 인덱스를 생성할 경우의 문제점은, 커버링 인덱스가 아니기 때문에 &lt;strong&gt;Random I/O&lt;/strong&gt; 가 발생한다는 점입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; created_at&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; start_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; created_at
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; trip_schedule
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-01-01&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-06-01&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; is_private &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; created_at &lt;span class=&quot;token keyword&quot;&gt;DESC&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;OFFSET&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;MySQL 의 LIMIT 절의 특성은, LIMIT 절에서 필요한 레코드 건수만 준비되며 즉시 쿼리를 종료한다는 것입니다. 하지만, 커버링 인덱스를 생성하지 않는 일반적인 모든 조회 쿼리는 LIMIT - OFFSET 을 수행할 때 실제 레코드에 접근하는 Random I/O 가 발생합니다. 즉, 커버링 인덱스로 처리할 수 없는 조회 쿼리는 테이블의 실제 레코드를 맨 처음부터 OFFSET 값 만큼의 모든 레코드를 읽어야합니다. 위 쿼리의 경우 5,000건의 실제 레코드를 읽는 Random I/O 가 발생합니다. 그렇게 읽은 5,000건의 데이터는 모두 버리고 마지막 30건만 사용자에게 결과값을 반환하게 됩니다. 즉, &lt;strong&gt;OFFSET의 수치가 커질수록 Random I/O 의 발생횟수는 비례해서 증가&lt;/strong&gt;하고, 이 떄문에 상당히 오랜 시간이 걸리게 되는 것입니다.&lt;/p&gt;
&lt;h2 id=&quot;커버링-인덱스-vs-커서-기반-페이징&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EB%B2%84%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4-vs-%EC%BB%A4%EC%84%9C-%EA%B8%B0%EB%B0%98-%ED%8E%98%EC%9D%B4%EC%A7%95&quot; aria-label=&quot;커버링 인덱스 vs 커서 기반 페이징 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커버링 인덱스 vs 커서 기반 페이징&lt;/h2&gt;
&lt;p&gt;이를 해결하기 위한 대표적인 2가지 방법이 존재합니다. 하나는 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 커버링 인덱스를 생성하여 Random I/O 를 제거하는 방식이고, 다른 하나는 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 커서 기반 페이징 기법을 적용하는 것입니다. 일반적으로 권장하는 방법은 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 방식읕 커버 기반 페이징 방식입니다. 이 커버 기반 페이징 방식이란 무엇일까요?&lt;/p&gt;
&lt;h3 id=&quot;커버-기반-페이징-cursor-based-paging&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EB%B2%84-%EA%B8%B0%EB%B0%98-%ED%8E%98%EC%9D%B4%EC%A7%95-cursor-based-paging&quot; aria-label=&quot;커버 기반 페이징 cursor based paging permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커버 기반 페이징 (Cursor Based Paging)&lt;/h3&gt;
&lt;p&gt;커버 기반 페이징 방식이란 &lt;code class=&quot;language-text&quot;&gt;OFFSET&lt;/code&gt; 키워드를 사용하지 않는 것으로, 커서 값을 클라이언트에서 매번 일일이 저장해두었다가 다음 페이지는 가져오는 방식입니다. 아래 코드는 저희의 OFFSET 기반 쿼리문을 커서 기반 페이징으로 변환한 쿼리문입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; created_at&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; start_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end_date
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; trip_schedule
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; {커서값} 
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-01-01&apos;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-06-01&apos;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; is_private &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; created_at &lt;span class=&quot;token keyword&quot;&gt;DESC&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;가장 변화된 지점은 바로 WHERE 절에 ID 값이 추가되었으며, OFFSET 이 없어졌다는 점입니다. 이렇듯 커서 기반 페이징은 OFFSET 을 제거한 방식입니다. 그 대신에 단점은, 클라이언트에서 매번 일일이 어떤 지점에서부터 또 다시 다음 페이지 데이터들을 읽어들어야 할지 항상 기억하고 있어야한다는 점입니다. 이때 &lt;strong&gt;커서(Cursor)&lt;/strong&gt; 란 위처럼 ID 값이 될 것입니다. 커서란 말 그대로 포인터와 같은 것으로, 한번 데이터를 LIMIT 만큼 읽어들인 뒤 또 다시 데이터를 조회하기 위해 그 위치 값을 클라이언트에서 저장하는 방식입니다.&lt;/p&gt;
&lt;h3 id=&quot;-커서-기반-페이징을-도입하지-않은-이유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-%EC%BB%A4%EC%84%9C-%EA%B8%B0%EB%B0%98-%ED%8E%98%EC%9D%B4%EC%A7%95%EC%9D%84-%EB%8F%84%EC%9E%85%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%80-%EC%9D%B4%EC%9C%A0&quot; aria-label=&quot; 커서 기반 페이징을 도입하지 않은 이유 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;😤 커서 기반 페이징을 도입하지 않은 이유&lt;/h3&gt;
&lt;p&gt;커서 기반 페이징은 정말 좋은 방식입니다. OFFSET 을 사용하지 않기 떄문에 쿼리 성능이 매우 빨라진다는 점이 존재하죠. OFFSET 방식에서는 원하는 소량의 데이터를 읽기 위해 OFFSET 가 커질수록 불필요하게 Random I/O 가 많이 발생한다는 치명적인 문제점이 존재했습니다. 반면, 커서 기반 페이징은 LIMIT 만큼에 대해서만 Random I/O 가 발생하므로, 성능 저하를 우려할 필요가 없죠.&lt;/p&gt;
&lt;p&gt;하지만 문제는, 커서 기반 페이징방식은 이전 페이지(커서) 값을 기준으로 다음에 읽어들일 데이터를 페이징하기 떄문에, &lt;strong&gt;특정 페이지로 건너 뛰어서 넘어갈 수 없다는 한계점&lt;/strong&gt;이 존재합니다. 즉, 커서 기반 페이징은 &lt;strong&gt;곧 바로 다음 페이지로만 이동 가능하다는 문제점&lt;/strong&gt;이 존재합니다. 따라서 무한 스크롤이나 More 버튼이 있는 형태로만 페이징 기능을 사용 가능합니다.&lt;/p&gt;
&lt;p&gt;무엇보다 저희 플래너 API 에 사용되는 쿼리에 &lt;strong&gt;커서로 활용될 컬럼으로 어떤 컬럼을 지정할지 모호하다&lt;/strong&gt; 는 문제점이 존재합니다. 커서 기반 페이징은 다음에 어떤 페이지를 읽어들일지 알 수 있도록 커서 값을 클라이언트가 저장한다고 했었죠. 그런데, 만약 커서 값이 유니크(Unique) 하지 않고 중복되는 값이 존재한다면 어떤 문제가 발생할까요? 아래 쿼리를 다시 살펴봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; created_at&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; start_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end_date
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; trip_schedule
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; {커서값} 
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-01-01&apos;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-06-01&apos;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; is_private &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; created_at &lt;span class=&quot;token keyword&quot;&gt;DESC&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;보듯이 &lt;code class=&quot;language-text&quot;&gt;created_at&lt;/code&gt; 이 커서로 활용되고 있습니다. 하지만, &lt;code class=&quot;language-text&quot;&gt;created_at&lt;/code&gt; 은 플래너 여행 일정 등록날짜로, 유니크(Unique) 하다는 보장이 없습니다. 만약에 현재 커서값에 활용되는 created_at 에 2020-03-10 이 할당된 상태이고, 이 2020-03-10 이라는 중복된 값을 가지는 데이터 건수가 10개가 넘는다면 어떻게 될까요? 어쩔 수 없이 MySQL 쿼리는 그 10건의 데이터 중에 정확히 어떤 레코드부터 시작하여 LIMIT 값 만큼 읽어와야할지 알 수 없기 떄문에, 그 10건중 임의로 한 데이터를 읽어올 수 밖에 없습니다. 즉, 커서 기반 페이징에서 &lt;strong&gt;커서 값은 반드시 유니크(Unique) 해야 하지만, 쿼리 특성상 created_at 은 유니크하지 않으므로 문제가 발생합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;그렇다면 커서 값을 중복되지 않은 유니크한 컬럼을 선택하면 문제가 해결될 것입니다. 이를위해 PK 컬럼에 대해 커서로 지정해볼 수 있겠죠. 하지만, BETWEEN 절로 정렬되는 결과물의 여행 일정 리스트는 PK 를 기반으로 정렬되지 않고, created_at 을 기반으로 정렬됩니다. 즉, 위 쿼리에서 &lt;strong&gt;ORDER BY&lt;/strong&gt; 절을 통해 created_at 로 정렬된 결과 리스트에서 PK 의 크기는 서로 뒤죽박죽 이라는 점입니다. 따라서 PK 는 커서로 활용할 수 없습니다. 정리하자면, 커서 기반 페이징은 특정 컬럼에 의존하여 데이터를 정렬하기 때문에, &lt;strong&gt;고유하지 않은 필드로 정렬할 경우, 커서 값이 중복&lt;/strong&gt;될 가능성이 있어 데이터가 누락되거나 중복될 수 있다는 문제점이 발생합니다.&lt;/p&gt;
&lt;p&gt;이러한 이유로, 저희 서비스의 &lt;strong&gt;요구사항 특성상 커서 기반 페이징을 도입하기엔 어렵다는&lt;/strong&gt; 결론을 내리게 되었습니다. 비슷한 이유로, 역방향 페이징도 어렵고, 클러이언트와 서버 양측 모두 커서 관리의 부담이 커지며, 커서를 자유롭게 이동하면서 여행 일정을 편하게 조회할 수 없다는 문제점이 존재하므로 도입하지 않았습니다.&lt;/p&gt;
&lt;h2 id=&quot;-커버링-인덱스를-생성한-쿼리-성능-최적화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-%EC%BB%A4%EB%B2%84%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%EC%83%9D%EC%84%B1%ED%95%9C-%EC%BF%BC%EB%A6%AC-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94&quot; aria-label=&quot; 커버링 인덱스를 생성한 쿼리 성능 최적화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;🎯 커버링 인덱스를 생성한 쿼리 성능 최적화&lt;/h2&gt;
&lt;p&gt;앞서 설명했듯이, 커서 기반 페이징 또는 커버링 인덱스를 생성하여 이번 문제를 해결할 수 있다고 했습니다. 저희는 결론적으로 커서 기반 페이징 방식이 아닌 커버링 인덱스를 생성하여 쿼리 성능 문제를 해결했습니다.&lt;/p&gt;
&lt;p&gt;커버링 인덱스는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/index-range-scan-improvement/&quot;&gt;MySQL 인덱스 레인지 스캔을 통한 쿼리 성능 개선기&lt;/a&gt; 에서도 다루었듯이, &lt;strong&gt;Random I/O 자체를 완전히 제거&lt;/strong&gt;해버릴 수 있는 방식입니다. 커버링 인덱스란 쿼리내에 수행되는 모든 컬럼을 복합 인덱스가 모두 포함하고 있는 인덱스를 뜻한다고 했었죠. 이 경우 인덱스 이 경우 인덱스가 필요로하는 모든 데이터를 이미 가지고 있기 떄문에, 실제 레코드에 접근하여 데이터를 조회하는 랜덤 I/O 가 절대 발생하지 않습니다. 따라서 성능이 매우 빨라집니다.&lt;/p&gt;
&lt;p&gt;쿼리 성능 문제가 발생했던 쿼리를 아래처럼 다시 살펴봅시다. 이전에는 &lt;code class=&quot;language-text&quot;&gt;created_at&lt;/code&gt; 에 대해서만 단일 인덱스를 생성해주었다면, 커버링 인덱스를 위해 이번엔 &lt;code class=&quot;language-text&quot;&gt;created_at&lt;/code&gt; 은 몰론 SELECT 절에서 사용되는 모든 컬럼을 복합 인덱스에 포함시켜 주도록 해야합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; created_at&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; start_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; created_at
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; trip_schedule
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-01-01&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-06-01&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; is_private &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; created_at &lt;span class=&quot;token keyword&quot;&gt;DESC&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;OFFSET&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;실제로 저희 팀은 아래와 같이 커버링 인덱스를 생성함으로써, OFFSET 으로 인해 발생하는 쿼리 성능 문제를 해결하였습니다. 이로써 OFFSET 으로 인해 Random I/O 가 발생하는 일을 최소할 수 있겠죠?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; trip_schedule
&lt;span class=&quot;token keyword&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; trip_schedule_covering_idx&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;created_at&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; is_private&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; start_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end_date&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;성능-개선-확인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0-%ED%99%95%EC%9D%B8&quot; aria-label=&quot;성능 개선 확인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;성능 개선 확인&lt;/h3&gt;
&lt;p&gt;성능 개선 정도를 확인하기 위해, Jmeter 를 사용하여 실제 API 의 평균 응답시간을 분석했습니다. 1초 간격으로 100번의 요청을 보내 평균 응답 시간을 냈습니다. 또한 MySQL 의 실행계획을 측정하여 순수하게 쿼리 성능이 어느정도로 개선되었는지 또한 측정하였습니다. Page 값은 임의로 약 10만을 부여하였습니다.&lt;/p&gt;
&lt;h3 id=&quot;성능-개선-전&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0-%EC%A0%84&quot; aria-label=&quot;성능 개선 전 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;성능 개선 전&lt;/h3&gt;
&lt;p&gt;우선 플래너 API 호출 평균 응답시간은 &lt;code class=&quot;language-text&quot;&gt;4,637ms&lt;/code&gt; 가 측정되었네요. 심지어 간혹 Conneciton Timeout 으로 인해 애러율이 약 6.8%라는 수치가 도출되기도 했습니다. 상황에 따라 타임아웃이 발생하고, 평균 4.6초 이상이나 기다려야하는 응답시간을 받는 사용자 입장에선 큰 불편함을 느낄 수 밖에 없을 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4756643443c898ab275a8171f0befa69/0940f/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 22.699386503067483%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAvUlEQVR42o1QSRLDIAzLv5qFPUBCVqD9/0dc2+l0cuihB83IkmUbGmUcSGNBuxHGOIH1kbkLEyKCsg61AGYM2HdxAunkW9Yt+9p5aPZcIcwLN6Rth+3MPHBaVubUTEHqEfriad15OGmU19bjMTOsx4kDS+HLBqWhkwq2XDjUCvnlj37gBTEl5metMGMtcFl+vsBjvsfsUSo0CkUqaCBB46VSG+bkiQ+nSwms2+ubyKPX3PWmxY3dIH7i7v3L37hktTimH0YKAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/4756643443c898ab275a8171f0befa69/a6d36/image-6.png&quot;
        srcset=&quot;/static/4756643443c898ab275a8171f0befa69/222b7/image-6.png 163w,
/static/4756643443c898ab275a8171f0befa69/ff46a/image-6.png 325w,
/static/4756643443c898ab275a8171f0befa69/a6d36/image-6.png 650w,
/static/4756643443c898ab275a8171f0befa69/e548f/image-6.png 975w,
/static/4756643443c898ab275a8171f0befa69/0940f/image-6.png 1154w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;성능-개선-후&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0-%ED%9B%84&quot; aria-label=&quot;성능 개선 후 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;성능 개선 후&lt;/h3&gt;
&lt;p&gt;성능 개선 후에는 놀라운 변화가 일어났습니다. 기존 4.6초의 평균 응답식나이 522ms, 즉 0.52초라는 평균 응답시간으로 크게 개선되었습니다. 이로써 Random I/O 발생 유무 차이가 얼마나 성능에 큰 영향을 끼치는지 알 수 있죠. 페이지네이션 로직에서 커버링 인덱스를 적용한 쿼리 튜닝 방법은 API 호출에 &lt;strong&gt;약 8.8배 개선되었습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3e00bc3fa6b6076e7ef6af2a404ab016/9937c/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 16.56441717791411%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAkElEQVR42j2NWxaDMAhE3Zg274cxJmrTdv8bmQL19IPDnYGByYaEdj7hYkZcN/RrwKcVaavYzwvKR6RS4XOBcgFl7+BMIM05e+e2dog/1X7gGi8SEbM2YF1bx6y0cMircKYHxgcp9pV1OMegBw0PYzHeHzpcMGkasLHQMS5FzN6ijISE9Y/VvSeZu//Zedn5AoXVbdu2nBvhAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/3e00bc3fa6b6076e7ef6af2a404ab016/a6d36/image-5.png&quot;
        srcset=&quot;/static/3e00bc3fa6b6076e7ef6af2a404ab016/222b7/image-5.png 163w,
/static/3e00bc3fa6b6076e7ef6af2a404ab016/ff46a/image-5.png 325w,
/static/3e00bc3fa6b6076e7ef6af2a404ab016/a6d36/image-5.png 650w,
/static/3e00bc3fa6b6076e7ef6af2a404ab016/e548f/image-5.png 975w,
/static/3e00bc3fa6b6076e7ef6af2a404ab016/9937c/image-5.png 1156w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;MySQL 실제 쿼리 응답속도 또한 직접 확인한 결과, 쿼리 튜닝 후 응답속도가 17ms 가 측정되었네요. 기존 성능 개선전에 &lt;strong&gt;약 36.838초가 소요된것에 비해 0.017초로 극명하게 개선&lt;/strong&gt;된 것을 확인할 수 있었습니다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/36339a050655af6b4f0854161c5f0ad3/985a9/image-8.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 20.858895705521473%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAvUlEQVR42jVPW1LDMAzMORho4ocs2bGTppQC9z/YIonxx85a8mgfi9SB3jqeo2P0geu84+wdhQpYdtQ2ULhBavd5sqHtB7aQcVsj1i06L2/vKyhlFzzOB87HC6wC3ARbIWT9k1JhxikVBCIEVs4Z7Kb9H8SqQ1i2QHog+L7uuK5P/Dy/sL9+0caBGPVYEVUoZXa2RHM3YbvVUyYsVknUwapUrSDSQFZTE826s6rNpGmpNH/bvvDuoh+34JX/AM5sdMHXboRjAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/36339a050655af6b4f0854161c5f0ad3/a6d36/image-8.png&quot;
        srcset=&quot;/static/36339a050655af6b4f0854161c5f0ad3/222b7/image-8.png 163w,
/static/36339a050655af6b4f0854161c5f0ad3/ff46a/image-8.png 325w,
/static/36339a050655af6b4f0854161c5f0ad3/a6d36/image-8.png 650w,
/static/36339a050655af6b4f0854161c5f0ad3/e548f/image-8.png 975w,
/static/36339a050655af6b4f0854161c5f0ad3/3c492/image-8.png 1300w,
/static/36339a050655af6b4f0854161c5f0ad3/985a9/image-8.png 1512w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이렇게 페이지네이션에서 발생하는 OFFSET 으로 인한 쿼리 성능 이슈를 해결할 수 있었습니다. 저희 서비스 요구사항에 알맞게 커서 기반 페이징 방식과 커버링 인덱스를 통한 2가지 방식을 비교헀으며, 결국 커버링 인덱스를 통한 성능 개선을 이루어낼 수 있었습니다. 무엇보다 Random I/O 가 발생함으로 인해 발생하는 성능 저하가 생각보다 크다는 것을 이번 기회에 직접 체감할 수 있었습니다.&lt;/p&gt;
&lt;p&gt;다만, 지금의 개선 구조는 완벽한 구조가 아니라는 점입니다. 커서 기반 페이징 방식에서도 문제점이 존재하듯이, 커버링 인덱스를 통해 쿼리 튜닝 방식 몰론 문제점이 존재하죠. 지난 포스팅에서 커버링 인덱스가 아닌 단일 인덱스를 도입했던 이유처럼, &lt;strong&gt;커버링 인덱스는 무턱대고 도입하는 것이 좋은 것이 아닙니다.&lt;/strong&gt; 결국 쿼리의 모든 항목이 포함되기 떄문에 인덱스 크키가 커질 수 있고, 아무리 커버링 인덱스가 빠르다고 한들 커버 기반 페이징 방식과 달리 소요시간이 O(n) 으로 선형적으로 증가하는 문제는 여전이 존재하긴 합니다. 다만, 그럼에도 커버 기반 페이징 방식에 비해 더 좋은 이점들도 존재하기 떄문에 커버링 인덱스를 생성하여 문제를 해결해습니다. 게다가 소요시간이 선형적으로 증가하겠지만, 큰 값을 가지는 OFFSET 이 등장한다고 한들 대부분 커버링 인덱스로 최적화되어 빠른 성능이 도출됩니다.&lt;/p&gt;
&lt;p&gt;지금보다 더 좋은 방법이 또 있을지, 커서 기반 페이징 방식외에도 다른 방식도 존재하는지를 계속 고민해보는 시간을 가져볼까 합니다. 그럼 이만 포스팅을 마쳐보겠습니다 😎&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://jojoldu.tistory.com/529?category=637935&quot;&gt;https://jojoldu.tistory.com/529?category=637935&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jojoldu.tistory.com/528&quot;&gt;https://jojoldu.tistory.com/528&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jojoldu.tistory.com/476&quot;&gt;https://jojoldu.tistory.com/476&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://oneny.tistory.com/110&quot;&gt;https://oneny.tistory.com/110&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://taegyunwoo.github.io/tech/Tech_DBPagination&quot;&gt;https://taegyunwoo.github.io/tech/Tech_DBPagination&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@ppinkypeach/%EC%BB%A4%EB%B2%84%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-Offset-%EA%B8%B0%EB%B0%98-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0&quot;&gt;https://velog.io/@ppinkypeach/커버링-인덱스를-활용한-Offset-기반-페이지네이션-성능-개선&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chem-coder.tistory.com/186&quot;&gt;https://chem-coder.tistory.com/186&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL 인덱스 레인지 스캔을 통한 쿼리 성능 개선기]]></title><description><![CDATA[💡 2024.11.17 추가 : 이 포스팅에서 진행한 쿼리 성능 개선기는 스프링 페이지네이션에서 발생한 Latency…]]></description><link>https://haon.site/database/index-range-scan-improvement/</link><guid isPermaLink="false">https://haon.site/database/index-range-scan-improvement/</guid><pubDate>Fri, 15 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 2024.11.17 추가 : 이 포스팅에서 진행한 쿼리 성능 개선기는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/pagnation-latency-db-covering-index/&quot;&gt;스프링 페이지네이션에서 발생한 Latency 의 원인과 커버링 인덱스 생성을 통한 문제 해결기&lt;/a&gt; 로 이어집니다. 페이지네이션 로직에 대한 커버링 인덱스 적용기를 참고해주세요! 🙂&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;오랜만의 포스팅이다. 이번 포스팅에선 모행 서비스에서 SLOW QUERY 가 발생한 문제 상황을 어떻게 해결했는지에 대해 다루어보고자 한다. 데이터베이스 인덱스의 개념와 특징에 대해 다시금 곱씹어보며 어떠한 방식으로 인덱스를 적용했는지에 대한 근거를 공유해볼까 한다. 또한 우리 서비스내에 데이터베이스 인덱스를 생성한 사례 중 하나를 중점으로 자세히 다루어보고자 한다. 즉, 우리 서비스내에 생성한 인덱스 종류에는 이번 포스팅에서 다룰 인덱스 단 1가지 종류만 존재하는 것이 아니라는 것을 말하고 싶다 😎&lt;/p&gt;
&lt;h2 id=&quot;문제-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%A0%9C-%EC%83%81%ED%99%A9&quot; aria-label=&quot;문제 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문제 상황&lt;/h2&gt;
&lt;p&gt;우리 하모니 팀은 높은 &lt;strong&gt;가용성(Availability) 확보와 사용자 경험을 향상하기 위해 다양한 성능 개선을 시도&lt;/strong&gt;하고 있다. 지금까지 데이터베이스 레플리케이션을 통한 부하 분산, 로컬 캐시를 사용한 API 요청 최적화, 톰캣 서버 튜닝, HikariCP 적절 사이즈 튜닝, N+1해결등 수많은 문제를 해결해왔다. 하지만, 결국 데이터베이스내에 대량의 데이터가 쌓인다면 여러 성능 튜닝을 했음에도 &lt;strong&gt;SLOW QUERY&lt;/strong&gt; 가 발생할 수 밖에 없다.&lt;/p&gt;
&lt;p&gt;실제로 우리 팀내에도 대량의 더미 데이터를 삽입한 뒤 쿼리 속도를 측정한 결과 &lt;strong&gt;Long Query&lt;/strong&gt; 가 발생하고 있다. 이는 시스템내에 성능 저하가 발생 요인이 되며, 한 커넥션을 붙잡고 있는 시간이 길어지므로 시스템 전체에 성능 저하와 병목 현상을 일으킬 수 있다. 이 문제를 해결하기 위해 데이터베이스 인덱스 생성을 통한 쿼리 성능을 개선했다. 이번 포스팅에서 대량의 더미 데이터를 삽입한 상황속에서 인덱스를 생성한 쿼리 적용 과정, 고민 사항에 대해 다루어보고자 한다.&lt;/p&gt;
&lt;h2 id=&quot;플래너-api-특성-파악하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%8C%EB%9E%98%EB%84%88-api-%ED%8A%B9%EC%84%B1-%ED%8C%8C%EC%95%85%ED%95%98%EA%B8%B0&quot; aria-label=&quot;플래너 api 특성 파악하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;플래너 API 특성 파악하기&lt;/h2&gt;
&lt;p&gt;우리 서비스에서 향후 출시할 플래너 공유 기능을 구현하고 테스트를 진행하고 있다. 이 API 는 향후 커뮤니티 기능으로 출시될 플래너 공유 기능에 활용될 예정이다. 대량의 데이터중 빠르게 원하는 여행 일정(플래너)를 찾을 수 있도록 등록 날짜를 기준으로 검색하는 기능이 존재한다. 아래처럼 검색할 시작날짜와 종료날짜를 기준으로 공개된 (비공개 상태 OFF) 여행 일정을 조회할 수 있도록 지원한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1c5a8993138079d0d86026d45b1ceb0e/c83ae/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 73.00613496932516%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAACXklEQVR42nVU2W4TQRDMp/MbPMF7kBASEpGQIDwkQBInEhDs+Ipsx8fuOnvYe8zOVdTMboidwEilnaO7urqnZw+sNbDWArAQhcbkzGJ8YjH5bjH4ZNH9YNE72gHXw2MDsaUfDPJNjTyrURUShlwHsjYoywpKK6jKYHmdYfEzw/JXimknJXHGIPuYdjJIBtfa4N3LLl69uMLnwxHcODA658GWKgsucyJk5IDfqEX4T1i7gTFbVGVKJJC1aAjD8Brd7gmGwzMuVwimd+hdjJBmIyTxLZ0COod7cIGSZIjl6hr3cZ8ZTrhXNoRSzlEUU1TVnDUIoWXAuqyR5xGESFrDwqPJIvOEUi68jxBzOI7mjIRR1MPNzTeMxx0oteRWjM0mQb8/wP197I2aSwPVWs4rn3KWjREEPdoMnissyxkj3fn0XHSleElKM7Lkfu3nSimuHXIfuFE4I+6eKuyyhl8xGl20CiMSS3+DjsgY086V/zpCR5amY4ThDZL0loKmjwq1XtF45ckeFHpC5frTYL0qkK5L7lGl1KjrrSc0OsBk1MGPq2NEq9+PhE0brFs0raJ1jVoob3D85haXn+Z+Lj1hTixgdYjz0yO8PXyN2fiSp9UuYbQHpURbL8kaCaYk/FcIV9ONV+jaR8mlh1aLpwr3CZ1CVz+tNSqSbPMaZSVRUnUlmpQfs3rI7C/h6tkrcC+nFkyNzqJFvE6QxhnKIvK9t+8T7BK6Rt20yLyBEDO+hAFi9lgcO/Tx5f05Tj9eYD7rkniK5371A+HucA2seKM10yr3oHXJHwjBubUS/xt/AAlgf4LW5U+1AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/1c5a8993138079d0d86026d45b1ceb0e/a6d36/image-1.png&quot;
        srcset=&quot;/static/1c5a8993138079d0d86026d45b1ceb0e/222b7/image-1.png 163w,
/static/1c5a8993138079d0d86026d45b1ceb0e/ff46a/image-1.png 325w,
/static/1c5a8993138079d0d86026d45b1ceb0e/a6d36/image-1.png 650w,
/static/1c5a8993138079d0d86026d45b1ceb0e/e548f/image-1.png 975w,
/static/1c5a8993138079d0d86026d45b1ceb0e/c83ae/image-1.png 1180w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;그런데 문제는, 대량의 데이터가 삽입된 상황이라면 원하는 데이터를 찾아오는데만 &lt;strong&gt;36.839초 라는 매우 긴 시간이 소요된다는 점이다.&lt;/strong&gt; 이 떄문에 실제 API 를 테스트를 해봤을 때 자꾸만 &lt;strong&gt;Connection Timeout&lt;/strong&gt; 이 발생한다. &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/hikaricp-theory/&quot;&gt;HikariCP 와 데이터베이스 커넥션 풀(DBCP) 최적화 고민하기 - 이론편&lt;/a&gt; 에서 설명했듯이, 우리 팀의 HikariCP 커넥션 타임아웃 대기 시간 정책은 3초로 정해져있다. 3초이상 사용자를 대기시킨다면 큰 붎편함을 줄 것이라 생각했기 때문이다.&lt;/p&gt;
&lt;p&gt;결국 사용자는 원하는 데이터를 검색해서 찾기위해 대기하는 시간만 길어지고, 데이터를 조회하지 못한 못한 상태로 Timeout 되버린다. 사용자 경험에서 정말 좋지 못하다. 이 뿐만 아니라 시스템내에 Slow Query 로 인한 커넥션 고갈이 발생하여 성능 저하의 주원인이 될 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;실제-long-query-수행시간-측정해보기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%EC%A0%9C-long-query-%EC%88%98%ED%96%89%EC%8B%9C%EA%B0%84-%EC%B8%A1%EC%A0%95%ED%95%B4%EB%B3%B4%EA%B8%B0&quot; aria-label=&quot;실제 long query 수행시간 측정해보기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실제 Long Query 수행시간 측정해보기&lt;/h2&gt;
&lt;p&gt;실제로 플래너 조회 API 를 호출시 나가는 쿼리를 확인해보자. 테스트 서버내에 쌓인 데이터 건수는 아래와 같이 &lt;strong&gt;약 1,600만건의 더미데이터가 저장된 상황&lt;/strong&gt;임을 가정한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/764fc3d5d3fb50f64a770d90504660de/38af3/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.036809815950924%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABIUlEQVR42pVPaUvDUBB8/8PeiUmTNM193w1thYoFEfT//5Fxd9sKghX9MOwxb2fmqdPLG86vHyibEVUzYNw/YRiP6Hd7QdPtsD+e0PYjLGeLJKtof5CZ+brdwQ9T2BsPjutDtcOBiCNiehglObKiRppXUhlxWkhNslKO4rQU46LqBHU7yB2LMa944fqhuJvWBl4Q00EvD3jH1fVCSVbWHbK8RBBlMkdxhufzO9Kig319q2pyy4tGvtaQeMKJKGEQJnDIcaUZ0HSTsIb+uL72Ju0vVTdsrIjjmaG25O5uA/iU7NaHlMAPEpjrDSbTBaaz5S/4zqvbAdeHyRyGacPzI0lo2a5ws/nqz1CXRvtasAALM9jkP2IiuFhq+Bk67nP38QlppvHRgzHTcAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/764fc3d5d3fb50f64a770d90504660de/a6d36/image-3.png&quot;
        srcset=&quot;/static/764fc3d5d3fb50f64a770d90504660de/222b7/image-3.png 163w,
/static/764fc3d5d3fb50f64a770d90504660de/ff46a/image-3.png 325w,
/static/764fc3d5d3fb50f64a770d90504660de/a6d36/image-3.png 650w,
/static/764fc3d5d3fb50f64a770d90504660de/38af3/image-3.png 894w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;또한 쌓인 더미데이터는 다음과 같은 특징을 지닌다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;약 1,600만건의 데이터가 저장된 상황&lt;/li&gt;
&lt;li&gt;2020년 ~ 2023년도 사이의 등록된 데이터가 골구로 분포된 상태 (즉, 여행 일정 등록날짜가 특정 년도에만 밀집된 것이 아니라 골구로 분산되어 있다.)&lt;/li&gt;
&lt;li&gt;이 외에도 여행 일정의 name, description 필드의 데이터끼리 100만가지 이상으로 서로 다른 값을 가진 상태 (중복되는 데이터 값을 최소화했다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;쿼리-특성-파악하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BF%BC%EB%A6%AC-%ED%8A%B9%EC%84%B1-%ED%8C%8C%EC%95%85%ED%95%98%EA%B8%B0&quot; aria-label=&quot;쿼리 특성 파악하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쿼리 특성 파악하기&lt;/h3&gt;
&lt;p&gt;아래 쿼리는 &lt;code class=&quot;language-text&quot;&gt;WHERE&lt;/code&gt; 절을 통해 여행 일정 등록날짜 &lt;code class=&quot;language-text&quot;&gt;created_at&lt;/code&gt; 를 기준으로 범위내에 해당하는 여행 일정 리스트를 조회한다. 스프링부트 내에서 부하 분산을 위해 페이지네이션 로직을 적용했기 떄문에 &lt;code class=&quot;language-text&quot;&gt;LIMIT&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;OFFSET&lt;/code&gt; 이 함께 나가는 것을 확인할 수 있다. 추가적으로 &lt;code class=&quot;language-text&quot;&gt;WHERE&lt;/code&gt; 절에서 &lt;code class=&quot;language-text&quot;&gt;is_private&lt;/code&gt; 이라는 boolean 필드가 존재하여, 검색에 활용되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; created_at&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; start_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; created_at
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; trip_schedule
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-01-01&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-06-01&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; is_private &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; created_at &lt;span class=&quot;token keyword&quot;&gt;DESC&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;OFFSET&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이떄 페이지네이션 적용을 통한 &lt;code class=&quot;language-text&quot;&gt;LIMIT&lt;/code&gt; 쿼리가 나간다는 점도 중요하게 볼 사항 중 하나이다. 데이터베이스 인덱스는 커버링 인덱스를 타지 않는 이상 리프 페이지의 매핑되는 실제 레코드를 읽어오기 위해 Random I/O 가 발생할 수 밖에 없다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://haon.blog/database/index-scan-type/&quot;&gt;MySQL 에서 B+ Tree 기반 인덱스로 데이터를 스캔하는 방식 (인덱스 레인지 스캔)&lt;/a&gt; 에서 다루었듯이, 랜덤 I/O 는 순차 I/O 에 비해 비용이 꽤 많이 드는 작업이다. 따라서 MySQL 옵티마이저는 실제 데이터 레코드 수가 전체에서 20 ~ 25%를 넘는다면, 테이블의 인덱스를 타지 않고 레코드를 직접 읽어 순차 I/O 가 발생하는 &lt;strong&gt;테이블 풀 스캔(Table Full Scan)&lt;/strong&gt; 방식을 수행하도록 쿼리를 유도한다. 따라서 페이지네이션 로직을 적용함으로써 읽어야 할 &lt;strong&gt;데이터 건수가 20~25% 수준을 절대 넘어가지 않고 소량의 데이터만 읽도록&lt;/strong&gt; 하여, 항상 인덱스를 타도록 스프링부트 애플리케이션 로직을 개발했다.&lt;/p&gt;
&lt;p&gt;위 쿼리에 대한 실제 수행시간을 확인해보면 아래와 같다. &lt;strong&gt;약 1,600만건의 대량 데이터&lt;/strong&gt;가 서비스내에 저장된 상황이라면 &lt;strong&gt;약 36.839초&lt;/strong&gt; 이라는 매우 긴 시간이 소요된다. 이렇듯 데이터베이스 인덱스를 생성하지 않은 상황아라면 &lt;strong&gt;Slow Query&lt;/strong&gt; 가 수행된다는 것을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dc9bc77dd348a4352941f7c4057caeb9/f3015/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 18.404907975460123%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAArUlEQVR42l2OWRLDIAxDc5FmARtDCAHadHr/k6mGof3Ixxsh4W06j4QrHai54FUqYghwEnCkAu8j9ngi7KnjY+o+KstqsQ7mxfyZ2BI++USpF3J9Q8IO8oJls3DEcDrUWMFqCKtjbNaCyWndAWKvNdQHPeatM3m9pqQTOVfU5wXR7UyijQ5G1TQd2OHtLd8Mj7dgIg5a4MGy941N3Y2W9z8Xuhe9uvk2uGWNX/YFivd0IYcZsK0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/dc9bc77dd348a4352941f7c4057caeb9/a6d36/image-2.png&quot;
        srcset=&quot;/static/dc9bc77dd348a4352941f7c4057caeb9/222b7/image-2.png 163w,
/static/dc9bc77dd348a4352941f7c4057caeb9/ff46a/image-2.png 325w,
/static/dc9bc77dd348a4352941f7c4057caeb9/a6d36/image-2.png 650w,
/static/dc9bc77dd348a4352941f7c4057caeb9/e548f/image-2.png 975w,
/static/dc9bc77dd348a4352941f7c4057caeb9/3c492/image-2.png 1300w,
/static/dc9bc77dd348a4352941f7c4057caeb9/f3015/image-2.png 1804w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;데이터베이스-인덱스를-생성한-성능-개선-시도&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%EC%83%9D%EC%84%B1%ED%95%9C-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0-%EC%8B%9C%EB%8F%84&quot; aria-label=&quot;데이터베이스 인덱스를 생성한 성능 개선 시도 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 인덱스를 생성한 성능 개선 시도&lt;/h2&gt;
&lt;p&gt;이러한 문제 상황이 발생하므로, 데이터베이스 인덱스를 통한 Slow Query 를 제거할 필요성을 느낄 수 있다. 하지만 무턱대고 인덱스를 마구잡으로 생성한다면, 자칫 되려 성능 저하와 불필요한 공간 낭비를 일으킬 수 있기 떄문에 효율적인 적용 방안을 마련해야한다.&lt;/p&gt;
&lt;h3 id=&quot;데이터베이스-인덱스를-생성하여-랜덤-io-를-최소화하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%EC%83%9D%EC%84%B1%ED%95%98%EC%97%AC-%EB%9E%9C%EB%8D%A4-io-%EB%A5%BC-%EC%B5%9C%EC%86%8C%ED%99%94%ED%95%98%EA%B8%B0&quot; aria-label=&quot;데이터베이스 인덱스를 생성하여 랜덤 io 를 최소화하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 인덱스를 생성하여 랜덤 I/O 를 최소화하기&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://haon.blog/database/db-index/storage-and-random-sequantial-io/&quot;&gt;쿼리 튜닝을 위한 HDD 와 SSD 의 순차 I/O 와 랜덤 I/O&lt;/a&gt; 에서 다루었듯이, 데이터베이스 쿼리 튜닝의 핵심은 &lt;strong&gt;Random I/O를 최소화하는 것이다.&lt;/strong&gt; HDD 의 경우 랜덤 I/O 가 발생한다면 디스크내에 불연속적으로 위치한 데이터를 읽기위해 헤더를 이동시키는 Seek Time 이 발생한다. 이 Seek Time 횟수가 많아질 수록 성능 저허가 심해지기 떄문에 인덱스를 통한 쿼리 튜닝을 시도해볼 수 있다. SSD 또한 HDD 에 비해 빠르다고 한들, 인덱스를 통한 쿼리 성능 튜닝이 필요하다. SSD 또한 랜덤 I/O 가 순차 I/O 에 비해 성능이, 즉 처리율(throughput)이 꽤 느리다. SSD 는 물리적으로 NAND 플래시 메모리에 저장된 데이터를 직접 쓰고 읽는 것이 아니라, 내부적으로 논리주소를 매핑하는 매핑 테이블을 사용한다. 매핑 테이블을 업데이트하려면 NAND 플래시 메모리의 물리적인 메모리를 찾아갸아 하므로, 랜덤 I/O의 경우 이 과정에 추가적으로 발생하기 때문에 성능이 느리다.&lt;/p&gt;
&lt;p&gt;인덱스의 B+ Tree 구조는 항상 정렬된 상태를 유지하기 떄문에 리프 페이지의 탐색 시작점을 빠르게 찾아낼 수 있다. 또한 리프 노드끼리 서로 링크드리스트로 연결되어 있기 떄문에, 인덱스 범위 탐색이 가능하다. (이 또한 B+ Tree 구조에서 리프 노드끼리도 서로 정렬된 상태를 구성하기 떄문에, 빠르게 범위 탐색이 가능하다.)&lt;/p&gt;
&lt;p&gt;그렇다면 아래 쿼리에서 어떤 필드를 기준으로 인덱스를 생성해야할까? 우리 팀은 어떻게 인덱스를 적용하기 위한 고민이 있었을까? 그 고민 사항을 하나씩 소개해보곘다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; created_at&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; start_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; created_at
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; trip_schedule
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-01-01&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-06-01&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; is_private &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; created_at &lt;span class=&quot;token keyword&quot;&gt;DESC&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;OFFSET&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;-1-커버링-인덱스를-통해-성능-개선을-시도해볼까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-1-%EC%BB%A4%EB%B2%84%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%ED%86%B5%ED%95%B4-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%EC%9D%84-%EC%8B%9C%EB%8F%84%ED%95%B4%EB%B3%BC%EA%B9%8C&quot; aria-label=&quot; 1 커버링 인덱스를 통해 성능 개선을 시도해볼까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;🤔 1. 커버링 인덱스를 통해 성능 개선을 시도해볼까?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 2024.11.17 추가 : 앞서 언급했듯이, 이 포스팅에서 진행한 쿼리 성능 개선기는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/pagnation-latency-db-covering-index/&quot;&gt;스프링 페이지네이션에서 발생한 Latency 의 원인과 커버링 인덱스 생성을 통한 문제 해결기&lt;/a&gt; 로 이어집니다. 페이지네이션 로직에 대한 커버링 인덱스 적용기를 참고해주세요! 🙂&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;일반적으로 디스크에 접근하는 그 행위 자체를 완전히 제거하고 싶다면 &lt;strong&gt;커버링 인덱스&lt;/strong&gt;를 통한 성능 개선을 생각해볼 수 있다. &lt;strong&gt;커버링 인덱스&lt;/strong&gt;란 쿼리내에 수행되는 모든 컬럼을 인덱스가 모두 가지고 있는 인덱스를 뜻한다. 이 경우 인덱스가 필요로하는 모든 데이터를 이미 가지고 있기 떄문에, 실제 레코드에 접근하여 데이터를 조회하는 랜덤 I/O 가 절대 발생하지 않는다. 따라서 성능이 매우 빨라진다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; trip_schedule
&lt;span class=&quot;token keyword&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; trip_schedule_covering_idx&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;created_at&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; is_private&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; start_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end_date&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 워 쿼리를 위한 커버링 인덱스를 생성한다면, 위와 깉이 인덱스를 생성할 수 있다. &lt;code class=&quot;language-text&quot;&gt;created_at&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;description&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;start_date&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;end_date&lt;/code&gt; 등 SELECT 쿼리내에 요구되는 모든 필드에 대해 인덱스를 생성했다.&lt;/p&gt;
&lt;p&gt;이렇게 된다면 모든 필드가 인덱스를 타기 떄문에 절대 디스크 랜덤 I/O 가 발생하지 않는다. 따라서 당장엔 성능이 매우 빨라진다. 하지만 &lt;a href=&quot;https://haon.blog/database/index-basic/&quot;&gt;데이터베이스 인덱스 B+ Tree 구조는 왜 조회 쿼리 성능이 빠를까?&lt;/a&gt; 에서 다루었듯이, 인덱스는 아무곳에나 필요하다고 막 적용하는 것이 아닌, &lt;strong&gt;정말 필요한 최소한의 컬럼에만 인덱스를 적용해야한다.&lt;/strong&gt; 인덱스가 생성되는 저장 공간은 데이터베이스 전체 공간에서 최대 10%만을 허용한다. 즉, 인덱스를 생성할 수 있는 저장공간은 한정적이므로 마구잡이로 모든 컬럼에 생성해버리면 불필요한 저장 공간을 낭비할 수 있다. 이러한 이유들로 커버링 인덱스는 우선 적용하지 않고, 더 적합한 인덱스 생성 방법을 찾아보는 것이 바람직하겠다 판단했다.&lt;/p&gt;
&lt;h2 id=&quot;-2-where-절에-포함된-모든-컬럼을-포함한-복합-인덱스를-생성해야할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-2-where-%EC%A0%88%EC%97%90-%ED%8F%AC%ED%95%A8%EB%90%9C-%EB%AA%A8%EB%93%A0-%EC%BB%AC%EB%9F%BC%EC%9D%84-%ED%8F%AC%ED%95%A8%ED%95%9C-%EB%B3%B5%ED%95%A9-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%EC%83%9D%EC%84%B1%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C&quot; aria-label=&quot; 2 where 절에 포함된 모든 컬럼을 포함한 복합 인덱스를 생성해야할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;🧐 2. WHERE 절에 포함된 모든 컬럼을 포함한 복합 인덱스를 생성해야할까?&lt;/h2&gt;
&lt;p&gt;모든 컬럼에 인덱스를 적용하는 방법이 다소 무모하다면, 랜덤 I/O 가 발생하더라도 &lt;strong&gt;인덱스 레인지 스캔&lt;/strong&gt;을 통해 쿼리 성능 개선을 시도해볼 수 있다. &lt;strong&gt;인덱스 레인지 스캔(Index Range Scan)&lt;/strong&gt; 이란 리프 페이지에서 범위 탐색의 시작점부터 끝점까지 순차대로 따라가며 원하는 데이터들을 스캔하는 방식이다. 이때 랜덤 I/O 가 발생하므로 커버링 인덱스에 비해선 상대적으로 다소 비효율적일 수 있지만, &lt;strong&gt;B+ Tree 구조 특성상 이 스캔 방식 또한 매우 빠르게 범위 탐색이 가능하다.&lt;/strong&gt; 그리고 &lt;code class=&quot;language-text&quot;&gt;WHERE&lt;/code&gt; 절에 담긴 필드를 인덱스가 생성되어야지 인덱스 레인지 스캔이 발생한다.&lt;/p&gt;
&lt;p&gt;앞서 살펴본 쿼리 내역중에 &lt;code class=&quot;language-text&quot;&gt;WHERE&lt;/code&gt; 절을 중점으로 다시 살펴보자. 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;created_at&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;is_private&lt;/code&gt; 을 모두 &lt;code class=&quot;language-text&quot;&gt;WHERE&lt;/code&gt; 절에 포함된 상태이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-01-01&apos;&lt;/span&gt; 
&lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; created_at &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2020-06-01&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; is_private &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 인덱스를 생성한다면 아래와 같이 생성해보는 것을 시도해볼 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; trip_schedule
&lt;span class=&quot;token keyword&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; trip_schedule_complex_index&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;created_at&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; is_private&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만, 여기서 위처럼 복합 인덱스로 생성할지, 아니면 &lt;code class=&quot;language-text&quot;&gt;created_at&lt;/code&gt; 에 대해서만 단일 인덱스로 생성할지 고민이 많았다. 복합 인덱스란 위의 &lt;code class=&quot;language-text&quot;&gt;(created_at, is_private)&lt;/code&gt; 과 같이 2개 이상의 여러 컬럼에 대한 인덱스를 생성하는 것을 뜻하며, 단일 인덱스란 &lt;code class=&quot;language-text&quot;&gt;(created_at)&lt;/code&gt; 과 같이 단 1개의 컬럼에 대해서 인덱스를 생성하는 방식이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1de12b1ce0939fa2235a4bb2d1c32037/c4842/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 19.631901840490798%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAuElEQVR42k2PSw7DIAwFc4yqUhOCAQMmNOnn/kd7tUkXWYz8sKyxmYo88d42fHrH+3jhq0jO4Fwh245cGqTtKHVDlY4iWjWL5tVHLI7gVsK8+MF0u8/oKjDh8/ii9ReyVHhOcN6DY0JiARHDUYBLUUWEGCJYl4WoyzUvKnvMKyYfGK0UdBUe+6HSD5INUoK/YNdc68h/LJN63BowcT63UCyI6cQu4my08WXLZ78OcumjWu+ck/E28Q/NO3SLJajCuwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/1de12b1ce0939fa2235a4bb2d1c32037/a6d36/image-4.png&quot;
        srcset=&quot;/static/1de12b1ce0939fa2235a4bb2d1c32037/222b7/image-4.png 163w,
/static/1de12b1ce0939fa2235a4bb2d1c32037/ff46a/image-4.png 325w,
/static/1de12b1ce0939fa2235a4bb2d1c32037/a6d36/image-4.png 650w,
/static/1de12b1ce0939fa2235a4bb2d1c32037/e548f/image-4.png 975w,
/static/1de12b1ce0939fa2235a4bb2d1c32037/3c492/image-4.png 1300w,
/static/1de12b1ce0939fa2235a4bb2d1c32037/c4842/image-4.png 1634w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;초기에는 복합 인덱스를 위처럼 &lt;code class=&quot;language-text&quot;&gt;(created_at, is_private)&lt;/code&gt; 으로 생성하고 쿼리 성능 개선을 시도하였다. 그 결과 실제 쿼리 시간은 약 &lt;code class=&quot;language-text&quot;&gt;55ms&lt;/code&gt; 가 측정된 것을 확인할 수 있다. 앞서 인덱스를 적용하지 않았을 때에 비하면 &lt;strong&gt;약 36.839초&lt;/strong&gt; 에서 &lt;strong&gt;0.055초&lt;/strong&gt; 극명하게 개선되었다.&lt;/p&gt;
&lt;h3 id=&quot;복합-인덱스-vs-단일-인덱스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B3%B5%ED%95%A9-%EC%9D%B8%EB%8D%B1%EC%8A%A4-vs-%EB%8B%A8%EC%9D%BC-%EC%9D%B8%EB%8D%B1%EC%8A%A4&quot; aria-label=&quot;복합 인덱스 vs 단일 인덱스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;복합 인덱스 vs 단일 인덱스&lt;/h3&gt;
&lt;p&gt;하지만 문제점이 있다. 이 또한 적합한 인덱스 생성 방식일까라는 의문이 남는다는 점이다. 앞선 커버링 인덱스 고민사항과 달리 &lt;code class=&quot;language-text&quot;&gt;WHERE&lt;/code&gt; 절에 담긴 컬럼만으로 인덱스를 타게 되었지만, &lt;code class=&quot;language-text&quot;&gt;is_private&lt;/code&gt; 컬럼을 포함한 복합  인덱스를 생성하는 것이 바람직할까 라는 점이다.&lt;/p&gt;
&lt;p&gt;복합 인덱스를 생성시 중요한 점중 하나는 &lt;strong&gt;카디널리티(Cardinality)&lt;/strong&gt; 이다. 애당초 인덱스는 생성된 컬럼들을 기준으로 정렬되는데, 복합 인덱스로 생성시에도 마찬가지로 생성된 컬럼들을 기준으로 B+ Tree 가 정렬된다. 이때 **카디널리티(Cardinality)**가 더 높은(더 유니크하고, 중복이 적은) 컬럼을 중심으로 더 선행 컬럼으로 배치하여 인덱스를 생성해야한다. 선행된 컬럼을 기준으로 인덱스가 우선 정렬되고, 그 다음으로 배치된 컬럼을 후순위로하여 인덱스가 정렬되기 떄문이다.&lt;/p&gt;
&lt;p&gt;그런데 문제는, 복합 인덱스로 생성한 &lt;code class=&quot;language-text&quot;&gt;(created_at, is_private)&lt;/code&gt; 에서 &lt;code class=&quot;language-text&quot;&gt;is_private&lt;/code&gt; 의 카디널리티가 과연 높은가이다. &lt;code class=&quot;language-text&quot;&gt;is_private&lt;/code&gt; 컬럼은 true, false 이 둘 중 하나의 값을 가지기 떄문에 카디널리티가 매우 낮은 수치이다. 이를 고려하여 복합 인덱스의 가장 선행되는 1번째 순서가 아닌 2번째 순서로 배치하였지만, created_at 에 비해 매우 낮은 카디널리티 수치를 가지므로 인덱스를 적용하는 것이 사실상 큰 성능 개선이 되지 않는다. &lt;code class=&quot;language-text&quot;&gt;(created_at, is_private)&lt;/code&gt; 와 같이 복합 인덱스를 생성한다면, &lt;code class=&quot;language-text&quot;&gt;created_at&lt;/code&gt; 을 기반으로 선행 정렬될 것이며, is_private 을 후순위로 하여 정렬되겠지만, true / false 를 정렬하는 것은 사실상 무의미한 행위이며, 되려 인덱스 공간을 낭비하는 셈이 될 것이라 생각되었다.&lt;/p&gt;
&lt;p&gt;그렇다고 해서 &lt;code class=&quot;language-text&quot;&gt;is_private&lt;/code&gt; 로 인해 랜덤 I/O 가 발생하지 않는 것도 아니다. 랜덤 I/O 가 발생하지 않는 것은 앞선 커버링 인덱스일 때만 가능해진다. 따라서 복합 인덱스 생성은 성능 개선이 미미한 행위가 되며, 불필요한 인덱스 공간 낭비가 된다.&lt;/p&gt;
&lt;h2 id=&quot;-단일-인덱스를-생성한-인덱스-레인지-스캔-유도&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-%EB%8B%A8%EC%9D%BC-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%EC%83%9D%EC%84%B1%ED%95%9C-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EB%A0%88%EC%9D%B8%EC%A7%80-%EC%8A%A4%EC%BA%94-%EC%9C%A0%EB%8F%84&quot; aria-label=&quot; 단일 인덱스를 생성한 인덱스 레인지 스캔 유도 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;🎯 단일 인덱스를 생성한 인덱스 레인지 스캔 유도&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; trip_schedule
&lt;span class=&quot;token keyword&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; trip_schedule_simple_index&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;created_at &lt;span class=&quot;token keyword&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;결국 &lt;code class=&quot;language-text&quot;&gt;(created_at, is_private)&lt;/code&gt; 형태의 복합 인덱스 생성은 비효율적인 인덱스 생성 개선 방법이라 판단되어, 위처럼 &lt;code class=&quot;language-text&quot;&gt;created_at&lt;/code&gt; 에 대한 단일 인덱스를 생성하여 성능 개선을 시도하였다.&lt;/p&gt;
&lt;p&gt;실제로 쿼리가 인덱스를 타는지 &lt;code class=&quot;language-text&quot;&gt;EXPLAIN&lt;/code&gt; 명령어, 즉 MySQL 싫행게획을 통해 조회해보면 아래와 같이 조회된다. &lt;code class=&quot;language-text&quot;&gt;type=range&lt;/code&gt; 가 뜻하는 바는 인덱스 레인지 스캔이 수행됨을 뜻한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e7ce2532ab3a54350c2b5fa88d91dd22/a9577/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 14.11042944785276%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAj0lEQVR42mVN2RKCMAzshwgFnPGg2is9EPD/P2tNIz75kMleySrnCYEyyrKC8oJlfTPeYVm3Psq4kGDdgVn3zNt2gTi7IVJBTBXEoyo/8mzMxsrT293APCwqBxMXtGNKBSEm8Vo2Mp/NE5frjFI35PLiwiClSg8T2vR6lN31g+Af//MOfuq08K8/YRjPon8APEpaso+OuE8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e7ce2532ab3a54350c2b5fa88d91dd22/a6d36/image-5.png&quot;
        srcset=&quot;/static/e7ce2532ab3a54350c2b5fa88d91dd22/222b7/image-5.png 163w,
/static/e7ce2532ab3a54350c2b5fa88d91dd22/ff46a/image-5.png 325w,
/static/e7ce2532ab3a54350c2b5fa88d91dd22/a6d36/image-5.png 650w,
/static/e7ce2532ab3a54350c2b5fa88d91dd22/e548f/image-5.png 975w,
/static/e7ce2532ab3a54350c2b5fa88d91dd22/3c492/image-5.png 1300w,
/static/e7ce2532ab3a54350c2b5fa88d91dd22/a9577/image-5.png 1784w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;실제 쿼리를 실행했을 때의 시간을 측정해보면 아래와 같이 수행시간에 &lt;strong&gt;약 23ms&lt;/strong&gt; 라는 시간이 소요됨을 확인할 수 있다. 여기서 신기한점은, 앞선 복합 인덱스 생성방식 보다 되려 수행시간이 비슷하다는 점이다. 미미한 수준이지만, 되려 수행시간이 더 빠르게 측정되기도 했다. 이로보아 is_private 과 같이 카디널리티가 매우 낮은 컬럼에 대해 인덱스에 포함한 복합 인덱스를 생성한다고 해서, 그 효과는 사실상 미미하거나 없다고 보는 것이 맞다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8081958cd4aaa41b4f599881a7da6e60/eee07/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 20.245398773006134%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAuElEQVR42lWPWQ7CMAxEew2EaLM6W5ukpXD/mw12EAg+nmyN7clkiqWilRVn3bC3joMpMYFCQlkbYizIvJPyNohCWlFY08ZjURZKW8yLGUyX64zkCc9WsR8P1H7yQYaLBGUsyBGITZ2PUNZDB8ZaeOuGsaeEwDtKGdxmNjQuIHOiVit639H3O0LZ4CyNBIar+emlakN//We2aIeJAqfxiV/KePdxJApf1lFFlzSCfFv2RZOZ3Ali/AKi73RR4BDS8gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/8081958cd4aaa41b4f599881a7da6e60/a6d36/image-6.png&quot;
        srcset=&quot;/static/8081958cd4aaa41b4f599881a7da6e60/222b7/image-6.png 163w,
/static/8081958cd4aaa41b4f599881a7da6e60/ff46a/image-6.png 325w,
/static/8081958cd4aaa41b4f599881a7da6e60/a6d36/image-6.png 650w,
/static/8081958cd4aaa41b4f599881a7da6e60/e548f/image-6.png 975w,
/static/8081958cd4aaa41b4f599881a7da6e60/3c492/image-6.png 1300w,
/static/8081958cd4aaa41b4f599881a7da6e60/eee07/image-6.png 1628w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;무엇보다 기존 &lt;strong&gt;테이블 풀 스캔(Table Full Scan)&lt;/strong&gt; 방식이 &lt;strong&gt;약 36.839&lt;/strong&gt; 소요시간이 수행된 것에 비해, &lt;strong&gt;인덱스 레인지 스캔(Range Scan)&lt;/strong&gt; 을 통해 쿼리가 인덱스를 타니 &lt;strong&gt;약 0.023초&lt;/strong&gt; 로 극명하게 개선된 것을 확인할 수 있다 🙂&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이렇게 인덱스를 통한 쿼리 성능 개선기에 대해 다루어보았다. 우리 서비스내에 적합한 인덱스를 적용하기 위해 위처럼 하나하나의 많은 고민과 효율적인 해결안을 찾고자 노력하였고, 기존 테이블 풀 스캔 방식일 때 &lt;strong&gt;약 36.839초&lt;/strong&gt; 가 걸리던 쿼리를 &lt;strong&gt;0.023초&lt;/strong&gt; 로 개선하였다. 만약 커버링 인덱스를 적용한다면 현재 측정된 소요시간인 &lt;strong&gt;0.023초&lt;/strong&gt; 에 비해 더 빠른 성능을 이루어낼 수 있었겠지만, 이 쿼리 소요시간 만으로도 충분히 쿼리 성능이 극명하게 최적화되었다고 생각하여 커버링 인덱스 생성을 적극 고려하지 않았던 것도 이유 중 하나이다. 성능 개선이 된다고 한들, 그 수준이 매우 미미한 수준일 것이다.&lt;/p&gt;
&lt;p&gt;앞으로도 우리 서비스 사용자들이 더 편리함을 누릴 수 있도록, 사용자 경험을 개선할 수 있도록 많은 고민 시간을 가져볼까 한다. 이만 포스팅을 마쳐본다 😎&lt;/p&gt;</content:encoded></item><item><title><![CDATA[[가상 면접 사례로 배우는 대규모 시스템 설계 기초] 제 2장. 계략적인 규모 측정]]></title><description><![CDATA[…]]></description><link>https://haon.site/virtual-interview/chap02/</link><guid isPermaLink="false">https://haon.site/virtual-interview/chap02/</guid><pubDate>Fri, 15 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;시스템 설계 면접을 볼 때, 떄로는 시스템 용량이나 성능 요구사항을 개략적으로 추정해보라는 요구를 받게 된다. 개략적인 규모 측정은 보편적으로 통용되는 성능 수치상에서 사고 실험을 행하여 추정치를 게산하는 행위로써, 어떤 설계가 요구사항에 부합할 것인지 보기 위한 것이다.&lt;/p&gt;
&lt;p&gt;개략적 규모 추정을 효과적으로 해 내려면 규모 확장성을 표현하는데 필요한 기본기에 능숙해야한다. 특히 2의 제곱수나 응잡지연(latency) 값, 그리고 가용성에 관계된 수치들을 기본적으로 잘 이해하고 있어야 한다.&lt;/p&gt;
&lt;h2 id=&quot;2의-제곱수&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2%EC%9D%98-%EC%A0%9C%EA%B3%B1%EC%88%98&quot; aria-label=&quot;2의 제곱수 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2의 제곱수&lt;/h2&gt;
&lt;p&gt;분산 시스템에서 다루는 데이터 양은 엄청나게 커질 수 있으나, 그 계산법은 기본을 크게 벗어나지 않는다. 제대로 된 계산 결과를 얻으려면 데이터 볼륨의 단위를 2의 제곱수로 표현하면 어떻게 되는지를 우선 알아야 한다. 최소 단위는 1바이트이고, 8비트로 구성된다. ASCII 문자 하나가 차지하는 메모리 크기가 1바이트이다.&lt;/p&gt;
&lt;h2 id=&quot;모든-프로그래머가-알아야-하는-응답지연-값&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A8%EB%93%A0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EA%B0%80-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%98%EB%8A%94-%EC%9D%91%EB%8B%B5%EC%A7%80%EC%97%B0-%EA%B0%92&quot; aria-label=&quot;모든 프로그래머가 알아야 하는 응답지연 값 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;모든 프로그래머가 알아야 하는 응답지연 값&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;메모리는 빠르지만 디스크는 아직도 느리다.&lt;/li&gt;
&lt;li&gt;디스크 탐색(seek) 은 가능한 피하라.&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;h2 id=&quot;가용성에-관계된-수치들&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%80%EC%9A%A9%EC%84%B1%EC%97%90-%EA%B4%80%EA%B3%84%EB%90%9C-%EC%88%98%EC%B9%98%EB%93%A4&quot; aria-label=&quot;가용성에 관계된 수치들 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;가용성에 관계된 수치들&lt;/h2&gt;
&lt;p&gt;고가용성은 시스템이 오랜시간동안 지속적으로 중단없이 운영될 수 있는 능력을 지칭하는 용어이다. 고가용성을 표현하는 것은 퍼센트로 표현하는데, 100%는 시스템이 단 한번도 중단된 적이 없었음을 의미한다. 대부분의 서비스는 99% 에서 100% 사이의 값을 갖는다.&lt;/p&gt;
&lt;h2 id=&quot;팁&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%81&quot; aria-label=&quot;팁 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;팁&lt;/h2&gt;
&lt;p&gt;개략적인 규모 추정과 관계된 면접에서 가장 중요한 것은 문제를 풀어 나가는 절차이다. 올바른 절차는 밟느냐가 결과를 내는 것보다 중요하다. 면접자가 보고싶어하는 것은 문제 해결 능력이다. 이와 관련된 팁은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;근사치를 활용한 계산 : 면접장에서 복잡한 게산을 하는것은 어려운 일이다. 계산하는데 시간을 쓰는 일은 낭비이다. 적절한 근사치를 활용하여 시간을 절약하자.&lt;/li&gt;
&lt;li&gt;가정들은 적어두라. 나중에 살펴볼 수 있도록.&lt;/li&gt;
&lt;li&gt;많이 출제되는 개략적 규모 추정 문제는 QPS, 최대 QPS, 저장소 요구량, 캐시 요구량, 서버 수 등을 측정하는 것이다. 면접에 임하기 전에 이런 값들을 계산하는 연습을 미리 해두자.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[✒️ 팀 프로젝트에서 발견한 진정한 의미의 함께 성장하기]]></title><description><![CDATA[…]]></description><link>https://haon.site/회고/growing-up-together/</link><guid isPermaLink="false">https://haon.site/회고/growing-up-together/</guid><pubDate>Wed, 13 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 카카오테크 부트캠프는 자율 주제 글쓰기 &amp;#x26; 소정의 이벤트가 주어진다. 이 포스팅은 &quot;우리는 프로젝트에서 함께 성장할 수 있을까&quot; 주제로 작성한 글이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;솔직하게-말하는-습관이-팀을-더-성장시킨다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%86%94%EC%A7%81%ED%95%98%EA%B2%8C-%EB%A7%90%ED%95%98%EB%8A%94-%EC%8A%B5%EA%B4%80%EC%9D%B4-%ED%8C%80%EC%9D%84-%EB%8D%94-%EC%84%B1%EC%9E%A5%EC%8B%9C%ED%82%A8%EB%8B%A4&quot; aria-label=&quot;솔직하게 말하는 습관이 팀을 더 성장시킨다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;솔직하게 말하는 습관이 팀을 더 성장시킨다&lt;/h2&gt;
&lt;p&gt;저는 갈등을 정말 싫어하는 사람이었습니다. 카테부에 참여하기 전의 저는, 남에게 제가 무지하다는 것을 들키는 것이 정말 싫었어요. 제가 가지고 있는 생각에 대한 확신도 없고, 자신감이 많이 없었습니다. 타인과 갈등이 생기지 않도록 항상 제 의견을 감추는 편이었답니다. 그저 단순히 맹목적으로 타인의 의견을 수용하고, 열정적인 의견 교환이 일어나지 않았죠. 갈등없이 화목하게 지내는 팀이 좋은 팀이라고 생각했었죠.&lt;/p&gt;
&lt;p&gt;하지만, 혼자 의견을 감추고 의견을 적극 내세우지 않는다는 것이 좋지 않다는 것을 깨달았습니다. 팀 프로젝트를 통해 깨달은 점이 하나 있어요. &lt;strong&gt;서로 다른 의견을 가지고 있는 크루들과 열정적으로 의견을 주고받아야만 전혀 생각치 못한 방법을 알게되고, 다양한 시야가 확장된다&lt;/strong&gt;는 것이에요.&lt;/p&gt;
&lt;p&gt;회의에서 계속 정적이 흐르고, 만장일치 의견이 나온다는 것은 팀원 간 사이가 좋다는 뜻이 아닙니다. 계속되는 만장일치는 팀 전체가 하나의 수동적인 틀에만 박힌 생각에서 빠져나오지 못하게 되는 경우가 많거든요. 하지만 서로 다른 의견을 가지고 있는 사람과 뜨겁게 의견을 주고받다보면 전혀 생각치 못한 획기적인 방법을 깨닫고, 다양한 시야가 확장된다는 것을 알 수 있었습니다. &lt;strong&gt;더 빠르게 팀이 성장하기 위한 방법은 더 능동적으로 최대한 많이 토론하고, 잡담하는 것입니다.&lt;/strong&gt; 저희 하모니 팀 크루들은 서로가 다른 의견을 가지고 있을 때 그 자리를 회피하기보다는, 오히려 의견 교환에 적극 참여하는 경우가 대부분입니다.&lt;/p&gt;
&lt;p&gt;그러나, 자칫 무작정 반대하는 의견은 서로 간의 신뢰가 쌓여있지 않다면 자신에 대한 공격으로 받아들여질 수 있습니다. 그 떄문에 하모니 팀은 프로젝트 초반부터 좋은 팀 분위기를 형성하는데 많은 시간을 쏟았습니다. 특히, 서로의 의견을 편안하게 이야기할 수 있는 분위기를 만드는 것이 가장 큰 목표였어요. 자신의 생각과 고민을 편안하게 이야기할 수 있어야만 더 활발한 의견 교류가 이어지고, 팀원 전체가 공통된 하나의 목표를 위해 빠르게 달려나갈 수 있거든요.&lt;/p&gt;
&lt;h2 id=&quot;팀원을-설득하기-위해선-그를-위한-충분한-근거를-만들자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%80%EC%9B%90%EC%9D%84-%EC%84%A4%EB%93%9D%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%B4%EC%84%A0-%EA%B7%B8%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%B6%A9%EB%B6%84%ED%95%9C-%EA%B7%BC%EA%B1%B0%EB%A5%BC-%EB%A7%8C%EB%93%A4%EC%9E%90&quot; aria-label=&quot;팀원을 설득하기 위해선 그를 위한 충분한 근거를 만들자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;팀원을 설득하기 위해선, 그를 위한 충분한 근거를 만들자!&lt;/h2&gt;
&lt;p&gt;카테부의 참여하기 전의 저 자신과 크게 달라진 점이 또 있어요. &lt;strong&gt;팀원을 설득하기 위해선, 그를 위한 충분한 논리와 설득력이 필요하다는 점이죠.&lt;/strong&gt; 어떤 기술을 도입하기 위해선 항상 &lt;strong&gt;&quot;Why?&quot;&lt;/strong&gt; 를 떠올리며, 해당 기술과 행위에 대한 당위성을 확보해야한다는 것을 알게 되었어요.&lt;/p&gt;
&lt;p&gt;하모니 팀에서 RefreshToken 을 위한 저장소를 구축하던 중, 한 가지 재밌었던 일화가 기억에 남아요. 인증/인가 로직을 구현하던 중 리프레시 토큰을 Redis 에 저장하는 방식을 생각했습니다. 로컬에서 Redis 를 간단히 설치하여 구축 후, 회의 시간에 저희 클라우드 팀원 레오, 엘라에게 Redis 를 사용하자고 의견을 내세웠지만, &lt;strong&gt;&quot;왜 써야해?&quot;&lt;/strong&gt; 라는 질문에 답하지 못했어요. 충분한 논리와 설득력없이 &lt;strong&gt;&quot;그냥&quot;&lt;/strong&gt; 쓰기만 했어요.&lt;/p&gt;
&lt;p&gt;아무리 생각해보아도, 저희 팀에게 Redis 가 꼭 필요한 근거를 찾을 수 없었습니다. Redis 는 서버를 다중화하여 운영할 때 데이터 동기화 문제를 해결할 수 있다는 큰 장점이 있지만, 저희 하모니 팀은 다중화를 한 상황이 아닙니다. 단일 서버 1대라면 Redis 없이도 충분히 인메모리에서 토큰을 관리할 수 있었죠.&lt;/p&gt;
&lt;p&gt;기술은 어떤 특정한 상황을 해결하기위해 등장한 것입니다. 기술은 이유 없이 등장한 것이 아니죠. 기술을 도입할 이유가 없는데도 충분한 고민없이 기술을 도입하는 것은 팀의 학습비용과, 자칫 불필요한 비용 낭비만 발생합니다. 저희 팀은 이때부터 새로운 기술 도입이 생길 때마다 더 효율적인 방안은 없을지, 우리에게 이 기술이 꼭 필요한 것인지 충분한 근거와 고민 시간을 확보하고 있답니다. 과거의 저는 단순히 새로운 기술 도입이 그저 멋져 보여서 사용한 것이 아닐까요?&lt;/p&gt;
&lt;h2 id=&quot;팀원들을-통해-전혀-몰랐던-나만의-장점을-찾아내기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%80%EC%9B%90%EB%93%A4%EC%9D%84-%ED%86%B5%ED%95%B4-%EC%A0%84%ED%98%80-%EB%AA%B0%EB%9E%90%EB%8D%98-%EB%82%98%EB%A7%8C%EC%9D%98-%EC%9E%A5%EC%A0%90%EC%9D%84-%EC%B0%BE%EC%95%84%EB%82%B4%EA%B8%B0&quot; aria-label=&quot;팀원들을 통해 전혀 몰랐던 나만의 장점을 찾아내기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;팀원들을 통해, 전혀 몰랐던 나만의 장점을 찾아내기&lt;/h2&gt;
&lt;p&gt;혼자서 프로그래밍을 공부할 때와 달리, 팀원들과 함께 협업해야하다보니 말하기 스킬이 늘었던 것 같습니다. 팀원들과 의사소통하면서 내가 몰랐던 장점을 발견할 수도 있었어요. &quot;하온은 글을 정말 잘 쓰는 것 같아! 필력이 진짜 좋다!&quot; 와 같이 내가 몰랐던 장점을 찾아낼 수도 있었어요. 팀원들과 함께 성장하기 위해 제가 학습했던 블로그 포스팅을 자주 공유하곤 했는데, 이때마다 크루들에게 칭찬을 많이 듣기도 했어요.&lt;/p&gt;
&lt;p&gt;이번 카테부에서 몰랐던 나만의 장점을 많이 발견할 수 있었답니다. 혼자 공부할 때는 내가 무엇을 잘 하고 못하는거지? 라는 좁은 시야에 갇혀서 빠져나오지 못했었. 하지만, 이번 깊이 있는 협업 프로젝트 경험에서 팀원들 덕분에 나만의 개성과 색깔은 무엇인지 알 수 있었어요. 그리고 제 성향과 색깔이 협업하는 크루들에게 어떤 기여도를 끼칠 수 있는지도 알게 되었답니다.&lt;/p&gt;
&lt;h2 id=&quot;다양한-색깔과-경험을-가진-사람들과-함께-성장하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%96%91%ED%95%9C-%EC%83%89%EA%B9%94%EA%B3%BC-%EA%B2%BD%ED%97%98%EC%9D%84-%EA%B0%80%EC%A7%84-%EC%82%AC%EB%9E%8C%EB%93%A4%EA%B3%BC-%ED%95%A8%EA%BB%98-%EC%84%B1%EC%9E%A5%ED%95%98%EA%B8%B0&quot; aria-label=&quot;다양한 색깔과 경험을 가진 사람들과 함께 성장하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다양한 색깔과 경험을 가진 사람들과 함께 성장하기&lt;/h2&gt;
&lt;p&gt;글을 적다보니 카카오테크 부트캠프에 참여하기 전, 대학교에서 팀 프로젝트를 진횅했을 때가 생각나네요. 지금까지 경험했던 팀 프로젝트는 팀원들의 열정이 제각각이었답니다. 누군가는 이번 프로젝트가 진심으로 열정을 다 하고 싶다면, 다른 누군가에겐 별 생각없이 억지로 참여하는 프로젝트였답니다.&lt;/p&gt;
&lt;p&gt;그런데, 카카오테크 부트캠프는 달랐습니다. 카카오테크 부트캠프 크루들은 모두가 열정을 다하고, 성장에 열망이 넘치는 사람들이 넘쳐나요. &lt;strong&gt;서로가 불 태우는 열정은 협업하는 팀원 모두에게 전염되고, 그 열정이 또 다른 이에게 전염되어 함께 성장하고 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;또한 서로 다른 개성과 경험, 가치관을 가진 팀원들이 모여 하나의 서비스를 개발하고 있습니다. 여기서 저는, 책 &lt;strong&gt;‘함께 자라기’&lt;/strong&gt; 에서 강조하는 함께하는 성장이 무엇인지 조금 알게 된 것 같아요. 하모니 팀은 모두가 팀이 직면한 문제를 해결히기 위해 함께 고민하고, 진심으로 열정을 태웁니다. 일과 시간이 끝나고도 카카오테크 부트캠프는 불이 꺼지지 않아요. 누군가 강제하는 것도 아닌데, 끝까지 남아서 크루들과 토론하고, 고민하고, 생각합니다.&lt;/p&gt;
&lt;p&gt;이 과정속에서 저는 크루들에게 많이 배우고 있어요. 혼자서 공부하고, 프로그래밍 하다 보면 자신만의 틀에 박힌 생각에 갇히게 되는 경우가 많거든요. 하지만 &lt;strong&gt;서로 다른 의견을 가지고 있는 사람과 뜨겁게 의견을 주고받다보면 전혀 생각치 못한 획기적인 방법을 깨닫고, 다양한 시야가 확장된다는 것을 알 수 있었습니다.&lt;/strong&gt; 더 빠르게 성장하는 학습 방법은 더 능동적으로 최대한 많이 토론하고, 잡담하는 것입니다.&lt;/p&gt;
&lt;p&gt;이런 선순환 속에서 저는 함께 자라기를 경험했습니다. 개인의 성장이 곧 팀의 성장이고, 팀의 성장이 곧 개인의 성장으로 이어지는 경험을 했습니다. 이런 경험을 쌓게해준 저희 카테부 하모니 팀원들에게 항상 고마울 따름이에요. 아직 부족함이 많은 저와 팀 프로젝트를 함께한 하모니 팀 크루 리안, 레오, 엘라, 도로시, 민디에게 항상 감사를 표하며 이만 글을 마치겠습니다. 😊&lt;/p&gt;</content:encoded></item><item><title><![CDATA[어노테이션 기반 Spring MVC 프레임워크 구현하기]]></title><description><![CDATA[진행한 실습 코드 PR 은 PR : 어노테이션 기반 MVC 프레임워크 구현하기 에서 확인 가능하다. 지난 레거시 MVC 프레임워크의 문제점 지난 AJAX 기반 레거시 MVC 프레임워크 구현하기
에선 AJAX 에 기반한 모방 MVC…]]></description><link>https://haon.site/haon/spring/annotation-mvc-framework/</link><guid isPermaLink="false">https://haon.site/haon/spring/annotation-mvc-framework/</guid><pubDate>Tue, 12 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;진행한 실습 코드 PR 은 &lt;a href=&quot;https://github.com/Durable-developers/10-jang/pull/1/files&quot;&gt;PR : 어노테이션 기반 MVC 프레임워크 구현하기&lt;/a&gt; 에서 확인 가능하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;지난-레거시-mvc-프레임워크의-문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%80%EB%82%9C-%EB%A0%88%EA%B1%B0%EC%8B%9C-mvc-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;지난 레거시 mvc 프레임워크의 문제점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;지난 레거시 MVC 프레임워크의 문제점&lt;/h2&gt;
&lt;p&gt;지난 &lt;a href=&quot;https://haon.blog/spring/ajax-based-mvc-impementation/&quot;&gt;AJAX 기반 레거시 MVC 프레임워크 구현하기&lt;/a&gt;
에선 AJAX 에 기반한 모방 MVC 프레임워크를 구현했지만 아쉽게도 문제점이 여럿 존재한다.&lt;/p&gt;
&lt;p&gt;우선 새로운 컨트롤러가 추가될 때 마다 &lt;strong&gt;(1) 매번 RequestMapping 클래스에 요청 URL 과 컨트롤러를 추가&lt;/strong&gt;줘야 한다. 꽤 번거롭고 귀찮은 작업이 발생한다. 유지보수 관점에서도 생각해본다면 컨트롤러 수가 100개가 넘어가기 만 해도 관리하기가 힘들어진다. 또한 각 컨트롤러의 &lt;code class=&quot;language-text&quot;&gt;execute()&lt;/code&gt; 매소드엔 10줄이 넘어가는 경우가 거의 없다. 새로운 기능이 추가될 때 마다 매번 &lt;strong&gt;컨트롤러 오브젝트를 추가하는 것이 아닌, 메소드를 추가하는 방식으로 개선&lt;/strong&gt;되면 좋을 것이다. 또한 &lt;strong&gt;(2) 요청 URL 을 매핑할 때 HTTP 메소드도 매핑에 활용할 수 있다면 좋을 것이다.&lt;/strong&gt; HTTP 메소드에 대한 지원이 가능하다면 URL 은 같지만 다른 메소드로 매핑하는 것도 가능할 것이다.&lt;/p&gt;
&lt;p&gt;이번 학습의 목적은 지난 MVC 프레임워크을 점진적으로 리팩토링 하면서, 어노테이션에 기반한 새로운 NVC 프레임워크로 개선하는 것이 주 목적이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;기본-어노테이션-및-클래스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EB%B3%B8-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EB%B0%8F-%ED%81%B4%EB%9E%98%EC%8A%A4&quot; aria-label=&quot;기본 어노테이션 및 클래스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기본 어노테이션 및 클래스&lt;/h2&gt;
&lt;h3 id=&quot;controller-requestmapping&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#controller-requestmapping&quot; aria-label=&quot;controller requestmapping permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@Controller, @RequestMapping&lt;/h3&gt;
&lt;p&gt;기본적인 어노테이션은 아래와 같이 제공되고 있다. 특히 RequestMethod 는 GET, POST, PUT, ... 등을 값으로 가지는 enum 클래스이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// RequestMapping.java&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ElementType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;METHOD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ElementType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TYPE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Retention&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RetentionPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RUNTIME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestMapping&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token class-name&quot;&gt;RequestMethod&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Controller.java&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ElementType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;METHOD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ElementType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TYPE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Retention&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RetentionPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RUNTIME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;requestmethod&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#requestmethod&quot; aria-label=&quot;requestmethod permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RequestMethod&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestMethod&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;controller-어노테이션-설정-클래스-스캔&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#controller-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%84%A4%EC%A0%95-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%8A%A4%EC%BA%94&quot; aria-label=&quot;controller 어노테이션 설정 클래스 스캔 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@Controller 어노테이션 설정 클래스 스캔&lt;/h2&gt;
&lt;p&gt;어노테이션 기반으로 MVC 프레임워크를 구현하려면 먼저 &lt;code class=&quot;language-text&quot;&gt;@Controller&lt;/code&gt; 어노테이션 설정이 되어있는 클래스를 찾아야한다. 자바 reflection 라이브러리를 활용해 클래스 목록을 찾고, 각 클래스에 대한 인스턴스 목록까지 구현한다.&lt;/p&gt;
&lt;h3 id=&quot;controllerscanner&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#controllerscanner&quot; aria-label=&quot;controllerscanner permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ControllerScanner&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ControllerScanner&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt; log &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ControllerScanner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Reflections&lt;/span&gt; reflections&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ControllerScanner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; basePackage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        reflections &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Reflections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;basePackage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getControllers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InstantiationException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IllegalAccessException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; preInitiatedControllers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; reflections&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTypesAnnotatedWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;instantiateControllers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;preInitiatedControllers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;instantiateControllers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; preInitiatedControlllers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InstantiationException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IllegalAccessException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; controllers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Maps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newHashMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; clazz &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; preInitiatedControlllers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                controllers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clazz&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; clazz&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InstantiationException&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IllegalAccessException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; controllers&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;requestmapping-어노테이션-설정을-활용한-매핑&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#requestmapping-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%84%A4%EC%A0%95%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%A7%A4%ED%95%91&quot; aria-label=&quot;requestmapping 어노테이션 설정을 활용한 매핑 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@RequestMapping 어노테이션 설정을 활용한 매핑&lt;/h2&gt;
&lt;p&gt;앞서 ControllerScanner 와 자바 리플랙션을 통해 찾아낸 컨트롤러 클래스를 찾아냈을 것이다. 찾아낸 컨트롤러 클래스 내부의 @RequestMapping 어노테이션 설정을 기반으로 매핑을 해야한다. 매핑을 이전 MVC 프레임워크와 같이 Map 을 활용한다.&lt;/p&gt;
&lt;h3 id=&quot;handlerkey&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#handlerkey&quot; aria-label=&quot;handlerkey permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HandlerKey&lt;/h3&gt;
&lt;p&gt;새로운 MVC 프레임워크 버전에서 다른점은 Map 의 Key 로 사용되는 값이 요청 URL 과 더붙어 HTTP 메소드 조합으로 구성되어야 한다는 것이다. &lt;strong&gt;요청 URL 과 HTTP 메소드 정보를 가지는 클래스를 HandlerKey 라는 이름으로 구현했다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerKey&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 요청 URL&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestMethod&lt;/span&gt; requestMethod&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// HTTP 메소드 정보&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestMethod&lt;/span&gt; requestMethod&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;requestMethod &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; requestMethod&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;HandlerKey [url=&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;, requestMethod=&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; requestMethod &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hashCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; prime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; prime &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requestMethod &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; requestMethod&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hashCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; prime &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hashCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;HandlerKey&lt;/span&gt; other &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HandlerKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requestMethod &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;requestMethod&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;other&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;other&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;HashMap 의 Key 값으로 활용하기 위해 위 HandlerKey 오브젝트를 &lt;code class=&quot;language-text&quot;&gt;hashCode()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 를 재정의하여 동등성을 보장해줬다.&lt;/p&gt;
&lt;h3 id=&quot;handlerexecution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#handlerexecution&quot; aria-label=&quot;handlerexecution permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HandlerExecution&lt;/h3&gt;
&lt;p&gt;한편 Map 의 Value 값으로는 &lt;code class=&quot;language-text&quot;&gt;@RequestMapping&lt;/code&gt; 어노테이션이 설정되어 있는 메소드 정보를 가져야한다. 값에 저장되는 메소스 정보는 자바 리플렉션으로 해당 메소드를 실행할 수 있는 정보를 가져야한다. &lt;strong&gt;즉, 메소드가 위치하는 클래스의 인스턴스 정보와 java.lang.reflect.Method 정보를 가지고 있어야한다.&lt;/strong&gt; 이 정보를 가지는 클래스를 &lt;code class=&quot;language-text&quot;&gt;HandlerExecution&lt;/code&gt; 이라는 이름으로 아래와 같이 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerExecution&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HandlerExecution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; declaredObject&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 메소드가 위치하는 클래스의 인스턴스 정보&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Method&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Method 정보&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerExecution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; declaredObject&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Method&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;declaredObject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; declaredObject&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;method &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;invoke&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;declaredObject&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IllegalAccessExcpetion&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IllegalArgumentException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;{} method invoke fail. Error message : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RunTimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;annotationhandlermapping&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#annotationhandlermapping&quot; aria-label=&quot;annotationhandlermapping permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AnnotationHandlerMapping&lt;/h3&gt;
&lt;p&gt;Map 에서 사용할 key 와 value 에 대한 클래스인 HandlerKey, HandlerExecution 의 구현을 완료했다. HandlerKey 와 HandlerExecution 을 연결해야 한다. HandlerKey, HandlerExecution 이들에 대한 &lt;code class=&quot;language-text&quot;&gt;매핑 초기화 작업&lt;/code&gt; 을 AnnotationHandlerMapping 클래스를 추가해 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnnotationHandlerMapping&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AnnotationHandlerMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; basePackage&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HandlerKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerExecution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; handlerExecutions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Maps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newHashMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnnotationHandlerMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; basePackage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;basePackage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; basePackage&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;initalize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;ControllerScanner&lt;/span&gt; controllerScanner &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ControllerScanner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;basePackage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; cotnrollers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; controllerScanner&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getControllers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Method&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; methods &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getRequestMappingMethods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;controllers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keySet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Method&lt;/span&gt; method &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; methods&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RequestMapping&lt;/span&gt; rm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAnnotation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debud&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;register handlerExecution : url is {}, method is {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        handlerExecutions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createHandlerKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rm&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerExecution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;controllers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getDeclaringClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerKey&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createHandlerKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RequestMappign&lt;/span&gt; rm&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token annotation punctuation&quot;&gt;@SuppressWarnings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;unchecked&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Method&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getRequestMappingMethods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; controllers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Method&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; requestMappingMethods &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Sets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newHashSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; clazz &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; controllers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        requestMappingMethods&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ReflectionUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAllMethods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clazz&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReflectionUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withAnnotataion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; requestMappingMethods&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 클라이언트 요청에 해당하는 HandlerExecution 을 조회하는 매소드&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerExecution&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; requestUri &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestURI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;RequestMethod&lt;/span&gt; rm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUpperCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;reqeustUri: {}, requestMethod: {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; requestUri&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rm&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; handlerExecutions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requestUri&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rm&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;dispatcherservlet-과-annotationhandlermapping-통합&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dispatcherservlet-%EA%B3%BC-annotationhandlermapping-%ED%86%B5%ED%95%A9&quot; aria-label=&quot;dispatcherservlet 과 annotationhandlermapping 통합 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DispatcherServlet 과 AnnotationHandlerMapping 통합&lt;/h2&gt;
&lt;p&gt;지금까지 앞선 구현을 통해 어노테이션 기반으로 컨트롤러 설정이 가능하도록 구현했다. 이젠 DispacherServlet 에서 새롭게 추가한 AnnotationHandlerMapping 을 활용해 서비스가 가능하도록 통합해야한다. 그런데 DispatcherServlet 기존 코드에는 컨트롤러를 RequestMapping 클래스로 정보를 담고있다. 즉 DispatcherServlet 은 이전까지 레거시 MVC 프레임워크의 매핑 정보를 담고있는 &lt;code class=&quot;language-text&quot;&gt;RequestMapping&lt;/code&gt; 과, 현재 어노테이션 기반으로 매핑 정보를 담고있는 &lt;code class=&quot;language-text&quot;&gt;AnnotationHandlerMapping&lt;/code&gt; 의 오브젝트를 모두 필드로 가지고, 지원해야한다.&lt;/p&gt;
&lt;p&gt;RequestMapping 과 AnnotationHandlerMapping 을 모두 지원하기 위해 둘을 각각 분리해서 관리할 수도 있겠지만, 둘을 일관된 인터페이스로 통합한 후 관리하는 것이 확장성 측면에서 유리하다. 이 2가지 매핑 클래스에 대해 추상화한 공용 인터페이스를 &lt;code class=&quot;language-text&quot;&gt;HandlerMapping&lt;/code&gt; 이라는 이름으로 추가했다. 즉, RequestMapping 과 AnnotationHandlerMapping 클래스가 HandlerMapping 인터페이스를 구현한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerMapping&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;legacyrequestmapping&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#legacyrequestmapping&quot; aria-label=&quot;legacyrequestmapping permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LegacyRequestMapping&lt;/h3&gt;
&lt;p&gt;또한 기존 RequestMapping 클래스의 이름을 &lt;code class=&quot;language-text&quot;&gt;LegacyRequestMapping&lt;/code&gt; 으로 이름을 변경했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LegacyHandlerMapping&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerMapping&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; mappings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

   &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;initMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

   &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; mappings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestURI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;dispatcherservlet-리팩토링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dispatcherservlet-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81&quot; aria-label=&quot;dispatcherservlet 리팩토링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DispatcherServlet 리팩토링&lt;/h3&gt;
&lt;p&gt;DispatcherServlet 이 HandlerMapping 인터페이스의 구현체 2가지 타입인 LegacyHandlerMapping, AnnotationHandlerMapping 에 대해 모두 동작(호환) 되도록 개선했다. 초기화가 끝난 HandlerMapping 을 List 로 관라하면서 요청 URL 과 HTTP 메소드에 해당하는 컨트롤러를 찾아 컨트롤러가 존재할 경우 컨트롤러에게 작업을 위임하도록 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@WebServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dispatcher&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; urlPatterns &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loadOnStartUp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispacherServlet&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServlet&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DispacherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HandlerMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; mappings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Lists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newArrayList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;LegacyHandlerMapping&lt;/span&gt; lhm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LegacyHandlerMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    lhm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;initMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;AnnotationHandlerMapping&lt;/span&gt; ahm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnnotationHandlerMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;next.controller&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    ahm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;initalize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    mappings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lhm&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    mappings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ahm&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletRespisnoe&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; handler &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handler &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IllegalArgumentExcpetion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;존재하지 않는 URL입니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt; mav &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handler&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; view &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Throwable&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Exception : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HandlerMapping&lt;/span&gt; handlerMapping &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; mappings&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; handler &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; handlerMapping&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handler &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; handler&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;execurte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; handler&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handler &lt;span class=&quot;token keyword&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Contoller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executea&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HandlerExecution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;controller-어노테이션-활용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#controller-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%ED%99%9C%EC%9A%A9&quot; aria-label=&quot;controller 어노테이션 활용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@Controller 어노테이션 활용&lt;/h2&gt;
&lt;p&gt;모든 작업이 끝났다. 이제 테스트 컨트롤러를 생성한 후 아래와 같이 @Controller 어노테이션을 명시하면 원활히 동작할 것이다.
앞서 코드 구현을 통해 기존 컨트롤러에 대한 지원도 가능하면서 새롭게 추가한 AnnotationHandlerMapping 도 지원하므로 호환성이 유리한 MVC 프래임워크가 되었다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Controller&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TestController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/get-test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; method &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; modelAndView&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/post-test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; method &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; modelAndView&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이렇게 어노테이션 기반 Spring MVC 를 모방한 프레임워크를 구현할 수 있었다. 스프링부트 프레임워크의 내부 동작원리를 깊게 이해할 수 있었던 가장 유익한 학습거리가 되었다! 지금까지 학습해왔던 내용들중에 가장 큰 배움이 있었던 미션이지 않나 싶다. 기존 레거시 코드에 대한 처리, 점진적인 리팩토링 과정하는 과정이 꽤 어려웠지만, MVC 프레임워크를 직접 구현해보니 덕분에 이해도가 훨씬 높아졌다.&lt;/p&gt;
&lt;p&gt;다만, 아직 DI 컨테이너 직접 구현하기에 대한 미션이 남아있다. 어서 진행해보고 싶은 마음이다. DI 컨테이너를 구현한 후, DI 컨테이너와 현재 구현된 Spring MVC 코드를 어서 통합해봐야겠다 🙂&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해야-할-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EC%95%BC-%ED%95%A0-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해야 할 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해야 할 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;자바 리플렉션 (JAVA Reflection)&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[HTTPS 의 SSL 기반 암호화, 복호화 통신 과정은 어떻게 이루어질까?]]></title><description><![CDATA[HTTP vs HTTPS HTTPS 는 HTTP 프로토콜에 SSL…]]></description><link>https://haon.site/haon/network/ssl-tls/</link><guid isPermaLink="false">https://haon.site/haon/network/ssl-tls/</guid><pubDate>Fri, 08 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;http-vs-https&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-vs-https&quot; aria-label=&quot;http vs https permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP vs HTTPS&lt;/h2&gt;
&lt;h3 id=&quot;https-는-http-프로토콜에-ssl-을-추가로-덧붙였다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#https-%EB%8A%94-http-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%97%90-ssl-%EC%9D%84-%EC%B6%94%EA%B0%80%EB%A1%9C-%EB%8D%A7%EB%B6%99%EC%98%80%EB%8B%A4&quot; aria-label=&quot;https 는 http 프로토콜에 ssl 을 추가로 덧붙였다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTPS 는 HTTP 프로토콜에 SSL 을 추가로 덧붙였다.&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;HTTP(Hypertext Transfer Protocol)&lt;/code&gt; 은 서버에서 브라우저로 데이터를 전송시 가장 널리 사용되는 프로토콜이지만, 전송되는 정보가 암호화되지 않아 보안에 취약하다는 문제점이 있다. 그로인해 등장한 프로토콜이 &lt;code class=&quot;language-text&quot;&gt;HTTPS(Hypertext Transfer Procotol Secure)&lt;/code&gt; 로, 기존 HTTP 프로토콜에 &lt;code class=&quot;language-text&quot;&gt;SSL&lt;/code&gt; 을 추가적으로 덧붙여 사용한 프로토콜이다.&lt;/p&gt;
&lt;h3 id=&quot;http-그-자체를-암호화하진-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-%EA%B7%B8-%EC%9E%90%EC%B2%B4%EB%A5%BC-%EC%95%94%ED%98%B8%ED%99%94%ED%95%98%EC%A7%84-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;http 그 자체를 암호화하진 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP 그 자체를 암호화하진 않는다.&lt;/h3&gt;
&lt;p&gt;그렇다고 HTTPS 는 HTTP 그 자체를 모두 암호화하는 것은 아니다. HTTP 를 사용해서 운반하는 내용, 즉 HTTP 의 Message Body 를 암호화하지만 HTTP Header 는 아쉽게도 암호화하지 않는다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ssl-과-tls&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ssl-%EA%B3%BC-tls&quot; aria-label=&quot;ssl 과 tls permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SSL 과 TLS&lt;/h2&gt;
&lt;p&gt;그렇다면 SSL 은 무엇일까? &lt;code class=&quot;language-text&quot;&gt;SSL(Secure Socket Layer)&lt;/code&gt; 의 약자로, Netscape 사에서 보안성이 강화된 통신을 위해 만든 프로토콜이다. 가장 큰 특징은 &lt;code class=&quot;language-text&quot;&gt;공개키(public key) 와 개인키(private key)&lt;/code&gt;, 즉 공개키 방식과 대칭키 방식을 혼합해서 함께 사용한다는 것이다.&lt;/p&gt;
&lt;h3 id=&quot;대칭키-symmetric-key&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8C%80%EC%B9%AD%ED%82%A4-symmetric-key&quot; aria-label=&quot;대칭키 symmetric key permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;대칭키 (Symmetric Key)&lt;/h3&gt;
&lt;p&gt;대칭 키란 데이터 송.수신자 사이에 동일한 Key 값을 기반으로 암호화 및 복호화를 수행하는 기법이다. 그래서 이 암호화.복호화에 활용할 대칭키값만 탈취한다면, 해당 데이터를 쉽게 복호화할 수 있다. 이 떄문에 대칭키 방식은 Key 값이 탈취되면 암호화 복호화가 매우 쉽게 진행된다는 취약점이 존재한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a3d17c4f13ec44c801fb49be1cd50510/0940f/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.73619631901841%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABxElEQVR42qWTy27TQBSG+0S8AW+CxIIFi2xYwYYNQmKBuIgVLJBYgIQoKKKioC5YIBJCu0BtoLk0dhLq+FJP3CbGcZ3El49xm1Y2SQQSRxpZ8+vMd/4zc7xCJiaTCVO50m8aSXKqOwMPzXBymuv+wjBtgiDIIljJbvSeTvlzGbWlIITAMHR0w+JD8REvnlzD6Qss64Cf+xqV0ifu3S5g6AbZSjmgsAV7jSb2gU0URUzGAeMpfHlzmY3HF/C9AWEU4/s+P75XWX35XBawZsAFDlP7iqqc75NZVVM6UurNnOY4DrV646Tw0pb/jDCaEiQ+Xk/FbTeZEjINgxk4Jo7juTNzwNRBnJwmtpxtHr6+xIP3N7i/fp07xQLl7to5cFEsdHjW1uBYsK2V2a1tsFt9x465iel2ZleW/B2YXraiqBwdHuXAarXFt9JOThuNRgxkXnpmDniWpGk6d28V2KqUOJTJlmXS3e+x/uomb59dwbZNRL9Pp92mUauz9XVTjlpvuUNh26wVV9lrNgjDUL76MaG8qkrxKh+fXsQfuVKPTvS+SMEdhsPhcmD6h6Sj4Hlerj3DMGl3umQHbixnVFXVf3uU/4nff2mPR00joqYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/a3d17c4f13ec44c801fb49be1cd50510/a6d36/image-1.png&quot;
        srcset=&quot;/static/a3d17c4f13ec44c801fb49be1cd50510/222b7/image-1.png 163w,
/static/a3d17c4f13ec44c801fb49be1cd50510/ff46a/image-1.png 325w,
/static/a3d17c4f13ec44c801fb49be1cd50510/a6d36/image-1.png 650w,
/static/a3d17c4f13ec44c801fb49be1cd50510/e548f/image-1.png 975w,
/static/a3d17c4f13ec44c801fb49be1cd50510/0940f/image-1.png 1154w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;공개-키-public-key&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%B5%EA%B0%9C-%ED%82%A4-public-key&quot; aria-label=&quot;공개 키 public key permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;공개 키 (Public Key)&lt;/h3&gt;
&lt;h4&gt;대칭 키 방식과 비교&lt;/h4&gt;
&lt;p&gt;대칭 키(Symmetric Key) 방식이 통신의 양방향측에서 서로 동일한 Key 값으로 암호화하는 방식이라면, 공개 키는 서로 다른 Key 값으로 암호화.복호화를 수행하는 기법이다. &lt;strong&gt;즉, 대칭키와 공개키 방식은 동일한/서로 다른 Key 값으로 암호화(복호화)를 수행하는가에 따라 차이가 나는 기법이라고 정리할 수 있다.&lt;/strong&gt; 이 때문에 공개 키 방식은 &lt;code class=&quot;language-text&quot;&gt;비대칭키(Non Symmetric Key)&lt;/code&gt; 방식이라고도 불린다.&lt;/p&gt;
&lt;h4&gt;공개 키&lt;/h4&gt;
&lt;p&gt;더 자세히는 &lt;strong&gt;데이터를 암호화할 땐 공개키를 사용하고, 데이터를 복호화할 떄는 개인키를 사용한다. 공개키로 암호화한 데이터는 오직 개인키로만 복호화할 수 있기 떄문에, 그 누구던지 공개키를 탈취해도 문제가 없다.&lt;/strong&gt; 즉, 공개키는 말 그대로 공개된 키로써 중간에 누군가에게 탈취되어도 문제가 발생하지 않는다.&lt;/p&gt;
&lt;p&gt;데이터의 송.수신자 입장에서 다시 정리해보자. 데이터 송신자는 전달할 데이터를 공개키를 기반으로 암호화하여 전송한다. 암호화된 데이터를 전송받은 수신자는, 해당 데이터를 복호화하기 위해 개인키를 활용하여 복호화를 진행한 후 데이터를 열람할 수 있게된다. 도중에 제 3자가 공개키를 탈취하고 데이터 복호화를 시도하려고 한들, 개인키 또한 탈취하지 않는 이상 복호화가 불가능하다.&lt;/p&gt;
&lt;p&gt;하지만 이 또한 단점이 있다. 공개키 방식은 대칭키 방식에 비해 &lt;strong&gt;암호화 연산 시간이 더 오래걸린다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;최종적으로 정리해보자면, 공개 키 방식은 통신 양측간에 서로 다른 Key 값을 사용하여 전달받은 암호문을 복호화한다고 했다. 이떄 &lt;strong&gt;서로 다른 Key 값이란 개인 키(private key) 를 의미한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;공개키-vs-대칭키&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%B5%EA%B0%9C%ED%82%A4-vs-%EB%8C%80%EC%B9%AD%ED%82%A4&quot; aria-label=&quot;공개키 vs 대칭키 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;공개키 vs 대칭키&lt;/h3&gt;
&lt;p&gt;결국 공개 키가 가진 단점을 대칭키에선 장점으로 누리고 있으며, 반대로 대칭키에서 가진 단점을 공개키에선 장점으로 활용하고 있다.
이 때문에 SSL 은 2가지 방식이 가진 장.단점 떄문에 이 둘을 모두 적절히 활용한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ssl-의-통신-과정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ssl-%EC%9D%98-%ED%86%B5%EC%8B%A0-%EA%B3%BC%EC%A0%95&quot; aria-label=&quot;ssl 의 통신 과정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SSL 의 통신 과정&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/000d521f932d81538b544d7bde416926/39148/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.601226993865026%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB60lEQVR42pWS72rTYBTGeym9A+9hfpi34WfvQxQRQZGCCPskdkXYBBEdblpBsEKdDtxcU836Z13a5k+TJk3SJu/Pk7TdJkXRF07Om3PO+/Cc55wCyxP0YerzL0elMFMxEzVeyRWyT7vT4eHdmxx/O8iDaZr+EWyZ297e4vadWyu1OeDR0SHr69d4vbOTB5MkuYQQQjwUH5OkKg+Vy2WKxSJra1cZj+cslVIXgGEYUqlUaDabebA1+MnAMRi7Lq55gtvbx7O6uCNbaifUajWq1Sq6rq8yzJAzbFceZ8lUyN34eJ1n2gZYOp5RJzA1AuOzyLxP4FlSp4iiiCAIFuwy0CXDWQz2GZZpMYtGhMYnjB8vsfqH0s4Eb+TgeWPc7O75eOJ9fyJgPv45oLpgeNp9T+lxkcbxV5Kzt6j2UzBewHBPdIvmVVMLoraIKz7uSTwgiqf4loayP5D035Eab1CTHoVu2OFRp0TPPJVdmEphhOM4jBxLzM7vrujXbHyhu/sAu74p8khM2JqNV6Btkra2SLvPUd73uYYsNMymmymaiJZJkub/mWXaBjOp2btPUK8wy9ZW2m5qB2w8uUKrvbtoPVkOJc01+ev+CbAjrFw/ENY2w4HJyLcp6ffQfe18OAX+42QP5qZ+G8TltfkFNI4+V9+TCDgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/000d521f932d81538b544d7bde416926/a6d36/image-6.png&quot;
        srcset=&quot;/static/000d521f932d81538b544d7bde416926/222b7/image-6.png 163w,
/static/000d521f932d81538b544d7bde416926/ff46a/image-6.png 325w,
/static/000d521f932d81538b544d7bde416926/a6d36/image-6.png 650w,
/static/000d521f932d81538b544d7bde416926/e548f/image-6.png 975w,
/static/000d521f932d81538b544d7bde416926/3c492/image-6.png 1300w,
/static/000d521f932d81538b544d7bde416926/39148/image-6.png 2876w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;SSL 은 2가지 방식을 적절히 섞어 활용한다고 했다. 더 정확히는 SSL 은 공개키 방식으로 대칭키를 얀전하게 전달하여, 서로가 동일한 대칭키 값을 기반으로 통신하는 방식이다. 이 대칭키를 활용하여 암호화, 복호화를 하며 통신을 진행한다.&lt;/p&gt;
&lt;p&gt;우선 송신자는 수신자에게 접속 요청을 보내서 수신자의 공개키를 전달받는다. 그러면 송신자는 전달받은 공개키를 기반으롷 본인의 대칭키를 암호화한다. 암호화된 결과(대칭키)를 수신자에게 전달하면, 수신자는 전달받은 대칭키를 자신의 개인키로 복호화하여, 그 복호화 결과로 송신자의 대칭키를 얻어낼 수 있게된다.&lt;/p&gt;
&lt;p&gt;즉, 데이터 암호화 및 복호화를 위한 한 쪽의 대칭키를 다른쪽의 공개키로 암호화하여 전송하면, 반대편에서 자신의 개인키로 복호화하여 그 반대편의 대칭키를 알아내고, 이 동일한 대칭키 값을 바탕으로 서로 안전하게 통신할 수 있게된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ssl-의-유효한-사이트-판별-방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ssl-%EC%9D%98-%EC%9C%A0%ED%9A%A8%ED%95%9C-%EC%82%AC%EC%9D%B4%ED%8A%B8-%ED%8C%90%EB%B3%84-%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;ssl 의 유효한 사이트 판별 방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SSL 의 유효한 사이트 판별 방법&lt;/h2&gt;
&lt;p&gt;그렇다면 사용자가 접속한 사이트가 유효한 사이트인지 어떻게 확인할 수 있는가? 즉, 특정 사이트가 HTTPS 프로토콜을 사용하여 사이트 사용자에게 안전한 사이트임을 알리기 위해선 &lt;code class=&quot;language-text&quot;&gt;사이트 인증기관(CA)&lt;/code&gt; 로 부터 &lt;code class=&quot;language-text&quot;&gt;SSL 인증서 (사이트 인증서)&lt;/code&gt; 를 발급받아야 한다. 이 과정을 자세히 살펴보자.&lt;/p&gt;
&lt;h3 id=&quot;1-ca-인증기관-과의-협력&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-ca-%EC%9D%B8%EC%A6%9D%EA%B8%B0%EA%B4%80-%EA%B3%BC%EC%9D%98-%ED%98%91%EB%A0%A5&quot; aria-label=&quot;1 ca 인증기관 과의 협력 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. CA 인증기관 과의 협력&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/980dfb4d444da4b03b699dcb0044a94e/efa1a/image-7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.44171779141104%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABuUlEQVR42pWT2WpTQRiA8xKKD5R3EfTevoEiKHrvlUsQimJdLioB9UJFvclS05pi0HCa6tmSs+8zn39yStpDW9GBf2aY+ef712lx5tD1HDuowx7Vrx1ZB2hzF/IFlCEUvmhUp162OGcopVbr7mjExsY1+oMBZRagFwK3P6Gjn2Lxf4C69nI8HtNut+l2u/X5Ykh18FKgH1CzVxKF0YjqGFhGEoaHLkJ07lLlPr7vkWUZ/X4fx7ZXatW8hzK2UNMnIpvozD4HmByiw0ltMdjDPRiSJnEjr647xwuiBuCfQvajlFCkfqcln9UKEAQBlmmSJHKnUspgQhVOZZ+dAErFdBlI9VwJeU6aJszn7rFfqYn2v4nsSf52cC0D23bJ7CFEX8D7KAWarr1uKeczlfEcZk8xeo/pdDpHjh2FtKykKkRy2efkebbKZ1kW3H/wiO3t1w391pKqxLs4tNif/OD2nbuNtml0p66nolJcv3mLCxcvcfnK1dPA5ch8E3f0jq9vN6VAVn2q9RlEMebPeHHvBm+2HvL9/TN08Ht9f6JtMsmFhZLfofOIv/2geGEROwZp4ODN9kk8Zw38A2FhRuCHdyI+AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/980dfb4d444da4b03b699dcb0044a94e/a6d36/image-7.png&quot;
        srcset=&quot;/static/980dfb4d444da4b03b699dcb0044a94e/222b7/image-7.png 163w,
/static/980dfb4d444da4b03b699dcb0044a94e/ff46a/image-7.png 325w,
/static/980dfb4d444da4b03b699dcb0044a94e/a6d36/image-7.png 650w,
/static/980dfb4d444da4b03b699dcb0044a94e/e548f/image-7.png 975w,
/static/980dfb4d444da4b03b699dcb0044a94e/3c492/image-7.png 1300w,
/static/980dfb4d444da4b03b699dcb0044a94e/efa1a/image-7.png 2040w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이해를 돕기위해, 사이트를 네이버에 비유하겠다. 가장 먼저 네이버는 본인이 안전한 사이트임을 사용자에게 알리고, HTTPS 프로토콜을 지원하기 위해 SSL 인증서를 발급받아야한다. 이를위해 네이버 본인의 사이트 정보와 Public key 를 CA 인증기관에게 전송하여 SSL 인증서를 발급받기를 시도한다. 이 기대에 따라 CA 인증기관은 전달받은 데이터를 검증하여 (CA 기관 본인의 private key 로 데이터를 서명하는 과정 발생) 네이버에게 SSL 인증서를 발급해준다.&lt;/p&gt;
&lt;p&gt;또한 CA 인증기관은 네이버 사용자에게 본인의 public key 를 전달하고, 이 공개키는 사용자 브라우저내에 자동으로 내장된다.&lt;/p&gt;
&lt;h3 id=&quot;2-사용자가-웹-사이트-접속-요청시&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EC%82%AC%EC%9A%A9%EC%9E%90%EA%B0%80-%EC%9B%B9-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EC%A0%91%EC%86%8D-%EC%9A%94%EC%B2%AD%EC%8B%9C&quot; aria-label=&quot;2 사용자가 웹 사이트 접속 요청시 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 사용자가 웹 사이트 접속 요청시&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1830a29a662d7c8e9c9abd37362190cb/1d7f7/image-8.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.920245398773%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABpElEQVR42qWSS2/TUBCF84fKD0D9b9AVSxZI7Cs2sKECREXbwAIQEmpVEpFWtEghJI4TO4kf14/Yse/9mDhOCalgw5WO5j5G586cOQ3+a5lb+8Y/040kqWsI2uB/EbTQszNMEa8TahqzgtFrwtVhDa31za/5rEPWPyS9eETefQbeKSwJ5yO0/QY9OqG0XktsYtLhitAkfbT1gnJwII8vQecURUkQ+Kg4F8wFSRX9uEBFMdk8YhH+IHK/oTMbMkeIypowHVH+fIru7lOOP9A8OcL3feloXanEwpWqLEjtqoNYPsgXJV87l3w+Pb+RqLEpahQpwijh3t4eYahIkgTHnTCZTnGHV7iDCyb2dzkHpOmcw6NjdnbucHd3F08KqIZicg89fodx3pJYHwl7LT4dH6BDu2rNm1gEdgffbuOPr/CHLWZWiyJVvHrymIcP7tN8vk/i9OopL4fgvEeLfiYUPaZdSvca7fXqwhdQKkG4igtvKTqZmhL323I9REkRanC5aRvzh6fMpm3+aimZg2hYQQZY1M647cPKOmbLtNtmNlv737m/ACQW+sODFUP+AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/1830a29a662d7c8e9c9abd37362190cb/a6d36/image-8.png&quot;
        srcset=&quot;/static/1830a29a662d7c8e9c9abd37362190cb/222b7/image-8.png 163w,
/static/1830a29a662d7c8e9c9abd37362190cb/ff46a/image-8.png 325w,
/static/1830a29a662d7c8e9c9abd37362190cb/a6d36/image-8.png 650w,
/static/1830a29a662d7c8e9c9abd37362190cb/e548f/image-8.png 975w,
/static/1830a29a662d7c8e9c9abd37362190cb/3c492/image-8.png 1300w,
/static/1830a29a662d7c8e9c9abd37362190cb/1d7f7/image-8.png 2038w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위 까지는 사용자가 사이트에 접속하기 전까지 내용이다. 이어서 사용자가 사이트 접속을 요청했을 때 어떤 일이 일어날까?&lt;/p&gt;
&lt;p&gt;우선 사용자가 네이버에 접속을 요청하면, 네이버는 앞서 CA 인증기관으로 부터 발급받은 SSL 인증서를 사용자에게 전달하여 자신이 신뢰할 수 있는 사이트임을 증명한다.&lt;/p&gt;
&lt;p&gt;그러면 사용자는 앞서 브라우저 내에 내장한 CA 인증기관의 public key 로 전달받은 네이버의 SSL 인증서를 복호화하여 검증한다. SSL 인증서 복호화에 성공했다면, 네이버에 대한 사이트 정보와 공개키를 얻을 수 있게된다. 이렇게 얻은 네이버 사이트의 public key 를 활용하여 사용자는 본인의 대칭키를 암호화하고, 그 암호화한 대칭키를 네이버에게 전달한다.&lt;/p&gt;
&lt;p&gt;이어서 네이버는 본인의 private key 로 전달받은 암호문을 복호화하여 사용자의 대칭키를 얻어내고, 이제 이렇게 얻은 대칭키를 활용하여 사용자와 네이버 사이트는 서로 암호문을 주고받을 수 있게 된다. 즉, SSL 통신을 할 수 있게된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=wPdH7lJ8jf0&amp;#x26;t=623s&quot;&gt;https://www.youtube.com/watch?v=wPdH7lJ8jf0&amp;#x26;t=623s&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nuritech.tistory.com/25&quot;&gt;https://nuritech.tistory.com/25&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wayhome25.github.io/cs/2018/03/11/ssl-https/&quot;&gt;https://wayhome25.github.io/cs/2018/03/11/ssl-https/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[AJAX 기반 레거시 MVC 프레임워크 구현하기]]></title><description><![CDATA[스터디를 진행하면서 알게된 학습 내용, 고민 사항을 정리했다. MVC 패턴, JSP MVC 패턴이 등장하기 이전까지만 해도 대부분의 로직은 JSP 에 모두 담겨있었다. JSP…]]></description><link>https://haon.site/haon/spring/ajax-based-mvc-impementation/</link><guid isPermaLink="false">https://haon.site/haon/spring/ajax-based-mvc-impementation/</guid><pubDate>Wed, 06 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;스터디를 진행하면서 알게된 학습 내용, 고민 사항을 정리했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;mvc-패턴-jsp&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mvc-%ED%8C%A8%ED%84%B4-jsp&quot; aria-label=&quot;mvc 패턴 jsp permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MVC 패턴, JSP&lt;/h2&gt;
&lt;p&gt;MVC 패턴이 등장하기 이전까지만 해도 대부분의 로직은 JSP 에 모두 담겨있었다. JSP 에 상당 로직을 포함하는 것이 초기 개발 속도는 빠를진 몰라도 유지보수 비용은 상당했다.&lt;/p&gt;
&lt;p&gt;MVC 패턴을 적용할 경우 기존 JSP 만이 존재할 때와 다른점은, JSP 의 역할은 단순히 컨트롤러로부터 전달된 데이터를 단순히 출력하는 로직만을 포함하고 있다는 점이다. 대부분의 로직은 컨트롤러와 모델이 담당하고 있고, JSP 는 단순 뷰의 로직만을 수행하면 된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1단계-dispatchersevlet-기반-mvc-프레임워크-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1%EB%8B%A8%EA%B3%84-dispatchersevlet-%EA%B8%B0%EB%B0%98-mvc-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;1단계 dispatchersevlet 기반 mvc 프레임워크 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1단계. DispatcherSevlet 기반 MVC 프레임워크 구현&lt;/h2&gt;
&lt;p&gt;우선 기본적인 MVC 프레임워크 1단계를 진행했다. 지난 실습에선 RequestHandler 에서 모든 요청을 받고 요청 URL 에 따라 분기 처리해줬는데, 이를 서블릿으로 변경한다. 즉 &lt;code class=&quot;language-text&quot;&gt;DispatcherServlet&lt;/code&gt; 서블릿을 구현하하고, 이 하나의 서블릿이 모든 요청을 받고 요청 URL 에 따라 분리 처리하도록 구현했다.&lt;/p&gt;
&lt;h3 id=&quot;controller&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#controller&quot; aria-label=&quot;controller permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Controller&lt;/h3&gt;
&lt;p&gt;우선 클라이언트 요청에 대한 처리를 담당하는 부분인 컨트롤러를 추상화했다. &lt;code class=&quot;language-text&quot;&gt;execute()&lt;/code&gt; 에서 리다이렉트 방식으로 이동할 경우 &lt;code class=&quot;language-text&quot;&gt;redirect:&lt;/code&gt; 로 시작하고 forward 방식으로 이동할 경우 JSP 를 반환하도록 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Excpetion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;controller-구현체&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#controller-%EA%B5%AC%ED%98%84%EC%B2%B4&quot; aria-label=&quot;controller 구현체 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Controller 구현체&lt;/h3&gt;
&lt;p&gt;Controller 를 구현한 컨트롤러 구현체 중 하나다. 이렇게 생성된 여러 컨트톨러 오브젝트들은 이후 살펴볼 &lt;code class=&quot;language-text&quot;&gt;RequestMapping&lt;/code&gt; 에서 서비스에서 발생하는 모든 요청 URL 과 각 URL 에 대한 서비스를 담당할 컨트롤러가 매핑된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ListUserController&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isLogined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSession&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;redirect:/users/loginForm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;users&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Database&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/item/list.jsp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isLogined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpSession&lt;/span&gt; session&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;requestmapping&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#requestmapping&quot; aria-label=&quot;requestmapping permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RequestMapping&lt;/h3&gt;
&lt;p&gt;앞서 언급한 RequestMapping 이다. RequestMapping 은 서비스에서 발생하는 모든 요청 URL 과 각 URL 에 해당 서비스를 담당할 컨트롤러를 연결하는 작업을 담당한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestMapping&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; mappings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;initMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    mappings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HomeController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    mappings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/users/form&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ForwardController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    mappings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/users/profile&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProfileController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; mappings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt; controller&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    mappings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; controller&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;dispatcherservlet&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dispatcherservlet&quot; aria-label=&quot;dispatcherservlet permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DispatcherServlet&lt;/h3&gt;
&lt;p&gt;마지막으로 구현할 부분은 클라이언트의 모든 요청을 받아 URL 에 해당하는 컨트롤러로 작업을 위임하고, 실행된 결과 페이지로 이동하는 작업을 담당하는 &lt;code class=&quot;language-text&quot;&gt;DispatcherServlet&lt;/code&gt; 를 구현했다. 각 요청 URL 에 대한 매핑 컨트롤러 정보는 ReqeustMapping 필드를 통해 보유하고 있다.&lt;/p&gt;
&lt;p&gt;유심해서 볼 부분은 DispatcherServlet 을 &lt;code class=&quot;language-text&quot;&gt;프론트 컨트롤러 패턴&lt;/code&gt; 을 활용하여 구현했다는 점이다. &lt;code class=&quot;language-text&quot;&gt;move()&lt;/code&gt; 메소드를 보면 각 서블릿에서 서블릿과 JSP 사이를 이동하기 위해 모든 중복 코드를 이곳에서 담당하고 있으며, 이를 프론트 컨트롤러 패턴이라고 한다. DispatcherServlet 은 모든 컨트롤러 앞단에서 모든 요청을 가장 먼저 받아 각 컨트롤러에게 작업을 위임한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@WebServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dispatcher&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; urlPatterns &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loadOnStartUup &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServlet&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; serialVersionUID &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_REDIRECT_PREFIX&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;redirect:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestMapping&lt;/span&gt; rm&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletExcepton&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    rm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    rm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;initMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOExcpetion&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; requestUri &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rqe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestURI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt; controller &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requestUri&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; viewName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; controller&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Throwable&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Exception : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; viewName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletExcpetion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DEFAULT_REDIRECT_PREFIX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendRedirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DEFAULT_REDIRECT_PREFIX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;RequestDispatcher&lt;/span&gt; rd &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestDispatcher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    rd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2단계-ajax-기반-mvc-프레임워크로-개선&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2%EB%8B%A8%EA%B3%84-ajax-%EA%B8%B0%EB%B0%98-mvc-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC%EB%A1%9C-%EA%B0%9C%EC%84%A0&quot; aria-label=&quot;2단계 ajax 기반 mvc 프레임워크로 개선 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2단계. AJAX 기반 MVC 프레임워크로 개선&lt;/h2&gt;
&lt;p&gt;위 1단게 구현까지 마쳤다면 그것만으로 훌륭한 구현 코드라고 생각할 수 있으나, 아쉽게도 위 코드는 JSON 데이터를 응답하지 못한다. 즉, SSR 기반 HTML 정적 데이터 타입만을 응답하도록 구현된 코드이다. 따라서 HTML, JSON 2가지 형태를 모두 응답하는 MVC 프레임워크로 개선해보았다.&lt;/p&gt;
&lt;h3 id=&quot;기존-coneroller-의-문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EC%A1%B4-coneroller-%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;기존 coneroller 의 문제점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기존 Coneroller 의 문제점&lt;/h3&gt;
&lt;p&gt;앞서 보여줬던 컨트롤러 구현체중 하나다. 이 컨트롤러 구현체 하나만 놓고 본다면 썩 문제점이 발생하지 않을 것 같고, 기존 MVC 프레임워크 코드만으로도 원활히 동작할 것 으로 보인다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ListUserController&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isLogined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSession&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;redirect:/users/loginForm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;users&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Database&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/item/list.jsp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isLogined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpSession&lt;/span&gt; session&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 아래 컨트롤러를 보면 2가지의 문제점을 가지고있다. 우선 JSON 으로 응답을 보내는 경우 이동할 JSP 페이지가 없으니 불필요하게 null 을 리턴하고 있다. AJAX 에서 사용할 컨트롤러를 리턴값이 굳이 필요없다. 이 문제가 발생한 원인은, 기존과 달리 컨트롤러에서 응답할 뷰 타입이 JSP 하나에서 JSP, JSON 2가지로 늘어났기 떄문이다.&lt;/p&gt;
&lt;p&gt;또한 자바 객체를 JSON 으로 변환하고 응답하는 부분에서 중복이 발생한다. 중복 코드를 최소화할 필요가 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DeleteAnswerController&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; answerId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseLong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;answerId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;AnswerDao&lt;/span&gt; answerDao &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnswerDao&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    answerDao&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;answerId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;ObjectMapper&lt;/span&gt; mapper &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectMapper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setContentType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;application/json;charset=UTF-8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;PrintWriter&lt;/span&gt; out &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWriter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mapper&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeValueAsString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 불필요하게 null 을 리턴&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;modelandview&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#modelandview&quot; aria-label=&quot;modelandview permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ModelAndView&lt;/h3&gt;
&lt;p&gt;위 2가지 문제점을 해결할 수 있는것은 바로 &lt;code class=&quot;language-text&quot;&gt;ModelAndView&lt;/code&gt; 라는 추상화 클래스를 만드는 방법에 있었다. 이 클래스는 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 기존에는 JSP 뷰 타입만을 리턴했다면, 이젠 다른 종류의 뷰인 JSON 도 응답할 수 있도록 기능을 제공한다. 또한 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 컨트롤러의 &lt;code class=&quot;language-text&quot;&gt;exetute()&lt;/code&gt; 메소드에서 HttpServletRequest 타입으로 전달되는 모델 데이터를 별도의 Map 을 활용해 타입을 변환하여 전달하도록 한다.&lt;/p&gt;
&lt;p&gt;여기서 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 를 해야하는 이유가 있다. 만약 ModelAndView 에 Map 으로 변환하는 코드가 없는 경우라면, HttpServletRequest 에 추가되어 있는 모든 데이터를 JSON 으로 변경한다. 그런데 HttpServletRequest 의 경우 서블릿 필터, 서블릿의 여러 단계를 거치면서 개발자가 모르는 상태에서 추가 데이터가 발생할 수 있다. 이 경우 개발자가 의도치 않게 불필요한 데이터가 모두 JSON 으로 변환되어 클라이언트 응답으로 보내질 수 있으므로, HttpServletRequest 를 통해 데이터를 전달하지 않고 개발자가 원하는 데이터만 정확하게 뷰에 전달할 수 있도록 모델 데이터에 대한 추상화 작업을 진행해야 하는 것이다.&lt;/p&gt;
&lt;p&gt;이로써 뷰를 포함해 모델 데이터에 대한 추상화를 담당하는 클래스인 ModelAndView 를 따로 분리시켰다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; model &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;view &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; attributeName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; attributeValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    model&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;attributeName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; attributeValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Collections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unmodifiableMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;model&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;view&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#view&quot; aria-label=&quot;view permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;View&lt;/h3&gt;
&lt;p&gt;ModelAndView 를 보면 View 타입 필드가 있는것을 볼 수 있다. JSP 와 JSON 뷰를 추상화 한 것이다. 이 인터페이스의 구현체로 &lt;code class=&quot;language-text&quot;&gt;JspView&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;JsonView&lt;/code&gt; 를 만들었다. 간단한 if-else 분기 처리로 뷰 타입에 따른 로직을 수행하지 않고 따로 클래스로 분리시킴으로써 다양한 뷰 타입에 대한 유연한 확장을 가능하도록 했다. JSP, JSON 외의 신규 뷰 타입이 추가될 경우 추가 View 구현 클래스를 만들면 된다.&lt;/p&gt;
&lt;p&gt;또한 &lt;code class=&quot;language-text&quot;&gt;render()&lt;/code&gt; 메소드를 보면 모델 데이터를 파라미터로 전달할 수 있도록 했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; model&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResposne&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;jspview&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jspview&quot; aria-label=&quot;jspview permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JspView&lt;/h3&gt;
&lt;p&gt;JSP 에 대한 페이지 이동 처리를 담당한다. 이동할 뷰 이름을 생성자로 받은 후, &lt;code class=&quot;language-text&quot;&gt;render()&lt;/code&gt; 메소드를 호출 시 해당 페이지로 이동하도록 했다. 이 부분은 기존의 &lt;code class=&quot;language-text&quot;&gt;DispatcherServlet&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;move()&lt;/code&gt; 메소드 구현 부분을 &lt;code class=&quot;language-text&quot;&gt;render()&lt;/code&gt; 메소드 내에다 옮겼다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JspView&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_REDIRECT_PREFIX&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;redirect:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; viewName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JspView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; viewName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewName &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NullPointerExcpetion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;viewName is null. 이동할 URL 을 입력해주세요.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;viewName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; viewName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; model&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DEFAULT_REDIRECT_PREFIX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendRedirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DEFAULT_REDIRECT_PREFIX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; keys &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; model&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keySet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; keys&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; model&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;RequestDispatcher&lt;/span&gt; rd &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestDispatcher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	rd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;jsonview&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jsonview&quot; aria-label=&quot;jsonview permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JsonView&lt;/h3&gt;
&lt;p&gt;JSON 데이터 응답을 담당할 View 이다. 이동할 URL 이 없기 떄문에, &lt;code class=&quot;language-text&quot;&gt;render()&lt;/code&gt; 메소드는 HttpServletRequest 를 통해 전달되는 자바 객체를 JSON 으로 변환한 후 응답하는 기능을 가지도록 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JsonView&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;redner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; model&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;ObjectMapper&lt;/span&gt; mapper &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectMapper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setContentType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;application/json;charset=UTF-8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;PrintWriter&lt;/span&gt; out &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWriter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mapper&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeValueAsString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;model&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;controller-리팩토링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#controller-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81&quot; aria-label=&quot;controller 리팩토링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Controller 리팩토링&lt;/h3&gt;
&lt;p&gt;컨트롤러가 이젠 JSP 외에 JSON 타입도 지원해야 하므로, 기존에 String 타입을 리턴했던 코드를 ModelAndView 를 리턴하도록 개선했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;abstractcontroller&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#abstractcontroller&quot; aria-label=&quot;abstractcontroller permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AbstractController&lt;/h3&gt;
&lt;p&gt;ModelAndView 생성을 더 쉽도록 돕기 위한 추상 클래스다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AbstractController&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;jspView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; forwardUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JspView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;forwardUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;jsonView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JsonView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;controller-구현-클래스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#controller-%EA%B5%AC%ED%98%84-%ED%81%B4%EB%9E%98%EC%8A%A4&quot; aria-label=&quot;controller 구현 클래스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Controller 구현 클래스&lt;/h3&gt;
&lt;p&gt;기존에 Controller 인터페이스를 구현했던 구체 컨트롤러를 이젠 AbstractController 를 상속하도록 개선했다. AbstractController 메소드인 &lt;code class=&quot;language-text&quot;&gt;jsonView()&lt;/code&gt; 를 통해 손쉽게 ModelView 오브젝트를 리턴하는 것을 볼 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AddAnswerController&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AbstractController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt; log &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AddAnswerController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Answer&lt;/span&gt; answer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Answer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;writer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;contents&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseLong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;questionId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;answer : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; answer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;AnswerDao&lt;/span&gt; answerDao &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnswerDao&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Answer&lt;/span&gt; savedAnswer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; answerDao&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;answer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setContentType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;application/json;charset=UTF-8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;jsonView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;answer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; savedAnswer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;dispatcherservlet-리팩토링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dispatcherservlet-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81&quot; aria-label=&quot;dispatcherservlet 리팩토링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DispatcherServlet 리팩토링&lt;/h3&gt;
&lt;p&gt;마지막 작업으로 DispatcherServlet 이 JSP 뷰 타입만을 지원하기 위해 String 을 리턴했다면, 이젠 ModelAndView 뷰 타입을 리턴하도록 개선했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@WebServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dispatcher&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; urlPatterns &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loadOnStartup &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServlet&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; serialVersionUID &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_REDIRECT_PREFIX&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;redirect:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestMapping&lt;/span&gt; rm&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        rm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        rm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;initMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; requestUri &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestURI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Method : {}, Request URI : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; requestUri&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt; controller &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requestUri&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt; modelAndView&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            modelAndView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; controller&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; view &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; modelAndView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;modelAndView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Throwable&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Exception : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; viewName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DEFAULT_REDIRECT_PREFIX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendRedirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DEFAULT_REDIRECT_PREFIX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;RequestDispatcher&lt;/span&gt; rd &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestDispatcher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        rd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이로써 다양한 뷰 타입을 지원할 수 있는 MVC 프레임워크를 구현해봤다. 아직 100% 소화된 느낌은 아니기에, 조금 더 추가 학습을 해봐야겠다 🙂&lt;/p&gt;
&lt;p&gt;다음은 어노테이션 기반 MVC 프레임워크를 구현한 내용에 대해 다루고자 한다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[OSI 7계층의 물리, 데이터 링크 계층(1~2계층)]]></title><description><![CDATA[OSI 에 대한 전반적인 개요는 OSI 7계층, TCP/IP 프로토콜의 기본 규약 정리 를 참고하자. 물리 계층(Physical Layer) OSI…]]></description><link>https://haon.site/haon/network/physical-data-link/</link><guid isPermaLink="false">https://haon.site/haon/network/physical-data-link/</guid><pubDate>Tue, 05 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;OSI 에 대한 전반적인 개요는 &lt;a href=&quot;https://haon.blog/haon/network/intro/&quot;&gt;OSI 7계층, TCP/IP 프로토콜의 기본 규약 정리&lt;/a&gt; 를 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;물리-계층physical-layer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%BC%EB%A6%AC-%EA%B3%84%EC%B8%B5physical-layer&quot; aria-label=&quot;물리 계층physical layer permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;물리 계층(Physical Layer)&lt;/h2&gt;
&lt;p&gt;OSI 7계층에서 최하위에 위치한 물리 계층은 &lt;strong&gt;컴퓨터들을 물리적으로 연결하거나 데이터를&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;전기 신호&lt;/code&gt; *&lt;em&gt;로 변환하고 제어하는 역할을 한다.&lt;/em&gt; 이때 전기 신호에는 아날로그, 디지털 신호 2가지 종류가 존재한다.&lt;/p&gt;
&lt;p&gt;송신자가 수신자에게 데이터(문장)를 전송하는 과정은 먼저 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 문장을 0과 1로 구성된 비트로 변환하고 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 해당 비트를 전기 신호(디지털 신호)로 변환한 후 &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 수신자에게 해당 디지털 신호를 전달한다. 이후 &lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 수신자 측에서는 전기 신호(디지털 신호)를 다시 0과 1로 구성된 비트로 변환한다.&lt;/p&gt;
&lt;h3 id=&quot;랜-카드lan-card&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9E%9C-%EC%B9%B4%EB%93%9Clan-card&quot; aria-label=&quot;랜 카드lan card permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;랜 카드(LAN Card)&lt;/h3&gt;
&lt;p&gt;이때 직전에 설명한 과정속에서 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에서 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 로 넘어갈 때, 즉 전기 신호를 변환하는 것은 랜 카드가 해주는 것이다. 즉, 랜 카드란 전기 신호를 변환하는 역할로써, 데이터가 내 컴퓨터 밖을 나가는 출발점이라고 할 수 있다.&lt;/p&gt;
&lt;p&gt;정리하자면, &lt;strong&gt;랜 카드란 0과 1의 비트를 전기 실호로 변환하여 밖으로 내보내는 출발점 역할을 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;케이블&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BC%80%EC%9D%B4%EB%B8%94&quot; aria-label=&quot;케이블 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;케이블&lt;/h3&gt;
&lt;p&gt;한편 케이블은 랜 카드가 내보내는 전기 신호를 다른 곳으로 전달하는 역할을 수행한다. &lt;strong&gt;즉, 케이블이란 컴퓨터, 서버, 라우터 및 다른 네트워크 장치간에 데이터 신호를 전달하는 역할을 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;케이블은 구리선과 광섬유로 구성되며, &lt;code class=&quot;language-text&quot;&gt;동축 케이블&lt;/code&gt; 이 가장 많이 사용되는 케이블 종류다. 동축 케이블은 중앙의 구리선에 흐르는 전기 신호로 데이터를 주고받는다. 한편 컴퓨터에서 사용되는 &lt;code class=&quot;language-text&quot;&gt;꼬임선 방식&lt;/code&gt; 으로도 케이블을 구성할 수 있는데, 이는 말 그대로 몇 개의 전선들을 꼬아서 만든 케이블이다. 이 꼬임선 방식을 이용하는 케이블의 종류로는 &lt;code class=&quot;language-text&quot;&gt;UTP&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;FTP&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;STP&lt;/code&gt; 가 존재한다.&lt;/p&gt;
&lt;p&gt;꼬임선 방식에 대해 더 자세히 설명하는 것은 현재 나에겐 불필요한 지식이라고 판단되어 설명을 생략한다.&lt;/p&gt;
&lt;h3 id=&quot;리피터-vs-허브&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%ED%94%BC%ED%84%B0-vs-%ED%97%88%EB%B8%8C&quot; aria-label=&quot;리피터 vs 허브 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리피터 vs 허브&lt;/h3&gt;
&lt;h4&gt;리피터 (하위호환)&lt;/h4&gt;
&lt;p&gt;물리 계층에서 사용하는 장비로는 &lt;code class=&quot;language-text&quot;&gt;리피터&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;허브&lt;/code&gt; 가 있다. &lt;code class=&quot;language-text&quot;&gt;리피터(Reapter)&lt;/code&gt; 란 &lt;strong&gt;전기 신호를 &quot;증폭&quot;&lt;/strong&gt; 하는 기능을 가진 장치이다. 전기 신호를 전송할 때 거리가 멀어지면 신호가 감쇠되는 성질이 있느데, 이때 감쇠도니 신호를 다시 재생하여(증폭시켜서) 전다랗는 장치가 리피터다. 하지만 요즘 시대에선 리피터를 거의 사용하지 않는데, 이 기능을 허브라는 장치에서 대신해주기 때문이다.&lt;/p&gt;
&lt;h4&gt;허브 (상위호환)&lt;/h4&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ca9e2f57fc12cb00117035e00c47f21f/f098e/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.50920245398773%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACaUlEQVR42pWS20tUURTGD13oT4ggeuxB6jEiovcKxF56ELMyDG9QIiTdRNMUjIHBwNGSNM1RGlNIRcfRctKUEE17kC7mrWmm5urM6MyZy5nza8+MjURWdGCdvc+31vedtb+9JP7yRKJRwiIsVitjExN4/X4iioKqqn/kSNuBsVgssT4xjZJZqSFPU09ubR3nq7WcLa/lq9OVrNtGeFtBRUkK3mvv4mBmLscLr3GiqJS0rHwu1Wix2J3/J/iz0Dg9y2VdO7qBURoGzVQ+7af0sYH1DU8ir/5bMFmgxoR3G0sEwj5cYZn1iBtZ4D5FxhWwIwdWiIQcv3B+E4z7FhO5mBJEXl8gGvEmL0Z2EQ7aNjuKEfQvCEuihIJWQoFVwVESkvFu4yHFXykv1BDhwKIgBLf8DHtFOJOkOBDfb/5MiayhRq2CFhSnSmpIiUWMxuTsHINjJlQlnEiEPB7qRobRtLXg9ySPp/h8NJvN1HXo2fD5E9jc/Dsy9K1cuVPFdzFeksNuJ8dkZMdQPzvTDlNdUcUni4W0oQGk0SGkvfsoyCvE6nBwZKAP6dUw0v4DFObkYng7I75H2D1iRNq1h6vFJUjNL0SBsZdTk+McOnqMi1nZtE1OIBn0lIgOT6dncOv6DbpnppE628g3v+Rk+hmKC4q4+3oc6YGOpp4ezmVfoMPQhTS1+JkcrRZdrYb6xofYbDYsbjcaXQO1ZRWiqFvYoLDidFIjME15Fa36TuRQCPeaF/2jFm7fLGP8zdTWLX+Yf8/9xia+2R2py1haXqXXaCIciaSwldUvPHveR0CWU9iysOfj4lJqLn8AuxdBTnk85DMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;image&quot;
        title=&quot;&quot;
        src=&quot;/static/ca9e2f57fc12cb00117035e00c47f21f/a6d36/image.png&quot;
        srcset=&quot;/static/ca9e2f57fc12cb00117035e00c47f21f/222b7/image.png 163w,
/static/ca9e2f57fc12cb00117035e00c47f21f/ff46a/image.png 325w,
/static/ca9e2f57fc12cb00117035e00c47f21f/a6d36/image.png 650w,
/static/ca9e2f57fc12cb00117035e00c47f21f/e548f/image.png 975w,
/static/ca9e2f57fc12cb00117035e00c47f21f/3c492/image.png 1300w,
/static/ca9e2f57fc12cb00117035e00c47f21f/f098e/image.png 1506w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;허브는 리피터의 확장 버전이라고 보면된다. 즉, 허브는 전송되는 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; &lt;strong&gt;데이터 신호를 정형하고 증폭&lt;/strong&gt;하여 데이터 왜곡을 보정하며, 하나의 입력 신호를 여러 디바이스로 복제하여, &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; &lt;strong&gt;데이터에 대한 분배 및 네트워크 확장을 지원한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;리피터가 1:1 통신만 가능하다면, 허브는 포트를 여러개 가지고 있기 때문에 여러대의 컴퓨터와 연결할 수 있다. 그래서 요즘 시대에는 리피터가 아닌 허브를 사용하고 있다.&lt;/p&gt;
&lt;h4&gt;cf) 더미 허브&lt;/h4&gt;
&lt;p&gt;허브는 다음과 같은 특징이 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;각 컴퓨터가 허브로 데이터를 보낼 수 있다.&lt;/li&gt;
&lt;li&gt;그러면 허브에 연결된 모든 컴퓨터가 데이터틀 받는다. 이와같잉 허브는 특정 컴퓨터를 지정하여 데이터를 보낼 수 없다고 하여 &lt;code class=&quot;language-text&quot;&gt;더미 허브(Dummy Hub)&lt;/code&gt; 라고도 부른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;1계층물리-계층-설명-요약&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1%EA%B3%84%EC%B8%B5%EB%AC%BC%EB%A6%AC-%EA%B3%84%EC%B8%B5-%EC%84%A4%EB%AA%85-%EC%9A%94%EC%95%BD&quot; aria-label=&quot;1계층물리 계층 설명 요약 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1계층(물리 계층) 설명 요약&lt;/h3&gt;
&lt;p&gt;지금까지 설명한 것을 요약하면, 1계층에서는 랜 카드를 이용해서 데이터를 전기 신호(디지털 신호)로 변환한 후 케이블을 통해 데이터를 전송한다. 또한 1계층에 사용하는 장비로는 리피터와 허브가 있으며, 요즘 리피터는 잘 사용하지 않는다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;데이터-링크-계층data-link-layer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%A7%81%ED%81%AC-%EA%B3%84%EC%B8%B5data-link-layer&quot; aria-label=&quot;데이터 링크 계층data link layer permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터 링크 계층(Data Link Layer)&lt;/h2&gt;
&lt;p&gt;데이터 링크 계층은 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; &lt;strong&gt;데이터를 작은 프레임 단위로 분할하고, MAC 주소를 활용하여 장비를 식별&lt;/strong&gt;한다.&lt;/p&gt;
&lt;p&gt;또한 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 오류를 탐지하고 수정하는 역할을 하며, 데이터를 원활한 흐름과 네트워크 매체에서 충돌 관리도 수행한다. 네트워크에서 여러 기기가 동시에 데이터를 전송하려고 하면 신호 충돌이 발생할 것이다. 이 상황에서 데이터를 동시에 전송하면 여러 데이터가 충돌하게 되어 제대로 전달되지 않는데, 그래서 이러한 오류 탐지 및 수정 역할을 수행하는 것이다.&lt;/p&gt;
&lt;p&gt;오류를 감지하거나 수정하는 방법에는 크게 3가지가 있는데, &lt;code class=&quot;language-text&quot;&gt;회선 제어&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;오류 제어&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;흐름 제어&lt;/code&gt; 에 대해 알아보자.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;회선-제어&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%9A%8C%EC%84%A0-%EC%A0%9C%EC%96%B4&quot; aria-label=&quot;회선 제어 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;회선 제어&lt;/h3&gt;
&lt;p&gt;회선 제어는 &lt;strong&gt;오류를 감지하기 보다는 &lt;code class=&quot;language-text&quot;&gt;회피&lt;/code&gt; 하는 방법으로, 신호 간 충돌 현상이 발생하지 않도록 제어하는 기법이다.&lt;/strong&gt; 그러기위해 신호의 시작을 의미하는 &lt;code class=&quot;language-text&quot;&gt;ENQ(Enquiry)&lt;/code&gt; 와 끝을 의미하는 &lt;code class=&quot;language-text&quot;&gt;EOT(End of Transmission)&lt;/code&gt; 를 명시적으로 지정한다. 수신자는 송진자로부터 신호를 받으면 &quot;응! 문제없이 잘 받았어.&quot; 라는 &lt;code class=&quot;language-text&quot;&gt;ACK&lt;/code&gt; 을 송신자에게 보내서 신호를 정상적으로 수신헀음을 알린다. 이러면 신호 간 충돌을 피할 수 있다.&lt;/p&gt;
&lt;p&gt;즉, 송신자와 수신자 사이에선 다음과 같은 플로우로 회선 제어가 적용된다. 송신자 입장에서의 플로우는 다음과 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ENQ 를 전송 -&gt; ACK 받음 -&gt; 데이터 전송 -&gt; ACK 받음 -&gt; EOT 전송&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;오류-제어&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%A4%EB%A5%98-%EC%A0%9C%EC%96%B4&quot; aria-label=&quot;오류 제어 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;오류 제어&lt;/h3&gt;
&lt;p&gt;오류 제어는 송수신 데이터가 외부 간섭, 시간 지연등에 의해 데이터가 변형되거나 순서가 어긋나는 등 통신 장애가 발생하지 않도록 &lt;strong&gt;오류를 검출하고 정정하여 통신에 대한 신뢰성을 확보&lt;/strong&gt;하는 기법이다.&lt;/p&gt;
&lt;h4&gt;패리티 검사&lt;/h4&gt;
&lt;p&gt;오류 검사로 사용되는 것 중에 패리티 검사하는 것이 있다. &lt;code class=&quot;language-text&quot;&gt;패리티 검사(parity check)&lt;/code&gt; 란 수신자에게 보내는 최종 데이터의 1의 개수를 짝수개로 보낼지, 홀수개로 보낼지 송.수신자가 미리 약속하고 &lt;strong&gt;여분의 비트(패리티 비트) 를 채워서 보내는 방법&lt;/strong&gt;이다.&lt;/p&gt;
&lt;p&gt;예를들어 송.수신자가 1을 짝수개로 보내겠다고 미리 약속한다. 그리고 데이터에 1이 4개라면 송신자는 전송하려는 데이터 내부에서 남은 여분의 비트(패리티 비트)에다 0을 추가적으로 첨부해서 보내는 것이다. 수신자는 전달받은 데이터에서 패리티 비트를 조회하여 0이라면 문제가 없다고 판단하게 되고, 반대로 1이 조회된다면 통신간에 문제가 발생헀다고 판단할 수 있다. 이런 방법으로 송.수신에 사용된 데이터가 문제없음을 보장할 수 있다.&lt;/p&gt;
&lt;h4&gt;CRC, 검사 합(Check Sum), 해밍 코드(Hamming Code)&lt;/h4&gt;
&lt;p&gt;패리티 검사 외에도 CRC(Cyclic Redundancy Check) 나 검사합(Check Sum) 등의 기법도 있다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;CRC&lt;/code&gt; 는 데이터에 CRC 코드를 추가하여 오류를 감지하는 방법으로, 송신자가 CRC 코드를 생성하고 수신자 측에서도 동일한 계산을 수행하여 일치 여부를 확인하는 방법이다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;검사 합&lt;/code&gt; 은 데이터의 각 바이트 값을 더하거나 연산하여 생성되는 값이다. 송신자가 데이터와 관련된 검사합을 계산하고 수신자 측에서도 동일한 계산을 수행하여, 두 검사합 값이 일차하면 데이터가 오류없이 전송된 것으로 간주하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;해밍 코드&lt;/code&gt; 는 데이터가 추가적인 패리티 비트 여러개를 포함시킨다. 이 패리티 비트들은 데이터 비트와 조합되어 특별한 방식으로 구성된다. 패리티 비트들을 분석하여 오류가 발생한 비트를 식별하고, 해당 비트의 값을 수정함으로써 오류를 정정한다.&lt;/p&gt;
&lt;h3 id=&quot;흐름-제어&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%9D%90%EB%A6%84-%EC%A0%9C%EC%96%B4&quot; aria-label=&quot;흐름 제어 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;흐름 제어&lt;/h3&gt;
&lt;p&gt;흐름 에저는 송신자와 수신자의 데이터 처리 속도 차이를 해결하기 위해 &lt;strong&gt;수신자 상황에 따라 송신자의 데이터 전송량을 조절하는 기법이다.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;정지-대기(Stop &amp;#x26; Wait)&lt;/h4&gt;
&lt;p&gt;흐름 제어로 사용되는 방법 중 하나가 정지-대기 기법이다. 송신자가 하나의 데이터를 전송한 후 다음 데이터르 전달하기 전에 확인 응답을 기다리는 방법이다.&lt;/p&gt;
&lt;p&gt;예를들어 수신자가 ACK 메시지를 송신자에게 완전히 전달하고 난 뒤에서야 다음 데이터를 천천히 보내는 방법이 정지-대기 기법에 해당한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;이더넷&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%8D%94%EB%84%B7&quot; aria-label=&quot;이더넷 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이더넷&lt;/h3&gt;
&lt;p&gt;이더넷(Ethernet) 이란 &lt;strong&gt;다수의 컴퓨터, 허브, 스위치 등을 하나의 인터넷 케이블에 연결한 네트워크 구조다.&lt;/strong&gt; LAN 과 WAN 에서도 이더넷을 사용한다.&lt;/p&gt;
&lt;h4&gt;CSMA/CD 프로토콜&lt;/h4&gt;
&lt;p&gt;이더넷은 CSMA/CD 라고 하는 프로토콜을 사용한다. 2대 이상의 여러 컴퓨터가 동시에 데이터(프레임) 을 보내면 충돌이 발생할 수 있을 것이다. 이런 충돌 현상을 방지하기 위해 이더넷에서는 &lt;code class=&quot;language-text&quot;&gt;전류의 강도&lt;/code&gt;를 확인해 케이블이 사용 중인지 여부를 확인하는 CSMA/CD 방식을 사용한다. &lt;strong&gt;즉, 송신전에 케이블이 사용 가능한지 확인하고(Carrier Sense), 사용 가능하면 데이터를 전송(Multiple Access)한다.&lt;/strong&gt; 또한 전송 이후에도 여전히 발생할 수 있는 충돌을 수시로 확인한다.&lt;/p&gt;
&lt;p&gt;정리하자면, 이 기법은 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 전류의 강도를 확인하여 송신전에 케이블이 사용 가능한지 확인한다. 그러고 &lt;code class=&quot;language-text&quot;&gt;(2-1)&lt;/code&gt;전류의 강도 세기가 높다면 데이터를 전송하지 않고 강도가 낮아질 떄까지 대기했다가 천천히 데이터를 송신하게 된다. &lt;code class=&quot;language-text&quot;&gt;(2-2)&lt;/code&gt; 반대로 강도가 낮다면 데이터를 바로 송신한다.&lt;/p&gt;
&lt;p&gt;마치 비유를 하자면 CSMA/CD 는 각 컴퓨터끼리 충돌을 방지하기 위해, 데이터를 전송하기 전에 서로 &lt;code class=&quot;language-text&quot;&gt;눈치 게임&lt;/code&gt;을 하고 있는것이다.&lt;/p&gt;
&lt;p&gt;다만 &lt;code class=&quot;language-text&quot;&gt;스위치(Switch)&lt;/code&gt; 장비가 충돌 방지 역할을 대신 해주고 있기 때문에, CSMA/CD 는 현재 잘 사용되지 않는 기법이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;mac-주소&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mac-%EC%A3%BC%EC%86%8C&quot; aria-label=&quot;mac 주소 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MAC 주소&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;MAC(Media Access Control)&lt;/code&gt; 주소는 랜 카드에 할당된 값이며, 전 세계에서 하나밖에 존재하지 않는 고유한 값이다.&lt;/p&gt;
&lt;h4&gt;비트 체계&lt;/h4&gt;
&lt;p&gt;MAC 주소는 48비트로 표현된다. 상위 24비트는 &lt;code class=&quot;language-text&quot;&gt;OUI&lt;/code&gt; 라고 해서 랜 카드 제조사에 부여된 코드이며, 하위 24비트는 &lt;code class=&quot;language-text&quot;&gt;UAA&lt;/code&gt; 라고하여 제조사가 랜 카드에 부여한 고유 번호이다.&lt;/p&gt;
&lt;h4&gt;이더넷 헤더 구성요소&lt;/h4&gt;
&lt;p&gt;발신자 컴퓨터가 데이터를 전송할 때 &lt;strong&gt;2계층에서 MAC 주소를 헤더에 넣어 프레임을 생성&lt;/strong&gt;하게 된다. 이때 헤더가 추가되는 MAC 주소는 송신자의 MAC 주소뿐 아니라 발신자의 MAC 주소도 모두 함께 프레임에 추가된다. 또한 &lt;code class=&quot;language-text&quot;&gt;유형(type)&lt;/code&gt; 이라는 프로토콜 정보도 이더넷 헤더에 추가된다. 유형이란 IPv4, IPv6, ARP 프로토콜 중에서 어떤 프로토콜인지에 대한 정보이다.&lt;/p&gt;
&lt;p&gt;정리하자면, 2계층에서 이더넷 헤더에 추가되는 정보는 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 발신자 MAC 주소, &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 수신자 MAC 주소, &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 프로로콜 유형(type) 이렇게 3가지가 된다.&lt;/p&gt;
&lt;h3 id=&quot;arp-프로토콜이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#arp-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;arp 프로토콜이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ARP 프로토콜이란?&lt;/h3&gt;
&lt;p&gt;이때 ARP(Address Resolution Protocol) 이란 IP 주소와 MAC 주소를 매핑하기 위한 프로토콜이다. &lt;strong&gt;즉, ARP 프로토콜은 IP 주소에 해당하는 MAC 주소를 매핑하기 위한 프로토콜&lt;/strong&gt;이다.&lt;/p&gt;
&lt;p&gt;ARP 프로토콜을 이용해서 MAC 주소를 알아내는 과정을 다음과 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;(1) ARP 요청(브로드캐스팅): 컴퓨터 A는 허브에 묶여있는 모든 컴퓨터에게 192.168.0.3 이라는 IP 주소에 대응하는 MAC 주소를 묻는다. 이렇게 모든 컴퓨터에게 질의하는 것을 &lt;code class=&quot;language-text&quot;&gt;브로드캐스트&lt;/code&gt; 라고 하며, MAC 주소를 묻는 것을 &lt;code class=&quot;language-text&quot;&gt;ARP 요청&lt;/code&gt; 이라고 한다.&lt;/li&gt;
&lt;li&gt;(2) ARP 응답: 그러면 모든 컴퓨터는 자신의 IP 를 192.168.0.3 과 비교한다. 결국 192.168.0.3 IP 를 사용하는 컴퓨터 B가 A의 요청에 응답할텐데, 이때 컴퓨터 B의 MAC 주소를 전달한다. 이를 &lt;code class=&quot;language-text&quot;&gt;ARP 응답&lt;/code&gt; 이라고 한다. 이후 컴퓨터 A는 컴퓨터 B의 IP 와 MAC 주소를 메모리에 저장하는데, 이것을 &lt;code class=&quot;language-text&quot;&gt;ARP 테이블&lt;/code&gt; 이라고 한다. 앞으로 컴퓨터 A가 B와 통신하고자 할 떄는 일일이 물어보는 것이 아니라, 자신의 ARP 테이블을 참조한다.&lt;/li&gt;
&lt;li&gt;(3) 이제 컴퓨터 A는 B의 MAC 주소를 알게 되었으므로 이 2대의 컴퓨터는 통신이 가능한 상태가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;이때 중요한 것은 MAC 주소 확인은 LAN 환경(우리 집이나 학교등에서만) 안에서만 확인 가능하므로, WAN 환경에서의 MAC 주소는 &lt;code class=&quot;language-text&quot;&gt;라우터&lt;/code&gt; 장비를 거쳐야만 확인 가능하다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;스위치switch&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%EC%9C%84%EC%B9%98switch&quot; aria-label=&quot;스위치switch permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스위치(Switch)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/52bf0ecda3578cbae5644a0f8656618c/236d7/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.601226993865026%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB30lEQVR42p1STW/TMBjOb+TAL0FIcKWcuEzisCFgIDpNFVKDOAA3qLQOiYlJHUtpm7ZJmiV2Utex8zkBhwfbJYMDQmKHR7HeV3n8fNgqigLXxWI+R5qmqKoaZVmamfW/JHmeG4I0JdjZOYDdf4+mKcEYgxDi34Ttre15iwIiLzCZzvD0yQvs7x+Akhjj8VesVitYUkrDnOvb/4CQOTjn4Jkwe5Fl6iwRpxxRwrFmHMvlHBcX4ZVyDYtSitHZuRlWvxRoJU1V4NxxEIUhCEngTBeg60yRS9RVaTCdumbXNDXqulZRVLAGgyPcvvUQoy8TMFEpBRkyWcL+cIqbnS7u9o5w4iwxmXlIWAaiFFIm4QU+Ovceodt9i1xyeEu1TxJYw+EQe7uPcfzxEyaLEMcnIywCgjeDU9y4s4sH7z7Dixk2XJoYpLIlZIGZ66LXe4nDw54qKIHv+YiiGJbOh1JirOpFHEXmKXz/donLusSPplKhU4yVwlmQgijb2m6loPPbbNbG7pVlk1fTbDNUg3ahA24bNqWIDEwV5K5SjD1q7J+NHHh+aDLU/2guq23o91e39bdntC2sVmA8V4pd3O/s4fmz15A6Q89TTpPrPexaqQl8H7b9Cv2+bSIK/ABxTPATJhoqN6ePFTgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;image 1&quot;
        title=&quot;&quot;
        src=&quot;/static/52bf0ecda3578cbae5644a0f8656618c/a6d36/image-1.png&quot;
        srcset=&quot;/static/52bf0ecda3578cbae5644a0f8656618c/222b7/image-1.png 163w,
/static/52bf0ecda3578cbae5644a0f8656618c/ff46a/image-1.png 325w,
/static/52bf0ecda3578cbae5644a0f8656618c/a6d36/image-1.png 650w,
/static/52bf0ecda3578cbae5644a0f8656618c/e548f/image-1.png 975w,
/static/52bf0ecda3578cbae5644a0f8656618c/3c492/image-1.png 1300w,
/static/52bf0ecda3578cbae5644a0f8656618c/236d7/image-1.png 1486w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;스위치는 2계층에서 사용되는 장비로, 허브와 유사한 특징을 지니고 있다. 허브의 경우, 해당 허브에게 연결되어 있는 모든 컴퓨터에게 데이터가 전송된다. 반면 스위치는 연결되어 있는 컴퓨터들 중에서 데이터를 받아야하는 특정 컴퓨터에게만 전달할 수 있다.&lt;/p&gt;
&lt;p&gt;정리하자면, 스위치란 &lt;strong&gt;소규모 네트워크 안에서 모든 장치(컴퓨터)를 서로 연결해서 데이터를 쉽게 공유할 수 있도록 하는 장비로, 허브와 달리 특정 장치에만 데이터를 송.수신할 수 있어 효율성이 높다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;스위치에서-mac-테이블-관리하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%EC%9C%84%EC%B9%98%EC%97%90%EC%84%9C-mac-%ED%85%8C%EC%9D%B4%EB%B8%94-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0&quot; aria-label=&quot;스위치에서 mac 테이블 관리하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스위치에서 MAC 테이블 관리하기&lt;/h3&gt;
&lt;p&gt;스위치는 허브처럼 데이터를 공유하는 기능 외에도, &lt;strong&gt;스위치 포트에 연결된 컴퓨터의 MAC 주소를 관리하는 기능&lt;/strong&gt;도 있다. MAC 테이블이란 &quot;1번 포트의 MAC 주소는 무엇이다&quot; 와 같이 스위치에서 각 포트에 대한 MAC 주소를 매핑하는 역할을 한다.&lt;/p&gt;
&lt;p&gt;스위치는 처음부터 모든 컴퓨터의 MAC 주소를 MAC 테이블에 보유하고 있는 것이 아니기 떄문에, MAC 테이블에 필요한 컴퓨터의 MAC 주소를 추가한다. 스위치에 물려있는 모든 컴퓨터에게 데이터를 보내서 MAC 주소를 얻어내고, 그 정보들을 MAC 테이블에 업데이트하는 &lt;code class=&quot;language-text&quot;&gt;플러딩(Flooding)&lt;/code&gt; 과정이 발생한다. 또한 업데이트된 MAC 테이블을 정보를 기반으로, 스위치에 연결된 특정 컴퓨터에게만 데이터를 송.수신하는 &lt;code class=&quot;language-text&quot;&gt;MAC 주소 필터링&lt;/code&gt; 과정이 발생한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;단방향-통신과-앙방향-통신&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EB%B0%A9%ED%96%A5-%ED%86%B5%EC%8B%A0%EA%B3%BC-%EC%95%99%EB%B0%A9%ED%96%A5-%ED%86%B5%EC%8B%A0&quot; aria-label=&quot;단방향 통신과 앙방향 통신 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단방향 통신과 앙방향 통신&lt;/h3&gt;
&lt;h5&gt;단방향 통신(Simplex Transmission)&lt;/h5&gt;
&lt;p&gt;앞서 말헀듯이 여러대의 컴퓨터가 동시에 데이터를 보내면 충돌이 발생할 수 있다. 충돌이 발생하는 이유는 컴퓨터간에 단방향 통신을 하기때문이다. &lt;code class=&quot;language-text&quot;&gt;단방향 통신(Simplex Transmission)&lt;/code&gt; 은 선로(채널)가 하나만 있기 떄문에 일방 통행만 가능하다.&lt;/p&gt;
&lt;h5&gt;앙방향 통신(Duplex Transmission)&lt;/h5&gt;
&lt;p&gt;단방향 통신은 생각해보면 한 번애 여러개의 데이터를 주고받지 못하기 때문에 비효율적이다. 그래서 등장한 통신방식이 양방향 통신으로, 하나의 통신 채널에서 송수신이 모두 가능한 방시기다. 이에는 &lt;code class=&quot;language-text&quot;&gt;반이중 방식&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;전이중 방식&lt;/code&gt; 2가지 종류가 있다.&lt;/p&gt;
&lt;h4&gt;반이중 방식(Half Duplex)&lt;/h4&gt;
&lt;p&gt;반이중 방식은 양쪽 방향에서 통신이 가능하지만 동시에 통신할 수는 없다. 한쪽에서 데이터를 보내면 다른 쪽에서는 수신만 가능하다. 하지만 아쉽게도 반이중 방식도 충돌 현상이 여전히 발생한다. 채널을 여전히 1개만 사용하기 떄문에 충돌이 발생할 수 밖에 없다.&lt;/p&gt;
&lt;h4&gt;전이중 방식(Full Duplex)&lt;/h4&gt;
&lt;p&gt;반이중 방식의 충돌 현상을 방지하고자 등장한 방식이다. 이 방식은 &lt;strong&gt;채널(선로) 를 2개 두어서 양쪽 방향에서 동시에 데이터를 주고받을 수 있는 방식이며, 스위치에서 사용된다.&lt;/strong&gt; 채널을 2개나 사용하므로 얖선 충돌 문제를 해결할 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;다음에는 OSI 3~5계층에 대해 다루고자 한다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[OSI 7계층의 네트워크, 전송, 애플리케이션 계층 (3,4,7 계층)]]></title><description><![CDATA[OSI 에 대한 전반적인 개요는 OSI 7계층, TCP/IP 프로토콜의 기본 규약 정리 를 참고하자. 네트워크 계층(Network Layer) 라우터 가령 학교A 에서 학교B 로 이메일을 보내야하는 상황을 가정해보자. 이 상황에선 이전에 다루었던…]]></description><link>https://haon.site/haon/network/network-transport-application/</link><guid isPermaLink="false">https://haon.site/haon/network/network-transport-application/</guid><pubDate>Sun, 03 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;OSI 에 대한 전반적인 개요는 &lt;a href=&quot;https://haon.blog/haon/network/intro/&quot;&gt;OSI 7계층, TCP/IP 프로토콜의 기본 규약 정리&lt;/a&gt; 를 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;네트워크-계층network-layer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B3%84%EC%B8%B5network-layer&quot; aria-label=&quot;네트워크 계층network layer permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;네트워크 계층(Network Layer)&lt;/h2&gt;
&lt;h3 id=&quot;라우터&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9D%BC%EC%9A%B0%ED%84%B0&quot; aria-label=&quot;라우터 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;라우터&lt;/h3&gt;
&lt;p&gt;가령 학교A 에서 학교B 로 이메일을 보내야하는 상황을 가정해보자. 이 상황에선 이전에 다루었던 2계층 장비인 스위치로는 전송이 불가능하다. 스위치로 전송할 경우, 연결되어 있는 컴퓨터들중에 어떤 컴퓨터로 보내야할지 MAC 주소를 물어야한다. 하지만 어떤 MAC 주소로 보내야할지 알아내는 것은 같은 스위치로 연결된 경우에만 가능하다. 정리하자면, 스위치는 연결되어 있는 컴퓨터들중에 특정 컴퓨터에게만 MAC 주소를 알아내고 데이터를 전송할 수 있다는 특징이 있었기 떄문에, 이와 같이 연결되지 않은 네트워크 상의 지역끼리는 통신이 불가능하다.&lt;/p&gt;
&lt;p&gt;그래서 필요한 것이 3계층 장비인 &lt;code class=&quot;language-text&quot;&gt;라우터&lt;/code&gt;이다. &lt;strong&gt;2계층 장비인 스위치가 LAN 단위의 근거리 통신을 담당할 수 있다면, 3계층 장비인 라우터는 WAN 단위의 광범위 네트워크 통신을 담당한다.&lt;/strong&gt; 라우터는 데이터가 어떤 경로로 전달되어야 하는지를 알려주는 내비게이션과 같은 역할을 한다. 어떤 길이 가장 빠른지 결정하는 것이다. 그리고 어떤 길이 가장 빠른 길인지 찾는 과정을 &lt;code class=&quot;language-text&quot;&gt;라우팅(Routing)&lt;/code&gt; 이라고 한다. 또한 라우터는 스위치와는 다르게 IP 주소를 이용해서 통신을 한다.&lt;/p&gt;
&lt;p&gt;OSI 7계층의 네트워크 계층에서 추가되는 헤더 정보는 IP 주소이다. 네트워크 계층에서 헤더가 추가될때 다양한 정보가 추가되지만, 우리가 가장 주목해서 봐야할 부분은 &lt;strong&gt;송신자와 수신자의 IP 주소&lt;/strong&gt;이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;ip-주소&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ip-%EC%A3%BC%EC%86%8C&quot; aria-label=&quot;ip 주소 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;IP 주소&lt;/h3&gt;
&lt;p&gt;LAN 환경에서는 스위치를 이용하여 MAC 주소로 통신한다. 하지만 여기에는 치명적인 단점이 있다. 인터넷이 바로 접속할 수 없고, 우리 회사(근거리 통신망) 이외의 컴퓨터와 통신이 불가능하다. 그래서 필요한 것이 바로 IP 주소이다. &lt;code class=&quot;language-text&quot;&gt;IP(Internet Procotol)&lt;/code&gt; 이란 인터넷상에 있는 컴퓨터의 고유한 주소이다.&lt;/p&gt;
&lt;h4&gt;공인, 사설 IP&lt;/h4&gt;
&lt;p&gt;공인 IP 는 인터넷 서비스 제공자(SKT, KT, ...) 에 의해 전 세계적으로 고유하게 할당되는 IP 주소이다. 이 공인 IP 주소는 주기적으로 변경된다는 특징을 지닌다.&lt;/p&gt;
&lt;p&gt;반면 사설 IP 는 주로 기업 내부에서만 사용되는 IP 주소로, 기업밖에 위치한 컴퓨터와는 통신이 불가능하다.&lt;/p&gt;
&lt;h4&gt;IP 주소 체계&lt;/h4&gt;
&lt;p&gt;IP 주소의 길이는 4바이트, 즉 32 비트로 구성된다. 마지막 1바이트의 가장 작은 수인 0과 작은 큰 수인 255는 용도가 정해져있기 떄문에 컴퓨터의 IP 주소로 사용하지 않는다. 아래와 같이 &lt;strong&gt;마지막 1바이트가 0인 주소를 네트워크 주소&lt;/strong&gt;라고 한다. &lt;code class=&quot;language-text&quot;&gt;네트워크 주소&lt;/code&gt; 는 네트워크를 통칭하기위해 사용되는 주소이다. 또한 &lt;strong&gt;마지막 1바이트가 255인 경우를 브로드케스트 주소&lt;/strong&gt;라고 한다. &lt;code class=&quot;language-text&quot;&gt;브로드케스트 주소&lt;/code&gt;는 네트워크에 연결된 컴퓨터에 데이터를 한 번에 일괄적으로 전송할 때 사용하는 주소다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token number&quot;&gt;192.168&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.0&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// =&gt; 마지막 1바이트인 &quot;0&quot; 이 네트워크 주소임.&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;192.168&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.255&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// =&gt; 마지막 1바이트인 &quot;255&quot; 이 브로드케스트 주소임.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;IP 주소의 구조&lt;/h4&gt;
&lt;p&gt;IP 주소에는 많은 정보가 포함되어 있다. IP 주소는 &lt;code class=&quot;language-text&quot;&gt;네트워크 ID&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;호스트 ID&lt;/code&gt; 로 구분할 수 있다. &lt;code class=&quot;language-text&quot;&gt;호스트 ID&lt;/code&gt; 는 라우터에 연결된 개별 컴퓨터들을 관리하기 위해 사용된다. 즉, 호스트 ID 는 개별 컴퓨터들이 사용하는 것이고, &lt;code class=&quot;language-text&quot;&gt;네트워크 ID&lt;/code&gt; 는 컴퓨터가 많을 경우 관리의 어려움이 있기 떄문에 네트워크의 범위를 지정해(컴퓨터들의 집합) 관리를 쉽게 하기위해 사용한다.&lt;/p&gt;
&lt;p&gt;예를들어 네트워크 A에 포함된 컴퓨터 A, B, C 의 IP 주소가 다음과 같다고 해보자. 이때 컴퓨터 A의 네트워크 ID 와 호스트 ID 는 다음과 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// NetWork A&lt;/span&gt;
컴퓨터 &lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;192.168&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 네트워크 ID: 192.168.1 / 호스트 ID: 2&lt;/span&gt;
컴퓨터 &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;192.168&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.3&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 네트워크 ID: 192.168.1 / 호스트 ID: 3&lt;/span&gt;
컴퓨터 &lt;span class=&quot;token class-name&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;192.168&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.4&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 네트워크 ID: 192.168.1 / 호스트 ID: 4&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;ip-주소의-클래스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ip-%EC%A3%BC%EC%86%8C%EC%9D%98-%ED%81%B4%EB%9E%98%EC%8A%A4&quot; aria-label=&quot;ip 주소의 클래스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;IP 주소의 클래스&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f8694b353fa454df12d6df037d14a5d5/1e093/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.282208588957054%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABsElEQVR42pWTV2/CQBCE+f9/Ja+RIiUPKRIKUQqINISNSzC4UIwxYJs22Vl0KHmJiKXVFfZmv507avv9Hgx+Zm7WRVGgLEsdq6rCKV/trx/jOMZoNNJxmqanCfb7fbIRD5XQeJ4H13WxXC6P1Guhy2Yz3cuyDOPxGDNZbzYbzOdzrFYrbLfbg+BwOEQYj+AHIVKh6HQ+YXW7iKIIeZ6ryHQygW9ZKhTLvtPrgecoZMk+O6A1LF4rihUabw7Orh5x/9TCVz+A47jwfV/EO2AHpEqk9bEIL2SeclwslJJ5LE6flZAUbSvATdPGRf0VVw8fsBxPWvdh2wcSHuQhFZQuUiHKpFUWsm37NyErvXR83LYcXDbecX7XxPWzhXq7B0uSB4OBHpyIWCrCuQhmQjsXEFrElsMwVEElpEdlWYnBW20jkmqTaaqGM2i2qb7b7dS3XPLYIgv1xM8kSfRZKSE3mbRerxXddT0EQSAJpQoxaAsFGLxV0prb7soFMv/ooSFhdYqyEoNkJrhv8n7mcG7Wx2fDSvSC2PqExA8+Zgb3aYP555z0sI06CQ0R52bN8T/fN26imB6HtxsEAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;image&quot;
        title=&quot;&quot;
        src=&quot;/static/f8694b353fa454df12d6df037d14a5d5/a6d36/image.png&quot;
        srcset=&quot;/static/f8694b353fa454df12d6df037d14a5d5/222b7/image.png 163w,
/static/f8694b353fa454df12d6df037d14a5d5/ff46a/image.png 325w,
/static/f8694b353fa454df12d6df037d14a5d5/a6d36/image.png 650w,
/static/f8694b353fa454df12d6df037d14a5d5/e548f/image.png 975w,
/static/f8694b353fa454df12d6df037d14a5d5/3c492/image.png 1300w,
/static/f8694b353fa454df12d6df037d14a5d5/1e093/image.png 1376w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;공인 IP 주소에는 A~E 까지의 5개의 클래스가 있다. 또한 &lt;strong&gt;클래스 A 에서 E 로 내려갈수록 호스트 ID 의 개수가 줄어든다.&lt;/strong&gt; 클래스의 A의 경우 IP 주소의 4바이트 중에서 첫 바이트가 네트워크 ID 를 의미하고, 나머지 3개의 바이트는 호스트 ID 를 뜻한다. 즉, 클래스 A에선 하나의 네트워크가 가질 수 있는 호스트 ID 가 가장 많은 클래스로써, 컴퓨터들에게 할당할 IP 가 가장 많다는 것이된다.&lt;/p&gt;
&lt;p&gt;반면 B 클래스의 경우 A 클래스에서 네트워크 ID 가 하나 줄어든 형태이다. 앞의 2개의 바이트가 네트워크 ID 를 나타낸다. 이와 같은 동일한 원리로 C 클래스는 B 클래스에서 네트워크 ID 가 하나씩 줄어든다.&lt;/p&gt;
&lt;p&gt;또한 각 클래스의 맨 앞 비트들은 각각 A, B, C 순으로 &quot;0, &quot;10&quot;, &quot;110&quot;, ... 의 순으로 고정되어 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;브로트캐스트-유니캐스트-멀티캐스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%8C%EB%A1%9C%ED%8A%B8%EC%BA%90%EC%8A%A4%ED%8A%B8-%EC%9C%A0%EB%8B%88%EC%BA%90%EC%8A%A4%ED%8A%B8-%EB%A9%80%ED%8B%B0%EC%BA%90%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;브로트캐스트 유니캐스트 멀티캐스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;브로트캐스트, 유니캐스트, 멀티캐스트&lt;/h3&gt;
&lt;p&gt;네트워크에서 데이터를 주고받는 방식에는 브로드캐스트, 유니캐스트, 멀티캐스트가 있다. 이들에 대해서도 정리해보자.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5c9b8c07181cf0c94aea1527868bfd9a/82c1e/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.828220858895705%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABMUlEQVR42pWSi26DMAxF+/8ft3UvdSpUXbVSKAMCIQlJINyZ0DJ1mkaxZDkgx/G59gq/TJkWUrfoXY+uc3D9GAevpfHf/9nqeugviYfdAeEmhG07CGWQFSWEbGBsh3VwhtJ2zJ8riJ/Ko1/MGEPddXDOITwWEI2d0mY6HGOa14jPjJA7qEaDlRwVryGExMPmiKKSN0SzyOnxhGi3R6PHYlGcEjaDUg1egsQXrEqGvCh89/PIN/Q9pJRoqFOtDR43n4iSnP4J0lV6KRYXtNb6OPhrGCNj9X3IV5N6mK6l11uarIUk1IJVXsf1e4SSq2UaDmsTvG0JUdNl4deG18Jr+rQ9eQ2N0V7T+5B7N/rFHF26PvYcxEi+GBgNpKq4l2ORhsPucc6nTrYfKc5ZORH9hf0NLvFeUl7ic5IAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;image 1&quot;
        title=&quot;&quot;
        src=&quot;/static/5c9b8c07181cf0c94aea1527868bfd9a/a6d36/image-1.png&quot;
        srcset=&quot;/static/5c9b8c07181cf0c94aea1527868bfd9a/222b7/image-1.png 163w,
/static/5c9b8c07181cf0c94aea1527868bfd9a/ff46a/image-1.png 325w,
/static/5c9b8c07181cf0c94aea1527868bfd9a/a6d36/image-1.png 650w,
/static/5c9b8c07181cf0c94aea1527868bfd9a/e548f/image-1.png 975w,
/static/5c9b8c07181cf0c94aea1527868bfd9a/3c492/image-1.png 1300w,
/static/5c9b8c07181cf0c94aea1527868bfd9a/82c1e/image-1.png 1398w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;브로드캐스트 (BroadCast)&lt;/h4&gt;
&lt;p&gt;브로드캐스트는 같은 네트워크에 속한 모든 컴퓨터에게 데이터를 전달하는 방식이다. 허브와 비슷한 방식이다. 그런데 수신자 입장에선 내가 원하지 않더라도 일방적으로 배느는 정보를 받아야한다. 따라서 데이터를 수신하는 컴퓨터는 그 정보가 필요한지 판단해야하는 번거로움이 있다.&lt;/p&gt;
&lt;h5&gt;유니캐스트 (Unicast)&lt;/h5&gt;
&lt;p&gt;반면 유니캐스트는 &lt;code class=&quot;language-text&quot;&gt;1:1 통신&lt;/code&gt; 이다. 같은 네트워크에 여러대의 컴퓨터가 있을 때 특정한 1대의 컴퓨터에만 데이터를 보내고자 할때 사용한다. 이때 유니케스트로 통신하기 위해선 당연하게도 상대방의 MAC 주소를 알아야만 통신이 가능하다.&lt;/p&gt;
&lt;h4&gt;멀티케스트 (MultiCast)&lt;/h4&gt;
&lt;p&gt;멀티케스트는 브로드캐스트와 유니캐스트를 섞어놓은 방식이다. 같은 네트워크에서 데이터를 보내고 싶은 컴퓨터가 1대라면 상대방의 MAC 주소를 확인하는 것은 어렵지않다. 하지만 그 대상이 2대 이상이라면 꽤 번거롭다. 이떄 사용하는 것이 멀티캐스트다. 멀티캐스트란 특정 그룹을 지정해서 그 그룹에 해당하는 컴퓨터에게만 데이텅를 보내는 방식이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;서브넷-서브넷-마스크&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B8%8C%EB%84%B7-%EC%84%9C%EB%B8%8C%EB%84%B7-%EB%A7%88%EC%8A%A4%ED%81%AC&quot; aria-label=&quot;서브넷 서브넷 마스크 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서브넷, 서브넷 마스크&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;서브넷&lt;/code&gt;이란 하나의 네트워크 주소를 작은 단위로 쪼개서 사용하는 것이다. 또한 서브넷과 서브넷 마스크는 앞서 살펴봤던 &lt;code class=&quot;language-text&quot;&gt;네트워크 ID&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;호스트 ID&lt;/code&gt; 를 이용한다.&lt;/p&gt;
&lt;p&gt;그런데 A 클래스, B 클래스, C 클래스를 각각 하나의 네트워크로 사용하는 것은 비효율적인 방법이다. 하나의 네트워크에 속한 모든 컴퓨터는 브로드캐스트 주소로 오는 데이터를 모두 수신해야하고, 그것이 내가 필요한 정보인지 필터링해야한다. 그래서 하나의 네트워크를 좀 더 효율적인 방법으로 사용해보고자 등장한 개념이 바로 &lt;code class=&quot;language-text&quot;&gt;서브넷&lt;/code&gt;이다. &lt;strong&gt;서브넷은 하나의 네트워크가 분할된 작은 네트워크다. 이때 작은 네트워크로 나누는 작업을&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;서브넷팅(Subneting)&lt;/code&gt; &lt;strong&gt;이라고 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;서브넷팅의 원리&lt;/h4&gt;
&lt;p&gt;서브넷팅의 원리는 간단한다. IP 주소에는 네트워크 ID 과 호스트 ID 가 있을텐데, 네트워크 ID 는 그대로두고 호스트 ID 를 쪼개는 방식이다. 이때 호스트 ID 를 쪼개는 개수가 적을수록 사용 가능한 호스트(컴퓨터) 주소가 많아진다.&lt;/p&gt;
&lt;p&gt;서브넷팅을 네트워크 맥락에서 표현하면 다음과 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;호스트 ID 를 서브넷 ID 와 호스트 ID 로 나누는 작업&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;즉, 호스트 ID 를 서브넷 ID 와 호스트 ID 로 분리하는 작업이 서브넷팅이다.&lt;/p&gt;
&lt;h4&gt;서브넷 마스크&lt;/h4&gt;
&lt;p&gt;IP 주소를 서브넷팅하면 네트워크 ID 와 호스트 ID 의 경계를 판별하기가 어려울 때가 많은데, 이때 서브넷 마스크를 사용한다. 즉, &lt;code class=&quot;language-text&quot;&gt;서브넷 마스크&lt;/code&gt;&lt;strong&gt;란 IP 주소에서 네트워크 ID 와 호스트 ID 를 구분하기 위한 구분자이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;다음 2가지 표현을 보자. 아래에서 &quot;/24&quot; 와 &quot;255.255.255.0&quot; 은 표현만 다를 뿐 모두 같은 의미의 서브넷 마스크에 대한 표현이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token number&quot;&gt;192.168&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;255.255&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.255&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그럼 위와 같은 &quot;/24&quot; 혹은 &quot;255.255.255.0&quot; 은 어떻게 얻을 수 있을까? 서브넷 마스크는 1과 0의 조합으로 구성되긴 하지만 아래처럼 1이 연속적으로 나온 이후 0이 연속적으로 나오는 형태를 취한다. 또한, 서브넷 마스크를 구하기 위해서는 네트워크 ID 는 모두 1로 채우고 호스트 ID 는 0으로 채운다.&lt;/p&gt;
&lt;p&gt;예를들어 아래 그림과 같이 C 클래스의 경우 24비트가 네트워크 ID 이다. 따라서 24비트 1의 값을, 나머지 8비트에 해당하는 호스트 ID는 0으로 채워주면 다음과 같이 255.255.255.0 의 값이 나온다. 그리고 &quot;/24&quot; 가 의미하는 것은 네트워크 ID 의 비트 수이다. 24비트가 모두 1의 값을 가지기 떄문에 &quot;/24&quot; 라고 표현하는 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// C 클래스의 서브넷 마스크&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;진수&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11111111&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11111111&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11111111&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;00000000&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// =&gt; 네트워크 ID 비트 수가 24개이다.&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;진수&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;     &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;      &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;      &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;라우터의-동작-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9D%BC%EC%9A%B0%ED%84%B0%EC%9D%98-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;라우터의 동작 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;라우터의 동작 방식&lt;/h3&gt;
&lt;p&gt;앞서 간단히 살펴봤듯이, 라우터는 이름 그대로 &lt;strong&gt;데이터의 전송을 위해 가장 빠른 길을 찾아 네트워크 간의 경로(route) 를 설정하는 장비&lt;/strong&gt;이다.&lt;/p&gt;
&lt;p&gt;2계층의 장비인 스위치는 같은 네트워크 (LAN 범위) 내에서만 통신이 가능한 장비라고 했다. 즉, 서로 다른 네트워크 간의 통신은 불가능했다. 그래서 &lt;strong&gt;서로 다른 네트워크 간의 통신을 위해서는 라우터를 사용해야 한다.&lt;/strong&gt; 예를들어 &quot;172.16.1.0/24&quot; 네트워크와 &quot;172.16.2.0/24&quot; 네트워크는 서로 다른 네트워크이다. 이 둘 간에 통신이 가능하려면 라우터가 필요하다.&lt;/p&gt;
&lt;h5&gt;라우팅&lt;/h5&gt;
&lt;p&gt;그런데 서로 다른 네트워크 간에는 1개 이상의 라우터가 존재할 수 있다. 이러한 경우, 어떤 경로로 이동해야 빠른 통신이 가능한지 확인해야한다. 즉, &lt;strong&gt;네트워크와 네트워크 간의 데이터 이동시 가장 빠른 경로(route) 를 설정해야 하는데, 이를 라우팅&lt;/strong&gt;이라고 한다. 다시 말해서, 라우팅은 한 네트워크에서 다른 네트워크로 데이터를 전달하는 과정이다.&lt;/p&gt;
&lt;h4&gt;라우팅 테이블&lt;/h4&gt;
&lt;p&gt;그런데 라우팅시 경로는 어떻게 설정할까? 스위치에서 MAC 테이블을 이용했던 것과 같이 라우터에서는 라우팅 테이블이라는 것을 이용한다. &lt;strong&gt;라우팅 테이블은 데이터를 목적지까지 보내기 위한 거리과 방법등을 명시한 테이블&lt;/strong&gt;이다.&lt;/p&gt;
&lt;p&gt;라우팅 테이블에는 데이터를 목적지로 전달하기 위해 어떤 방향(인터페이스라고도 부름)으로 가야 하는지에 대한 정보를 가지고 있다. 따라서 라우터가 데이터를 목적지로 보내려고 하면 라우팅 테이블을 찾아보게 된다.&lt;/p&gt;
&lt;p&gt;예를들어 컴퓨터 A가 컴퓨터 B에게 데이텅를 보내려는 간단한 상황을 가정해보자. 먼저 라우터 A는 컴퓨터 B의 네트워크 주소를 확인한다. 그러고 컴퓨터 B로 가는길이 정의되어있는지 라우팅 테이블을 검색한다. 그랬더니 라우팅 테이블에 컴퓨터 B 네트워크로 가려면 E1 경로로 가라는 내용이 있다면, 이제 라우터 A는 라우터 B에게 컴퓨터 B의 IP 주소가 무엇인지 물어보기만 하면된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;전송-계층-transport-layer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%EC%86%A1-%EA%B3%84%EC%B8%B5-transport-layer&quot; aria-label=&quot;전송 계층 transport layer permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전송 계층 (Transport Layer)&lt;/h2&gt;
&lt;p&gt;전송 계층은 기본적으로 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; &lt;strong&gt;오류를 점검하는 기능(즉 데이터를 목적지에 문제없이 전달하는 기능)&lt;/strong&gt; 과 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; &lt;strong&gt;컴퓨터가 제대로 데이터를 받았을 경우 어떤 애플리케이션으로 전달해야 하는지를 식별하는 기능&lt;/strong&gt;을 담당한다. 이전에 2계층에서 살펴봤던 오류 점검 방식이 현재 전송 계층에도 존재한다.&lt;/p&gt;
&lt;h3 id=&quot;혼잡-제어&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%98%BC%EC%9E%A1-%EC%A0%9C%EC%96%B4&quot; aria-label=&quot;혼잡 제어 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;혼잡 제어&lt;/h3&gt;
&lt;p&gt;혼잡 제어(Congestion Control) 은 &lt;strong&gt;네트워크로 들어가는 정보량을 조절하여 네트워크가 혼잡해지지 않게 조절하는 방법&lt;/strong&gt;이다. 이를위해 &lt;strong&gt;송신자는 먼저 하나의 데이터만 보내고 수신자 측에서 ACK 가 오면 전송량을 2배씩 증가시켜 나간다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;그러다가 타임아웃이 발생하면 여러개 보냈던 데이터를 줄여서 보낸다. 또는 동일한 ACK 을 여러번 받는 경우에도 데이터를 줄여서 보내는 방식을 취한다. (타임아웃이나 ACK 을 여러번 받았다는 것은 네트워크가 데이터를 실어 나르느라 바쁘다는 뜻이니) 이후 정상적인 ACK 을 받게되면 또 다시 데이터를 배수로 전송함으로써 데이터 전송량을 조절한다.&lt;/p&gt;
&lt;h3 id=&quot;흐름-제어&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%9D%90%EB%A6%84-%EC%A0%9C%EC%96%B4&quot; aria-label=&quot;흐름 제어 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;흐름 제어&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/994f9503640aa278ebdb4b3bf358de8e/c655d/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.05521472392638%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAzUlEQVR42pWSyQqAMAxE/f+P9OqO+76MvEDAg4INBKs1rzNpIr3EcRyK41h9379tK0kSpWn6uhfpI+Z51rqulqyHYdC+7zrPU8uyWAYBAVBMTtOktm3VdZ3Bq6rSOI7233Vd/4B1XZsK7AMqikJlWdretm2WQQpR4LYp5umKOCgYCIxiLAPjANrAumkaa0OQZYqelrMsU57nBnDlQQqBoMpvlDVqCW9DsGW/ZYp5xybwp+XfQMaEucMyN84wY9nHKNiyD7I3HTBJ8P3L8g2shGNkzaKqyAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;image 3&quot;
        title=&quot;&quot;
        src=&quot;/static/994f9503640aa278ebdb4b3bf358de8e/a6d36/image-3.png&quot;
        srcset=&quot;/static/994f9503640aa278ebdb4b3bf358de8e/222b7/image-3.png 163w,
/static/994f9503640aa278ebdb4b3bf358de8e/ff46a/image-3.png 325w,
/static/994f9503640aa278ebdb4b3bf358de8e/a6d36/image-3.png 650w,
/static/994f9503640aa278ebdb4b3bf358de8e/e548f/image-3.png 975w,
/static/994f9503640aa278ebdb4b3bf358de8e/3c492/image-3.png 1300w,
/static/994f9503640aa278ebdb4b3bf358de8e/c655d/image-3.png 1586w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;흐름 제어는 데이터 링크 계층에서 사용했던 &lt;code class=&quot;language-text&quot;&gt;정지-대기(Stop &amp;amp; Wait)&lt;/code&gt; 방식과 완전히 동일하다. 정지-대기 방식은 송신자가 하나의 데이터를 전송한 후 다음 데이터를 전달하기 전에 확인 응답을 기다리는 방법이다. 예를들어 수신자 측에서 ACK 이라는 메시지가 송신자 측에 전달되어야만 다음 데이터를 보내는 방식이다.&lt;/p&gt;
&lt;h3 id=&quot;오류-제어&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%A4%EB%A5%98-%EC%A0%9C%EC%96%B4&quot; aria-label=&quot;오류 제어 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;오류 제어&lt;/h3&gt;
&lt;p&gt;오류 제어도 마찬가지로 데이터 링크 계층의 오류 제어 방식과 동일하다. 하지만 방법은 조금 다르다. 오류를 검출하기 위해서는 &lt;code class=&quot;language-text&quot;&gt;확인 응답&lt;/code&gt;과 &lt;code class=&quot;language-text&quot;&gt;시간 초과 방법&lt;/code&gt; 을 사용한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;(1) 확인 응답: 수신자 측으로부터 ACK 이라는 응답을 받아야하지만 ACK 이 없다면 오류로 판단한다.&lt;/li&gt;
&lt;li&gt;(2) 시간 초과 방법: 특정 시간 내에 ACK 이 없으면 세그먼트에 오류가 있다고 판단한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-way-handshake&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-way-handshake&quot; aria-label=&quot;3 way handshake permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3-way handshake&lt;/h3&gt;
&lt;p&gt;전송 계층은 데이터를 목적지에 문제없이 전달하는 것을 목표로 한다. 이떄 데이터를 전달하는 목적에 따라 연결형과 비연결형 통신으로 나뉜다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;연결형 통신: 데이터를 정확하게 전달하는 것을 목표로 하는 통신. TCP 프로토콜을 사용한다.&lt;/li&gt;
&lt;li&gt;비연결형 통신: 효율적으로 데이터를 보내는 통신. UDP 프로토콜을 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;TCP 프로토콜은 3-way handshake 방법을 통신을 시작한다. TCP 프로토콜이 데이터를 전달하는 것을 목표로하기 때문에 상대방이 내 신호를 받을 수 있는 상태인지 확인하고 전송하는 방식이다. 즉, 3-way handshake 는 TCP 통신을 하는 장치(컴퓨터)간에 서로 통신할 준비가 되었는지를 확인하는 과정이다.&lt;/p&gt;
&lt;p&gt;3-way handshake 의 과정은 다음과 같은 수행된다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/74bdfd89fe2809a402d326c03b83c1b3/891d5/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.576687116564415%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAABmElEQVR42n2T227jIBCG+6h50e5171aqlN42aQ4+YhswxsbAv8Okzkap3ZHGSMB8/HPwC55sdg6H4xHjOP7fjJGXQSs01zOC99iyl3vM97eeZrwLhWqc7/vhdog/lcXubw01h4eYX4GA8hFvKqDz8QkY8Cocdh8G2gdWHeNvQDotihJlUSAXEs5NmOeZVsfeNg2MHTHyQ/EeswqM/FrkwIlcWYdOKrRti77v2RshUNYNrqWAqCvkeQ5r7bbCR6WWylcTIM8zeGoAK50mtFLjQsA8u+J0OqMsyx8qGSg4OOcLVUWviw5VXdN+Da01u1IS2lj0k+cuz+R+pdsMDCFgGAaGJmCjLY/NRKrSWbJUQ6kNrP+Z0aPKe8opMNUkQdp+ZIUNQZRSkFLicjlDdBKNGmAHA2PMtsJn60ePnLq9BN1URlSixfFS4IsG//Nw4KalB1NJ1oHf0lPKX6cTOgooCHxTKqGohp0ZqesaBwLu93tkWcYZLGmvAmtJw0ujkkqw1DCZoxk0bvsv2UzZh/XraTudLY149AX4D1Tm9qscnL/ZAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;image 4&quot;
        title=&quot;&quot;
        src=&quot;/static/74bdfd89fe2809a402d326c03b83c1b3/a6d36/image-4.png&quot;
        srcset=&quot;/static/74bdfd89fe2809a402d326c03b83c1b3/222b7/image-4.png 163w,
/static/74bdfd89fe2809a402d326c03b83c1b3/ff46a/image-4.png 325w,
/static/74bdfd89fe2809a402d326c03b83c1b3/a6d36/image-4.png 650w,
/static/74bdfd89fe2809a402d326c03b83c1b3/e548f/image-4.png 975w,
/static/74bdfd89fe2809a402d326c03b83c1b3/3c492/image-4.png 1300w,
/static/74bdfd89fe2809a402d326c03b83c1b3/891d5/image-4.png 1520w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 송신자는 연결 요청을 위해 임의의 SYN 번호를 수신자에게 보낸다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 수신자는 요청을 잘 받았다는 의미로 송신자에게 받은 SYN 번호에 1을 더해 ACK 을 보낸다. 또한, 수신자도 연결을 요청하는 의미로 SYN 에 임의의 번호를 보낸다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 송신자는 연결이 수립되었다는 의미로 수신자로부터 받은 SYN 에 1을 더해서 수신자에게 ACK 을 보낸다. 그럼 이제 통신을 위한 연결이 수립된 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위와 같은 3 way handshake 를 이용하면 송신자와 수신자 양츩 모두 데이터를 전송할 준비가 되었다는 것을 보장한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;tcp-의-구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tcp-%EC%9D%98-%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;tcp 의 구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TCP 의 구조&lt;/h3&gt;
&lt;p&gt;전송 계층에선 헤더 정보에서 포트 번호를 포함한 다양한 정보들이 포함된다. 각 정보들을 상세하게 살펴보자.&lt;/p&gt;
&lt;h4&gt;1. 포트 번호&lt;/h4&gt;
&lt;p&gt;가장 중요한 포트번호이다. 전송 계층은 어떤 애플리케이션과 통신하는지 정의하는 곳이라고 했다. 그 기능을 하는것이 포트 번호이다.
포트 번호의 각 카테고리는 다음과 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;0 ~ 1023번: &lt;code class=&quot;language-text&quot;&gt;잘 알려진 포트(well-known port)&lt;/code&gt; 로, 말 그대로 특정한 쓰임을 위해 사용된다.&lt;/li&gt;
&lt;li&gt;1024 ~ 49151번: 기관이나 기업들이 사용하는 포트로, &lt;code class=&quot;language-text&quot;&gt;사전 등록된 포트(registered port)&lt;/code&gt; 라고도 한다.&lt;/li&gt;
&lt;li&gt;49152 ~ 65535번: 일반 사용자들이 자유롭게 사용할 수 있는 포트로, &lt;code class=&quot;language-text&quot;&gt;다이나믹 포트(dynamic port)&lt;/code&gt; 라고도 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h4&gt;2. 일련번호와 확인 응답 번호&lt;/h4&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c99921b05bf436ba1489320908414d4e/985a9/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.576687116564415%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACR0lEQVR42oWT6W7aUBCF8/5PkFdI/kYqaZUfaUILhB1jVpOw2Riz2cY23r/6Og1NG7U90tXoLnNmuWcu+At8z2Gra+zWK/aGjmObrxcp/8TFnwdpFEDo4WoKw/YTxnTIoFlhIZXz8yT083dRFOG6Lp7n5UvsPxKmKYmzIzmoHGcSL8MuB23Ky6DLulfJzznu8NOAnW+z3+7YbreoqoplWa+EQRDkkY7H4zmKwCYrV+q06MsS9acKw8wKOIT0ti80Ok26kkR/0EdwnEsulUqUy2Vq1SrtToco69HxFDFcWdw/dah0Fb4Ua3yuyYydLZvY5XkyofCpwH6/p16vM5/PfxaYcnF7e4ssy2iayuNjkY2XMjFcZPWIqq042A4rfc29MkE2TJwsmelsxt3dHcViEZGQYRi/CBVF4erqiuvr67wXb5ivDKqdHs3eiLo0QFYW57swDHOyy8vLPMPffnk0GlEoFLi5uWG5XIJnEu+XONM23eo3jOcenfIDy+ZXUlMjsQw810H4jcfj3IrMzoQimq5nOnMc4jiGJCYNMimoY9bPA1xjgab02CstUt8ljcPc8XQ65T7vyc6yEYTip99D9K/WaFGqVCl+L9Nod859EjBNM/d7r4wPOhRP3SDG8mMGuk2xISM9qxTrXe5bE+xTShil/5+Ut6hJZnQ7YLo7IS0tpvMF+i6zsyUPownlbAz71gY9OmAmmXazXopMbdvOJeT7/sfRe4MTJBhOyNaNXpcXZhUkIixhGmFlhJZrszE2+bSI8sUI/gC07NNlZ56kzgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;image 5&quot;
        title=&quot;&quot;
        src=&quot;/static/c99921b05bf436ba1489320908414d4e/a6d36/image-5.png&quot;
        srcset=&quot;/static/c99921b05bf436ba1489320908414d4e/222b7/image-5.png 163w,
/static/c99921b05bf436ba1489320908414d4e/ff46a/image-5.png 325w,
/static/c99921b05bf436ba1489320908414d4e/a6d36/image-5.png 650w,
/static/c99921b05bf436ba1489320908414d4e/e548f/image-5.png 975w,
/static/c99921b05bf436ba1489320908414d4e/3c492/image-5.png 1300w,
/static/c99921b05bf436ba1489320908414d4e/985a9/image-5.png 1512w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;포트번호 외에도 일련번호, 확인 응답 번호가 헤더에 추가된다. &lt;code class=&quot;language-text&quot;&gt;일련번호&lt;/code&gt; 는 &lt;strong&gt;송신자가 수신자에게 보내려는 데이터가 몇 번째인지&lt;/strong&gt; 알려주는 것이고, 반대로 &lt;code class=&quot;language-text&quot;&gt;확인 응답 번호&lt;/code&gt; 는 &lt;strong&gt;수신자가 몇 번째 데이터를 받았는지 송신자에게 알려주는 역할&lt;/strong&gt;을 한다. 이 둘을 이용해서 수신한 데이터가 순서는 맞는지, 오류는 없는지 검사할 수 있다.&lt;/p&gt;
&lt;p&gt;예를들어 다음과 같은 송신자가 수신자에게 데이터르 보내는 상황을 가정해보자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;보내야 할 총 데이터 크기 : 1500바이트&lt;/li&gt;
&lt;li&gt;패킷의 크기: 500바이트&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이떄 패킷의 크기란 한번에 보낼 수 있는 데이터의 최대 크기이다. 즉, 한번에 보낼 수 있는 최대 크기가 500 바이트라는 것이다. 따라서 1500바이트의 데이터를 보내기 위해서는 총 3번에 걸쳐 보내야한다. (500바이트씩 3번이면 1500바이트가 되므로) 따라서 일련번호는 이렇다. 처음의 일련번호가 1이라고 가정할 떄 다음의 일련번호는 501, 또 다음은 1001 이다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;확인 응답 번호&lt;/code&gt;는 수신자가 몇번쨰 데이터를 받았는지 송신자에게 알려주는 역할을 한다.&lt;/p&gt;
&lt;h4&gt;3. 윈도우 크기&lt;/h4&gt;
&lt;p&gt;설명에 앞서, 상황을 하나 가정해보자. 수신자가 받을 수 있는 데이터의 양은 적은데, 송신자가 그것을 공려하지 않고 많은 양의 데이터를 보낸다면 어떤 일이 발생할까? 송신자는 모든 데이터를 보냈다고 생각하곘지만, 수신자는 송신자가 보낸 데이터를 받아들이지 못하고 일부는 거절당한다. 그러면 정상적인 통신이 불가능해질 것이다. 그래서 등장한 것이 윈도우 크기다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;윈도우 크기(Window Size) 란 송신자가 한 번에 보낼 수 있는 데이터의 최대 크기이다.&lt;/strong&gt; 즉, 수신자가 얼마나 데이터를 받아들일 수 있는지 확인한 후, 그 크기를 고려해서 데이터를 보내겠다는 의도인 것이다.&lt;/p&gt;
&lt;p&gt;수신자의 윈도우 크기는 3-way handshake 과정에서 알아낼 수 있나. 헨드쉐이크 과정에서 SYN, ACK 외에도 Window Size 라는 정보를 주고받게 도니다. 이렇게 확인된 윈도우 크기를 이용하면 더 효윯적인 데이터 전송이 가능하다. 매번 데이터를 보내고 ACK 을 기다리는 것이 아니라, 수산자가 받을 수 있을만큼 데이터를 한 번에 보내면 된다.&lt;/p&gt;
&lt;h4&gt;4. 코드 비트&lt;/h4&gt;
&lt;p&gt;코드 비트는 헨드쉐이크 과정에서 세부 상태를 표현하낟. 모두 기본값은 0이며, 비트가 활성화되면 1이 된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TURG: 긴급 처리 데이터가 있음&lt;/li&gt;
&lt;li&gt;ACK: 확인 응답 번호 사용&lt;/li&gt;
&lt;li&gt;SYN: 연결을 초기화하기 위해 순서 번호를 동기화&lt;/li&gt;
&lt;li&gt;FIN: 데이터 송신 종료&lt;/li&gt;
&lt;li&gt;... (이하 생략)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;udp-프로토콜&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#udp-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C&quot; aria-label=&quot;udp 프로토콜 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UDP 프로토콜&lt;/h3&gt;
&lt;p&gt;UDP 는 TCP 와 달리 신뢰성이 보장되지 않으며, 데이터 전송 속도가 빠른 프로토콜이다. 데이터를 수신자 측에서 오류없이 받던간에 상관없이 데이터를 보내기만 하면 되며, 브로트캐스트시 사용되는 프로토콜이 UDP 이다.&lt;/p&gt;
&lt;p&gt;UDP 프로토콜에서 사용되는 헤더는 TCP 와 비교했을 때 상당이 축약된 정보만 포함하고 있다. 신뢰성을 보장하지 않기 때문이다. UDP 헤더내에 &lt;code class=&quot;language-text&quot;&gt;송.수신자의 포트번호&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;헤더 길이&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;검사 합(CheckSum)&lt;/code&gt; 정도의 정보만 포함한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;응용-계층&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%91%EC%9A%A9-%EA%B3%84%EC%B8%B5&quot; aria-label=&quot;응용 계층 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;응용 계층&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;응용 계층은 평소 서버 개발시 자주 사용하기에, 이미 잘 알고이는 지식들이 대부분이다. 따라서 다른 계층에 비해 설명은 많이 생략하도록 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;http-프로토콜&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C&quot; aria-label=&quot;http 프로토콜 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP 프로토콜&lt;/h3&gt;
&lt;p&gt;HTTP(HyperText Transfer Protocol) 은 클라이언트와 서버가 어떻게 데이터를 교환할지 정해놓은 규칙으로, 80번 포트를 사용한다.&lt;/p&gt;
&lt;h4&gt;HTTP 버전&lt;/h4&gt;
&lt;p&gt;현재 HTTP 버전은 1.0, 1.1, 2.0 순서로 진화했다. HTTP 프로토콜에는 자체적으로 정의한 헤더가 있어서 HTTP 버전 정보, 상태 정보등 여러 정보를 서버로 보낼 수 있게 되었다. 하지만 매 요청마다 연결을 수립하는 과정을 반복하다 보니, 이것을 처리하는 서버의 성능이 저하되는 문제점이 있었다.&lt;/p&gt;
&lt;p&gt;그래서 등장한 것이 HTTP 1.1 이며 현재 가장 많이 사용하는 버전이다. HTTP 1.1 부터는 &lt;code class=&quot;language-text&quot;&gt;keep-alive&lt;/code&gt; 기능이 추가되어 연결이 1번 수립되면 데이터 교환을 마칠 때까지 연결을 유지하고, 데이터 교환이 끝나면 연결을 끊는 구조이다. 한편 HTTP 2.0 부터는 응용 계층이 &lt;code class=&quot;language-text&quot;&gt;바이너리 프레임(Binary Frame)&lt;/code&gt; 을 추가하여 기존의 일반 텍스트 데이터를 바이너리로 변환한 후 데이터를 분할하여 보낸다. 이렇게 하면 전송 속도가 빨라지고 오류 발생 가능성도 낮아진다.&lt;/p&gt;
&lt;h3 id=&quot;dns-서버&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dns-%EC%84%9C%EB%B2%84&quot; aria-label=&quot;dns 서버 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DNS 서버&lt;/h3&gt;
&lt;p&gt;IP 주소를 도메인 주소로 변경해주는 것이 DNS 서버이다. 사용자가 가령 Google 웹 브라우저에서 랜더링을 요청하며, 해당 요청은 DNS 서버로 전송된다. 만약 이떄 DNS 서버가 google 의 IP 주소를 모른다면 또 다른 DNS 서버에 질의해서 IP 주소를 전달받는다.&lt;/p&gt;
&lt;h4&gt;DHCP 서버&lt;/h4&gt;
&lt;p&gt;DHCP 서버는 IP 주소를 자동으로 관리하기 위해 등장했다. DHCP(Dynamic Host Configuration Protocol) 서버는 사용자들에게 IP 주소를 할당하거나 회수하는 것을 자동으로 해주는 서버다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[OSI 7계층, TCP/IP 프로토콜의 기본 규약 정리]]></title><description><![CDATA[네트워크를 다시금 공부하면서 장기 기억화를 위해 글을 작성한다. OSI 의 각 계층에 대해선 향후 포스트에서 자세히 다루도록 할 것이며, 현 포스트에선 네트워크의 전반 흐름에 대해 오버뷰로 간단히만 다루도록 한다. 랜(LAN) 과 웬(WAN…]]></description><link>https://haon.site/haon/network/intro/</link><guid isPermaLink="false">https://haon.site/haon/network/intro/</guid><pubDate>Fri, 01 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;네트워크를 다시금 공부하면서 장기 기억화를 위해 글을 작성한다. OSI 의 각 계층에 대해선 향후 포스트에서 자세히 다루도록 할 것이며, 현 포스트에선 네트워크의 전반 흐름에 대해 오버뷰로 간단히만 다루도록 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;랜lan-과-웬wan&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9E%9Clan-%EA%B3%BC-%EC%9B%ACwan&quot; aria-label=&quot;랜lan 과 웬wan permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;랜(LAN) 과 웬(WAN)&lt;/h2&gt;
&lt;p&gt;네트워크를 이해하려면 통신 범위의 단위인 LAN 과 WAN 에 대해 선수로 알아야한다. &lt;code class=&quot;language-text&quot;&gt;랜(LAN, Local Area NetWork)&lt;/code&gt; 란 좁은 범위내에 특정 지역을 범위로 하는 네트워크를 뜻하며, &lt;code class=&quot;language-text&quot;&gt;웬(WAN, Wide Area NetWork)&lt;/code&gt; 이란 2개 이상의 LAN 을 연결한 광범위의 네트워크를 뜻한다. 또한 LAN 은 WAN 에 비해 비교적 근거리 통신망 형태로 구축되므로 전송 속도가 매우 빠르다.&lt;/p&gt;
&lt;h3 id=&quot;lan&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lan&quot; aria-label=&quot;lan permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LAN&lt;/h3&gt;
&lt;p&gt;특히 LAN 의 구성방법에 대해 자세히 알 필요가 있다. 랜의 구성 방법, 즉 네트워크 접속 형태는 다음과 같이 3가지 방식으로 구할 수 있다. (3가지 이상의 다양한 방식으로 LAN 을 구성 가능하지만, 이 정도만 알아도 충분할 것으로 보인다.)&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;스타형 (star) : 하나의 허브(Hub) 에 여러대의 컴퓨터를 연결하는 방식&lt;/li&gt;
&lt;li&gt;링형 (ring) : 개별 컴퓨터가 서로 원(circle) 형태로 연결되는 방식&lt;/li&gt;
&lt;li&gt;버스형 (bus) : 하나의 긴 케이블에 컴퓨터를 포함한 모든 주변 장치를 연결하는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;한편 WAN 은 LAN 과 달리 광범위한 영역을 커버해야 하기 때문에 LAN 과 같은 별도의 네트워크 접속 형태가 없다. 이미 LAN 에서 접속 형태를 정의헀으니, 또 다시 네트워크를 구성할 필요가 없기 때문이다. 네트워크 망을 구축시 LAN 사이에 통신이 잘 이루어지도록 연결만 해주면 된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;네트워크-기본-구성장치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%84%B1%EC%9E%A5%EC%B9%98&quot; aria-label=&quot;네트워크 기본 구성장치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;네트워크 기본 구성장치&lt;/h2&gt;
&lt;p&gt;컴퓨터와 컴퓨터간에 통신하기 위해선 다양한 접속 장치가 필요하다. 접속 장치에 대해 자세한 설명은 향후 포스트에서 자세히 다룰것이므로, 여기선 간단히 장치의 종류만 언급하겠다.&lt;/p&gt;
&lt;p&gt;컴퓨터 A, 컴퓨터 B 가 있다고 가정했을 때 이 둘은 서로 통신하기 위해 허브, 스위치, 라우터등의 네트워크 장치가 필요하다. 가령 이 예시의 경우 컴퓨터 A 가 B 에 대해 데이터를 통신하기 위해선 아래와 같은 플로우로 통신하게 된다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;컴퓨터 A -&gt; 허브1 -&gt; 스위치A -&gt; 라우터 -&gt; 스위치B -&gt; 허브2 -&gt; 컴퓨터B&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;반대로 컴퓨터 B 에서 A 방향으로 통신하길 원한다면 위 플로우와 반대 방향으로 이루어질 것이다.&lt;/p&gt;
&lt;p&gt;각 장치에 대한 기능을 정리해보면 다음과 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;허브: 여러대의 컴퓨터를 연결할 때 사용하는 장치&lt;/li&gt;
&lt;li&gt;스위치: 대역폭을 확대해주는 장치&lt;/li&gt;
&lt;li&gt;라우터: 컴퓨터 B를 찾아가기 위한 길을 제시하는 장치&lt;/li&gt;
&lt;li&gt;브리지: 2개 이상의 네트워크를 연결하기 위한 장치로, 데이터를 한 곳에서 다른 곳으로 전달하는 역할&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;이때 브리지는 OSI 7계층에서 각 상.하위 계층간에 데이터를 전갈할 때 다리 역할을 하는 것으로 이해하면 된다. 또한 스위치를 설명할 때 &lt;code class=&quot;language-text&quot;&gt;대역폭&lt;/code&gt; 이란 키워드가 등장했는데, 대역폭이란 1초당 처리할 수 있는 데이터의 양을 의미한다. 다시 말해, 대역폭이 넓을 수록 해당 네트워크가 동일한 시간내에 처리할 수 있는 양은 커지는 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;osi-7계층&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#osi-7%EA%B3%84%EC%B8%B5&quot; aria-label=&quot;osi 7계층 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OSI 7계층&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;프로토콜(protocol)&lt;/code&gt; 이란 컴퓨터나 원거리 통신 장비 사이에서 메시지를 주고받는 양식과 규칙의 체계를 뜻한다. 쉽게말해, 컴퓨터 세계에서 형식적으로 나누는 인사 과정을 뜻한다. 네트워크를 조금이라도 접해봤으면 알듯이, &lt;code class=&quot;language-text&quot;&gt;OSI 7계층&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;TCP/IP 4계층&lt;/code&gt; 이 대표적인 프로토콜이다. 이들의 간단한 오버뷰에 대해 알아보자.&lt;/p&gt;
&lt;h3 id=&quot;osi-7계층의-등장배경-필요성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#osi-7%EA%B3%84%EC%B8%B5%EC%9D%98-%EB%93%B1%EC%9E%A5%EB%B0%B0%EA%B2%BD-%ED%95%84%EC%9A%94%EC%84%B1&quot; aria-label=&quot;osi 7계층의 등장배경 필요성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OSI 7계층의 등장배경, 필요성&lt;/h3&gt;
&lt;p&gt;1980년대 부터 시작하여 &quot;다양한&quot; 통신 기술, 컴퓨터의 생산은 급격히 증가했다. 이에따라 당연하게도 컴퓨터간의 통신을 위해 각 회사마다 각기 다른 다양한 허브, 스위치, 라우터 장치를 생산하기 시작했다. 이렇듯 다양한 제조사에서 만든 컴퓨터와 허브, 스위치, 라우터를 서로 연결하여 통신할 떄 각기 다른 통신방법, 프로토콜을 사용하게 되었다.&lt;/p&gt;
&lt;p&gt;이에 따라 OSI 프로토콜 표준 규약이 등장했다. &lt;strong&gt;제조사마다 각기 다른 자신들만으 프로토콜을 사용하기 때문에 호환성을 보장하기 위해 등장했다.&lt;/strong&gt; 다양한 장치가 서로 간의 통신을 보장할 수 있는 규악이며, 각 계층별로 필요한 장치들을 정의했기 때문에 제조사들은 통신에 필요한 프로토콜을 통일할 수 있게 되었다.&lt;/p&gt;
&lt;h3 id=&quot;osi-7계층-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#osi-7%EA%B3%84%EC%B8%B5-1&quot; aria-label=&quot;osi 7계층 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OSI 7계층&lt;/h3&gt;
&lt;p&gt;OSI 7계층은 컴퓨터와 컴퓨터가 통신하는 구조를 7개의 계층으로 정의해둔 약속, 즉 프로토콜이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/794d64c52e5a009ba244260157114ea5/72aae/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.6441717791411%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACYElEQVR42nWS20sUYRiH94/w3pCgJCWkiMDErroKxIoISaIbDYsCO+yFiEE3UV4kaWEXhZR0EXXb4R+IDhedQGt3ZnfOzuw4552ZZXb8Ne/n7ua2OPDwvjMwz/cevlwYhiCiKATSGEA7cRxl1FCrtRPHcSs2HURuC9tPkgKfizW8+xbh/fcIb7P44UcMJ0iyw6oIowjVapX9FARBS0DSnU9uq2E0/TomlnWMLWxgfFHH6XkNl55UwCsOCn/WUSgWwfM8o5jlhUKBQe9RdliH0A1T3Hlt4dYLE/lVE9MrJm6/siFoLlRFgiRJEEURqqoyFEXB5uYmDMNgbXcI7SDF5LKBkXsaTt3XcPKuigtLBjjFhSjwEAQR5XKZQWISeZ4Hx3FQr9c7hRtOggPTInquCNh3VUTXxRIO5RWsCQ7EMgeO45lMEIQs56BpGquQSJKkU6i7dRyfUzBwU8LhvJRJBRybVbAuupDFEoqZkCojGbWr6zosy9pdaGTCoVkJ+6+J6KNKL5cxPKeiqPhQZQGSrLDZkdT3fbaI5qZ3aTnFkRkd/ddlHLwho3tKxNEZDZxaReDZsGyHVWTbNqvKNM3WUmiWO4TbRt0OMTj1Br1jz9E/voo9Z1YwOPkSnFSB77ls+K77LzYXQnfyv3vYEFoBhieeou/cEgbOP8be0QcYmniGkmwirPpwMwG1SniNnMR02cnR9OTSRqJUPPSMPkT3yAKLXSfm0Xv2EX6XdXiOBaNSYW1WGpHapcXQOy2JxG0zjGoJPv2S8WVNxdeMjz9l8LKN7Gg29DRNGZTvpPmtWeVfwiWhCEpPQrMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;image&quot;
        title=&quot;&quot;
        src=&quot;/static/794d64c52e5a009ba244260157114ea5/a6d36/image.png&quot;
        srcset=&quot;/static/794d64c52e5a009ba244260157114ea5/222b7/image.png 163w,
/static/794d64c52e5a009ba244260157114ea5/ff46a/image.png 325w,
/static/794d64c52e5a009ba244260157114ea5/a6d36/image.png 650w,
/static/794d64c52e5a009ba244260157114ea5/72aae/image.png 964w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;각 계층에 대한 특징은 다음과 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;7계층(Application Layer): 사용자와 애플리케이션 간의 소통을 정의함.&lt;/li&gt;
&lt;li&gt;6계층(Presentation Layer): 데이터를 어떻게 표현할지 정의함.&lt;/li&gt;
&lt;li&gt;5계층(Session Layer): 통신을 설정, 관리, 종료함.&lt;/li&gt;
&lt;li&gt;4계층(Transport Layer): 신뢰성있는 정확한 데이터를 전달함.&lt;/li&gt;
&lt;li&gt;3계층(Network Layer): 네트워크 장치간의 경로 선택과 데이터를 전송함.&lt;/li&gt;
&lt;li&gt;2계층(Data Link Layer): 물리적인 연결을 위해 오류없는 데이터를 전달함.&lt;/li&gt;
&lt;li&gt;1계층(Physical Layer): 전기 신호를 이용해서 통신 케이블로 데이터를 전송함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;캡슐화와-역캡술화-헤더header&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BA%A1%EC%8A%90%ED%99%94%EC%99%80-%EC%97%AD%EC%BA%A1%EC%88%A0%ED%99%94-%ED%97%A4%EB%8D%94header&quot; aria-label=&quot;캡슐화와 역캡술화 헤더header permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;캡슐화와 역캡술화, 헤더(Header)&lt;/h3&gt;
&lt;p&gt;송신자가 데이터를 전송할 때는 &lt;code class=&quot;language-text&quot;&gt;캡슐화&lt;/code&gt;, 반대로 수신자가 데이터를 전달받을때는 &lt;code class=&quot;language-text&quot;&gt;역캡슐화&lt;/code&gt; 가 발생한다. 캡슐화는 7계층에서 1계층 방향으로, 반대로 역캡슐화는 1계층에서 7계층으로 발생하면서 각 계층을 지나칠 때 마다 &lt;code class=&quot;language-text&quot;&gt;헤더(Header)&lt;/code&gt; 라는 단위가 덧붙여진다(해제된다).&lt;/p&gt;
&lt;p&gt;이때 각 계층에서 덧붙여지는 헤더(Header) 는 각 계층마다 다른 이름으로 부른다. 각 계층에서 사용하는 전송 단위인 헤더를 나열해보면 다음과 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;응용 계층부터 세션 계층까지(7~5계층): 데이터(메시지)&lt;/li&gt;
&lt;li&gt;전송 계층(4계층): 세그먼트(segment)&lt;/li&gt;
&lt;li&gt;네트워크 계층(3계층): 패킷&lt;/li&gt;
&lt;li&gt;데이터 링크 계층(2계층): 프레임&lt;/li&gt;
&lt;li&gt;물리 계층(1계층): 비트&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;다시 설명하자면 캡슐화 과정이 일어날 떄 한 단계씩 아래로 내려가면서 통신을 위한 정보인 헤더가 하나씩 붙게된다.
&lt;code class=&quot;language-text&quot;&gt;세그먼트&lt;/code&gt; 에서는 포트 번호, &lt;code class=&quot;language-text&quot;&gt;패킷&lt;/code&gt; 에서는 송.수신자의 IP 주소, &lt;code class=&quot;language-text&quot;&gt;프레임&lt;/code&gt; 에서는 송.수신자의 MAC 주소가 붙게 된다.&lt;/p&gt;
&lt;p&gt;반대로 역캡술화 과정이 발생할땐 헤더 정보가 하나씩 벗겨진다. &lt;code class=&quot;language-text&quot;&gt;데이터 링크 계층(프레임)&lt;/code&gt;에선 수신자가 MAC 주소를 획득하고, &lt;code class=&quot;language-text&quot;&gt;네트워크 계층(패킷)&lt;/code&gt;에선 IP 주소, &lt;code class=&quot;language-text&quot;&gt;전송 계층(세그먼트)&lt;/code&gt;에선 포트번호를 획득하게 된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;vpn&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#vpn&quot; aria-label=&quot;vpn permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;VPN&lt;/h2&gt;
&lt;p&gt;마지막으로 VPN 에게 소개하곘다. &lt;code class=&quot;language-text&quot;&gt;VPN(Virtual Private Network)&lt;/code&gt; 란 인터넷을 통해 데이터를 안전하게 전송하기 위한 기술이다.&lt;/p&gt;
&lt;p&gt;인하대학교 학사관리 시스템을 예로 들어보겠다. 가령 인하대학교 본교(본교는 인천시 미추홀구에 위치해있음) 의 학사관리 시스템이 있을 때, 학교의 중요한 기밀 정보를 외부에서 함부로 접근하지 못하도록 신경써야 할 것이다. 여기서 하나 가정을 더 붙여보자면, 인하대학교 본교가 아닌 제2 캠퍼스가 송도가 있다고 해보자. 이때 학사관리 시스템은 본교(미추홀구) 에 위치해있게 때문에 제2 캠퍼스(송도)에서 근무하는 교직원들에게도 특별히 외부 접근 권한을 부여해야 한다. 이때 VPN 이라는 것을 이용하면 제2 캠퍼스 교직원들이 학사관리 시스템이 접속하여 중요한 정보를 가져올 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;vpn-의-동작방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#vpn-%EC%9D%98-%EB%8F%99%EC%9E%91%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;vpn 의 동작방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;VPN 의 동작방식&lt;/h3&gt;
&lt;p&gt;VPN 은 일반적으로 다음과 같은 방식으로 동작한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;(1) VPN 클라이언트 소프트웨어 섩치&lt;/li&gt;
&lt;li&gt;(2) VPN 서버 연결 및 인증&lt;/li&gt;
&lt;li&gt;(3) 데이터 암호화&lt;/li&gt;
&lt;li&gt;(4) 터널링&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;우선 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 먼저 컴퓨터에 VPN 클라리언트 소프트웨어를 설치해야 한다. 인하대학교 제2 캠퍼트에서 근무하는 교직원들으 경우 자신의 노트북에 VPN 클라이언트를 설치해야 한다. 이후 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 설치된 VPN 클라이언트를 이용하여 VPN 서버에 연결해야 한다. 이때 사용자 이름과 암호를 사용하여 인증을 한다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; VPN 연결이 설정되면 모든 데이터는 암호화되어 전송된다. 이는 데이터가 인터넷을 통해 전송될 때 누구나 볼 수 없게 만들어준다. 마지막으로 &lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; VPN 은 암호화된 데이터를 전송하기 위해 터널링 기술을 사용한다. &lt;code class=&quot;language-text&quot;&gt;터닐링(Tunneling)&lt;/code&gt; 이란 본교와 제2 캠퍼스 간에 마치 터널이 뚫린 것처럼 네트워크 사이에 통로를 설정하는 것을 말한다.&lt;/p&gt;
&lt;p&gt;이후 통신이 종료되면 VPN 터널은 종료되고 VPN 연결도 종료된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;다음 포스트부터 본격적으로 OSI 7계층에 대해 다루고자 한다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[레거시 DAO 를 리팩토링하며 JDBC 라이브러리 구현하기]]></title><description><![CDATA[JDBC 라이브러리 구현하기 를 진행해보면서, 학습한 내용을 정리했다. JDBC 과거 HTTP 웹 서버가 가지고 있었던 문제는 사용자가 입력한 데이터가 서버를 재시작하면 사라진다는 점이다. 따라서 DB…]]></description><link>https://haon.site/haon/spring/jdbc-library-implementation/</link><guid isPermaLink="false">https://haon.site/haon/spring/jdbc-library-implementation/</guid><pubDate>Mon, 28 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;&quot;&gt;JDBC 라이브러리 구현하기&lt;/a&gt; 를 진행해보면서, 학습한 내용을 정리했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;jdbc&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jdbc&quot; aria-label=&quot;jdbc permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JDBC&lt;/h2&gt;
&lt;p&gt;과거 HTTP 웹 서버가 가지고 있었던 문제는 사용자가 입력한 데이터가 서버를 재시작하면 사라진다는 점이다. 따라서 DB 서버를 도입해야 하는것은 당연시 되었다. 자바 진영에서 JDBC 표준 인터페이스를 정의하고 있으며, 우리는 이를 활용하여 데이터베이스와의 통신을 할 수 있게된다.&lt;/p&gt;
&lt;p&gt;그런데 이떄, JDBC 는 &lt;code class=&quot;language-text&quot;&gt;인터페이스 정의&lt;/code&gt; 만을 제공하고 있다. 즉, JDBC 는 데이터베이스 통신을 위한 규악만 정하고, 이에대한 구현체는 데이터베이스를 만들어 서비스하는 회사가 직접 구현하고 제공하도록 하고있다. 서블릿도 마찬가지다. 서블릿 또한 인터페이스만 정의하고 서블릿 컨테이너를 만들어 제공하는 회사 또는 단체가 이 인터페이스에 대한 구현체를 제공하도록 하고 있다.&lt;/p&gt;
&lt;p&gt;정리하면, 이처럼 표준만 정의함으로써 DB 에 대한 연결설정만 변경해 다른 DB 를 지원함으로써 소스코드의 변경을 최소화하고 있다.&lt;/p&gt;
&lt;h3 id=&quot;jdbc-api&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jdbc-api&quot; aria-label=&quot;jdbc api permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JDBC API&lt;/h3&gt;
&lt;p&gt;JDBC API 를 통해 데이터베이스에 접근할 때, API 내부 코드에선 정말 많은 중복 코드가 발생한다. 그래서 이 중복 코드를 제거한 라이브러리가 바로 JdbcTemplate 이다. 즉 JdbcTemplate 이란 JDBC API 의 중복 코드를 제거한 SQLMapper 라고 볼 수 있다.&lt;/p&gt;
&lt;p&gt;이 라이브러리가 어떻게 등장했는지를 다루어보겠다. 우선 중복 코드가 많은 순수 JDBC API 레거시 코드를 점진적으로 중복 코드를 제거하면서, JdbcTemplate 를 모방한 코드를 직접 구현해보겠다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;레거시-userdao&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%EA%B1%B0%EC%8B%9C-userdao&quot; aria-label=&quot;레거시 userdao permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레거시 UserDao&lt;/h2&gt;
&lt;p&gt;UserDao 의 초기 레거시코드다. DAO 오브젝트를 통해 데이터베이스에 대한 접근 로직 처리를 담당하도록 되어있다.&lt;/p&gt;
&lt;p&gt;현재 초기 Dao 는 JDBC API 를 직접 사용하여 DataSource 생성하고, 커넥션을 획득 및 해제하며, 예외처리 작업을 모두 담당하고 있다. 그래서 가독성 및 생산성이 저하된다. 무엇보다 아까 언급했던 중복 코드가 여러 메소드에서 발생하고 있다. 따라서 이를 제거해야 함을 신경쓰면서 라이브러리를 구현해야했다. 구현한 세부 단계들을 천천히 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; con &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            con &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConnectionManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;INSERT INTO USERS VALUES (?, ?, ?, ?)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; con&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prepareStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pstmt &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;con &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                con&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; con &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            con &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConnectionManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;UPDATE USERS SET password = ?, name = ?, email = ?, WHERE userId = ?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; con&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prepareStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pstmt &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;con &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                con&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findByUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1단계-중복-로직을-처리할-클래스를-분리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1%EB%8B%A8%EA%B3%84-%EC%A4%91%EB%B3%B5-%EB%A1%9C%EC%A7%81%EC%9D%84-%EC%B2%98%EB%A6%AC%ED%95%A0-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%A5%BC-%EB%B6%84%EB%A6%AC&quot; aria-label=&quot;1단계 중복 로직을 처리할 클래스를 분리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1단계. 중복 로직을 처리할 클래스를 분리&lt;/h2&gt;
&lt;h3 id=&quot;jdbctemplate&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jdbctemplate&quot; aria-label=&quot;jdbctemplate permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JdbcTemplate&lt;/h3&gt;
&lt;p&gt;중복 코드를 제거하기 위해 앞서 말한 JdbcTemplate 모방 클래스를 구현했다. 반복적으로 발생하는 중복 로직을 상위 클래스가 구현하고 변화가 발생하는 부분만 추상 메소드로 만들어 구현하도록 하는 &lt;code class=&quot;language-text&quot;&gt;템플릿 메소드 패턴&lt;/code&gt; 을 적용했다. &lt;code class=&quot;language-text&quot;&gt;createQuery()&lt;/code&gt; 는 UserDao 로 부터 동적으로 SQL 쿼리문을 전달받으며, &lt;code class=&quot;language-text&quot;&gt;setValues()&lt;/code&gt; 는 PreparedStatement 를 활용하여 UserDao 에서 동적으로 쿼리문에 전달된 파라미터를 전달받는다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; con &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      con &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConnectionManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; con&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preparedStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pstmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pstmt &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;con &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        con&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;userdao&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#userdao&quot; aria-label=&quot;userdao permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UserDao&lt;/h3&gt;
&lt;p&gt;실제로 이에 상응하는 UserDao 구현부는 아래와 같이 구현해줬다. 2개의 추상 메소드를 Dao 에서 구현하여 동적으로 쿼리문을 JdbcTemplate 오브젝트에게 전달한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt; jdbcTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;INSERT INTO USERS VALUES (?, ?, ?, ?)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    jdbcTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ... (추가 메소드 구현)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2단계-select-절을-고려한-리팩토링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2%EB%8B%A8%EA%B3%84-select-%EC%A0%88%EC%9D%84-%EA%B3%A0%EB%A0%A4%ED%95%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81&quot; aria-label=&quot;2단계 select 절을 고려한 리팩토링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2단계. SELECT 절을 고려한 리팩토링&lt;/h2&gt;
&lt;h3 id=&quot;maprow-구현-createquery-제거&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#maprow-%EA%B5%AC%ED%98%84-createquery-%EC%A0%9C%EA%B1%B0&quot; aria-label=&quot;maprow 구현 createquery 제거 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;mapRow 구현, createQuery 제거&lt;/h3&gt;
&lt;p&gt;위 JdbcTemplate 코드만으론 SELECT 쿼리문에 대한 처리가 불가능하다. SELECT 절의 경우 INSERT, UPDATE 를 할때와 달리 &lt;strong&gt;조회한 데이터를 자바 객체로 변환하는 부분이 추가적으로 필요하다.&lt;/strong&gt; 그래서 새롭게 추가한 메소드가 &lt;code class=&quot;language-text&quot;&gt;mapRow()&lt;/code&gt; 이다.&lt;/p&gt;
&lt;p&gt;또한 기존의 &lt;code class=&quot;language-text&quot;&gt;createQuery()&lt;/code&gt; 는 제거했다. 쿼리문은 생각해보면 굳이 추상 메소드로 구현하지 않고도 UserDao 에서 insert(), update() 등의 메소드를 호출할 때 파라미터로 넘겨줄 수 있기 때문이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mapRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ResltSet&lt;/span&gt; rs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;query-queryforobject-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#query-queryforobject-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;query queryforobject 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;query, queryForObject 구현&lt;/h3&gt;
&lt;p&gt;UserDao 의 &lt;code class=&quot;language-text&quot;&gt;findUserById()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;findAll()&lt;/code&gt; 메소드가 활용할 &lt;code class=&quot;language-text&quot;&gt;query()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;queryForObject()&lt;/code&gt; 를 구현했다. 둘 다 조회를 위해 사용될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

   &lt;span class=&quot;token comment&quot;&gt;// 여러건의 데이터 조회&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; con &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;token class-name&quot;&gt;ResultSet&lt;/span&gt; rs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

     &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
       con &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConnectionManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
       pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; con&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prepareStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
       &lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pstmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        rs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mapRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// SELECT 쿼리를 실행하여 조회된 데이터를 하나씩 리스트에 Add&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

   &lt;span class=&quot;token comment&quot;&gt;// 한건의 데이터 조회&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;queryForObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mapRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ResltSet&lt;/span&gt; rs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;userdao-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#userdao-1&quot; aria-label=&quot;userdao 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UserDao&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;mapRow()&lt;/code&gt; 를 구현한 Dao 코드의 findAll() 구현부는 아래와 같다. 데이터베이스로부터 조회한 데이터는 ResultSet 파라미터에 담기고, 이를 자바 Object 타입으로 (정확히는 User 타입으로) 변환한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLExcpetion&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token annotation punctuation&quot;&gt;@SuperWarnings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;unchecked&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt; jdbcTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mapRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ResultSet&lt;/span&gt; rs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;userId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;password&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SELECT userId, password, name, email FROM USERS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;jdbcTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3단계-rowmapper-preparedstatementsetter-인터페이스를-활용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3%EB%8B%A8%EA%B3%84-rowmapper-preparedstatementsetter-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EB%A5%BC-%ED%99%9C%EC%9A%A9&quot; aria-label=&quot;3단계 rowmapper preparedstatementsetter 인터페이스를 활용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3단계. RowMapper, PreparedStatementSetter 인터페이스를 활용&lt;/h2&gt;
&lt;h3 id=&quot;문제점--불필요한-maprow--구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%A0%9C%EC%A0%90--%EB%B6%88%ED%95%84%EC%9A%94%ED%95%9C-maprow--%EA%B5%AC%ED%98%84&quot; aria-label=&quot;문제점  불필요한 maprow  구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문제점 : 불필요한 mapRow( ) 구현&lt;/h3&gt;
&lt;p&gt;하지만 여기서 또 문제가 생긴다. UserDao 는 SELECT 절 외에 INSET, DELETE, UPDATE 문을 실행할 땐 굳이 mapRow() 메소드를 구현할 필요가 없음에도, SELECT 절을 위해서 mapRow() 를 구현하는 불편함을 가지고 있다. 실제로 UserDaot 의 insert() 메소드를 살펴보자면 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt; jdbcTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mapRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Resultset&lt;/span&gt; rs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLExcpetion&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;INSERT INTO USERS VALUES (?, ?, ?, ?)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  jdbcTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이를 해결할 필요가 있다. 이 해결안은 바로 인터페이스에 있다.&lt;/p&gt;
&lt;h3 id=&quot;인터페이스-추가를-통해-해결하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EC%B6%94%EA%B0%80%EB%A5%BC-%ED%86%B5%ED%95%B4-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0&quot; aria-label=&quot;인터페이스 추가를 통해 해결하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인터페이스 추가를 통해 해결하기&lt;/h3&gt;
&lt;p&gt;mapRow() 를 쓸모없이 구현할 수 밖에 없었던 이유는 JdbcTemplate 와 UserDao 간의 의존관계가 생겨버렸기 때문이다. &lt;code class=&quot;language-text&quot;&gt;setValues()&lt;/code&gt; 메소드와 &lt;code class=&quot;language-text&quot;&gt;mapRow()&lt;/code&gt; 메소드를 별도로 분리해 서로 간의 의존관계를 끊어버릴 수만 있다면 더 유연한 개발이 가능해진다. 즉, Dao 의 각 메소드에선 본인에게 필요한 기능만 구현할 수 있게 만들면 된다.&lt;/p&gt;
&lt;p&gt;정리하자면, 위 2개의 추상 메소드를 같은 클래스가 가지도록 하지말고, 각 추상 메소드를 인터페이스로 따로 분리시키는 것이다. 아래와 같이 인터페이스를 구현한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreparedStatementSetter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RowMapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mapRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ResultSet&lt;/span&gt; rs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또 이를 활용한 리팩토링 코드는 아래와 같다. update, query 메소드만을 가져왔는데, 보듯이 인터페이스 오브젝트를 외부로 부터 필요한 것들만을 선택적으로 주입받고 활용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// PreparedStatementSetter 를 주입&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreparedStatementSetter&lt;/span&gt; pss&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; con &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      con &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConnectionManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; con&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preparedStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      pss&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pstmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pstmt &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;con &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        con&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// PreparedStatementSetter, RowMapper 를 주입&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreparedStatementSetter&lt;/span&gt; pss&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RowMapper&lt;/span&gt; rowMapper&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; con &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;ResultSet&lt;/span&gt; rs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      con &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConnectionManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; con&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prepareStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      pss&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pstmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      rs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;4단계-최종-리팩토링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4%EB%8B%A8%EA%B3%84-%EC%B5%9C%EC%A2%85-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81&quot; aria-label=&quot;4단계 최종 리팩토링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4단계. 최종 리팩토링&lt;/h2&gt;
&lt;p&gt;최종적으로 리팩토링한 전체 코드는 아래와 같다. 특별한것은 없고, 컴파일타임 Exception 인 SQLException 대신 RunTimException 을 상속한 커스터마이징 DataAccessException 을 활용헀다. 제네릭 문법을 활용해 User 타입 외에도 모든 타입에 범용성을 맞추도록 개선했으며, 가변인자를 활용하여 쿼리에 파라미터를 전달하도록 했다.&lt;/p&gt;
&lt;h3 id=&quot;jdbctemplate-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jdbctemplate-1&quot; aria-label=&quot;jdbctemplate 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JdbcTemplate&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; parameters&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataAccessException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; con &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConnectionManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
             &lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; con&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prepareStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; parameters&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;/* pss.setValues(pstmt);
            pstmt.executeUpdate(); */&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataAccessException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@SuppressWarnings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rawtypes&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreparedStatementSetter&lt;/span&gt; pss&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RowMapper&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; rowMapper&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataAccessException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ResultSet&lt;/span&gt; rs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; con &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConnectionManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
             &lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; con&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prepareStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

            pss&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pstmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            rs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rowMapper&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mapRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// SELECT 문으로 조회한 데이터를 자바 객체로 변환&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataAccessException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@SuppressWarnings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rawtypes&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;queryForObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreparedStatementSetter&lt;/span&gt; pss&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RowMapper&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; rowMapper&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataAccessException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pss&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rowMapper&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;userdao-2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#userdao-2&quot; aria-label=&quot;userdao 2 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UserDao&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt; jdbcTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;PreparedStatementSetter&lt;/span&gt; pss &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreparedStatementSetter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;INSERT INTO USERS VALUES (?, ?, ?, ?)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// jdbcTemplate.update(sql, pss);&lt;/span&gt;
        jdbcTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt; jdbcTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;PreparedStatementSetter&lt;/span&gt; pss &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreparedStatementSetter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                pstmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;UPDATE USERS SET password = ?, name = ?, email = ?, WHERE userId = ?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
       &lt;span class=&quot;token comment&quot;&gt;//  jdbcTemplate.update(sql, pss);&lt;/span&gt;
        jdbcTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt; jdbcTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;PreparedStatementSetter&lt;/span&gt; pss &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreparedStatementSetter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; pstmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;RowMapper&lt;/span&gt; rowMapper &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RowMapper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mapRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ResultSet&lt;/span&gt; rs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                        rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;userId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;password&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SELECT userId, password, name, email FROM USERS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; jdbcTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pss&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rowMapper&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findByUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt; jdbcTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SELECT userId, password, name, email FROM USERS WHERE userId = ?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; jdbcTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;queryForObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ResultSet&lt;/span&gt; rs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                    rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;userId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;password&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;JDBC 가 아직 다소 낮선 것 같다. ORM 에 최대한 의존하지 않겠다고 했지만, 역시 근본부터 시작해야 이 기술이 왜 등장했고, 왜 써야하는지를 알 수 있는것 같다. 이번 미션에서도 단순 JDBC API 를 넘어 JdbcTemplate 를 모방하여 구현해보며, SQLMapper 가 얼마나 중복 코드, 레거시 코드를 지양하기 위해 노력했는지 알 수 있었다 🙂&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;HikariCP&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Nginx로 로드밸런싱 환경을 구축해 트래픽 분산시키기]]></title><description><![CDATA[시작에 앞서 서비스 사용자가 늘어남에따라 서버의 부하를 방지하도록 로드밸런싱을 하여 트래픽을 적절히 분산시키는 환경을 간단히 실습해보고자 합니다. 이번 포스팅에서는 Nginx 로 직접 Reverse Proxy 를 셋팅하고 뒷단에 스프링부트 서버…]]></description><link>https://haon.site/haon/infra/nginx/load-balancing/</link><guid isPermaLink="false">https://haon.site/haon/infra/nginx/load-balancing/</guid><pubDate>Mon, 28 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;시작에-앞서&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%9E%91%EC%97%90-%EC%95%9E%EC%84%9C&quot; aria-label=&quot;시작에 앞서 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시작에 앞서&lt;/h2&gt;
&lt;p&gt;서비스 사용자가 늘어남에따라 &lt;strong&gt;서버의 부하를 방지하도록&lt;/strong&gt; 로드밸런싱을 하여 트래픽을 적절히 분산시키는 환경을 간단히 실습해보고자 합니다. 이번 포스팅에서는 Nginx 로 직접 Reverse Proxy 를 셋팅하고 뒷단에 스프링부트 서버 3대를 배치하여, 트래픽을 골구로 분산시키는 것을 구현해 보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;다중서버-구축-도메인-설계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%A4%91%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%84%A4%EA%B3%84&quot; aria-label=&quot;다중서버 구축 도메인 설계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다중서버 구축 도메인 설계&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/29abd7a5-3dea-4fc0-a099-8755232d1311/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;저희는 총 4개의 클라우드 서버 인스턴스를 생성해야합니다. 하나는 WS(웹서버)로써 Reverse Proxy 를 위한 Nginx 서버입니다. 나머지 3개의 서버에서는 8080포트로 다른 IP 서버를 구축할겁니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;클라우드 서버는 EC2 인스턴스를 4개 생성할까 고민했지만, 비용이 많이 나가는 것을 고려하여 Linode 에서 Ubuntu 22.04 LTS 를 기반으로 서버를 구축했습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;추가적으로 로드밸런싱 알고리즘은 가장 기본적인 Round Robin(라운드 로빈) 알고리즘으로 트래픽을 분산시켰습니다. (알고리즘에 대한 설명도 할것이니 걱정마세요!)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;애플리케이션-설계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%84%A4%EA%B3%84&quot; aria-label=&quot;애플리케이션 설계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;애플리케이션 설계&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/3911e5cc-f585-4c79-a5b6-7b1d35dcaa35/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;동일한 코드를 각 서버에서 실행시킬겁니다. 각 서버에서는 서버이름(serverName) 과 사용자 방문횟수(visitedCount) 를 멤버변수로 가지고 있는 구조입니다.&lt;/p&gt;
&lt;p&gt;매 방문마다 어떤 배포 서버에 접속했는지를 알 수 있도록 ServerName 와, 방문횟수를 매번 카운팅하여 몇번 방문했는지를 나타내는 visitedCount 변수의 값을 Map 형태로 리턴받는 구조입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;springboot-코드-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#springboot-%EC%BD%94%EB%93%9C-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;springboot 코드 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SpringBoot 코드 구현&lt;/h2&gt;
&lt;h3 id=&quot;servercontroller&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#servercontroller&quot; aria-label=&quot;servercontroller permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ServerController&lt;/h3&gt;
&lt;p&gt;아래와 같이 RestController 하나를 생성하고, 이 컨트롤러로 현재 요청을 보낸 서버에 대한 이름과 방문횟수를 조회할 수 있는 기능을 만들었습니다.&lt;/p&gt;
&lt;p&gt;이떄 @PropertySource 어노테이션을 통해 application.yml 에 존재하는 serverName 변수값을 불러왔습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@PropertySource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application.yml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServerController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;${serverName}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; serverName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; visitedCount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/getServerInfo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getServerInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        visitedCount&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; serverInfo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        serverInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ServerName:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; serverName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        serverInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;visitedCount:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; visitedCount&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;serverInfo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;applicationyml&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#applicationyml&quot; aria-label=&quot;applicationyml permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;application.yml&lt;/h3&gt;
&lt;p&gt;또 아래와 같이 application.yml 파일을 구성해줬습니다. 이떄 중요한 것은 각 서버를 구분짓기 위한 &lt;strong&gt;프로필(Profile) 설정&lt;/strong&gt;을 진행해준 것입니다.&lt;/p&gt;
&lt;p&gt;각 서버의 애플리케이션 코드는 모두 동일하나, 단순히 IP, 사용자 정보 정도만 바뀌는 정도라면 이렇게 application.yml 과 같은 설정파일에 프로필을 기록하는 것입니다.&lt;/p&gt;
&lt;p&gt;즉, 애플리케이션 코드는 전부 동일한데 설정정보만 조금 다르게 하고 싶다면, 각 서버를 구분지어줄 프로필을 부여하면 됩니다. 그러고 각 서버를 실행시킬때 각각 다른 프로필 정보에 기반해 실행하면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;spring&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  config&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    activate&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      on&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;profile&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; server1

serverName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; my&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;server1


&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;
spring&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  config&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    activate&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      on&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;profile&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; server2

serverName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; my&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;server2

&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;
spring&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  config&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    activate&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      on&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;profile&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; server3

serverName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; my&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;server3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;추후에 클라우드 서버에 올린 jar 파일을 실행할때 위와 같이 프로필 정보를 함께 입력해서 nohup 으로 실행하면, 해당 프로파일의 설정으로 애플리케이션을 시작할 수 있게됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;클라우드-환경-구축&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95&quot; aria-label=&quot;클라우드 환경 구축 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클라우드 환경 구축&lt;/h2&gt;
&lt;p&gt;클라우드 서버는 앞서 말씀드렸듯이 1개의 Nginx 서버와, 뒷단에서 스프링부트 애플리케이션이 실행되고 있는 3개의 서버를 구축했습니다. 우선 스프링부트 서버를 어떻게 구축했는지를 설명해보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;스프링부트-클라우드-서버-환경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EC%84%9C%EB%B2%84-%ED%99%98%EA%B2%BD&quot; aria-label=&quot;스프링부트 클라우드 서버 환경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링부트 클라우드 서버 환경&lt;/h2&gt;
&lt;h3 id=&quot;1jdk-11-설치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1jdk-11-%EC%84%A4%EC%B9%98&quot; aria-label=&quot;1jdk 11 설치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.jdk-11 설치&lt;/h3&gt;
&lt;p&gt;현재 Ubuntu 서버에는 자바가 설치되어 있지 않을겁니다. 자바 설치 여부를 확인해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;java &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;version&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만일 설치되어 있지 않다면 아래와 같이 자바를 서버에 설치해줍시다. default-jdk 로 jdk 를 설치하는 경우 기본적으로 jdk-11 이 설치된다는 점을 알고 넘어갑시다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;만일 본인의 프로젝트가 JAVA 11 버전이 아니라면 다른 방법을 통해 설치해주셔야합니다!&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;apt&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;get update
apt install &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;jdk &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;y&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러고 다시 자바 버전을 확인하여 자바가 정상적으로 설치되었는지 확인해봅시다!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;java &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;version&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2로컬에있는-jar-파일-전송&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2%EB%A1%9C%EC%BB%AC%EC%97%90%EC%9E%88%EB%8A%94-jar-%ED%8C%8C%EC%9D%BC-%EC%A0%84%EC%86%A1&quot; aria-label=&quot;2로컬에있는 jar 파일 전송 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.로컬에있는 jar 파일 전송&lt;/h3&gt;
&lt;p&gt;다음으로 로컬에서 작업한 스프링부트 프로젝트를 빌드하고 생성된 jar 파일을 클라우드 서버에 전송해봅시다. 저는 이번에는 scp 전송을 활용했습니다.
(SSH 도 사용해도 되지만, 예전에 사용해본 경험이 있어서 새로운 방식을 택했습니다!)&lt;/p&gt;
&lt;p&gt;아래처럼 gradlew 디렉토리에서 booJar 를 통해 프로젝트를 빌드하여 jar 파일을 생성합시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ ./gradlew bootJar&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;아래처럼 빌드에 성공해야합니다!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/41461ca6-7ac5-45df-98d4-4f0d2446a5e9/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그러면 생성된 jar 파일은 build/libs 안에 존재할겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ cd build
$ cd libs
$ ls   &lt;span class=&quot;token comment&quot;&gt;//  core-0.0.1-SNAPSHOT.jar&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3로컬에-생성한-jar-파일을-클라우드-서버-3개에-각각-전송하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3%EB%A1%9C%EC%BB%AC%EC%97%90-%EC%83%9D%EC%84%B1%ED%95%9C-jar-%ED%8C%8C%EC%9D%BC%EC%9D%84-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EC%84%9C%EB%B2%84-3%EA%B0%9C%EC%97%90-%EA%B0%81%EA%B0%81-%EC%A0%84%EC%86%A1%ED%95%98%EA%B8%B0&quot; aria-label=&quot;3로컬에 생성한 jar 파일을 클라우드 서버 3개에 각각 전송하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.로컬에 생성한 jar 파일을 클라우드 서버 3개에 각각 전송하기&lt;/h3&gt;
&lt;p&gt;이렇게 생성된 jar 파일을 클라우드 서버에 전송하면 됩니다. 앞서 말씀드렸듯이 SCP 를 활용했으며, 그 방법은 다음과 같이 진행해주시면 됩니다.&lt;/p&gt;
&lt;p&gt;참고로 test-server 란 제가 jar 파일을 제가 새롭게 파일명을 지어준것입니다.&lt;/p&gt;
&lt;p&gt;또 당연한 말이지만, 클라우드 서버 인스턴스를 3개 생성했다면 각 서버에 대해 모두 jar 파일을 각각 전송해주셔야합니다!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;파일명&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jar root&lt;span class=&quot;token annotation punctuation&quot;&gt;@111.111.111.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;test&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jar
&lt;span class=&quot;token comment&quot;&gt;// build/libs 에서 실행해주세요! (jar 파일에 존재하는 디렉토리에서)&lt;/span&gt;

ex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; $ scp core&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SNAPSHOT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jar  root&lt;span class=&quot;token annotation punctuation&quot;&gt;@111.111.111.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;test&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jar&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;jar 파일을 전송할떄 아래와 같이 root 계정의 비밀번호를 입력하도록 해야하는데, 본인의 클라우드 서버의 루트 계정을 입력해주시면 전송에 성공할겁니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/46f2127a-10a6-4ddc-a666-af415993c90a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;클라우드 서버에서 jar 파일이 잘 전송되었는지 확인도 해봅시다!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1ad53b68-d6dd-4848-98bf-b64d65225952/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4스프링부트-애플리케이션-실행하기-background-실행&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0-background-%EC%8B%A4%ED%96%89&quot; aria-label=&quot;4스프링부트 애플리케이션 실행하기 background 실행 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4.스프링부트 애플리케이션 실행하기 (Background 실행)&lt;/h3&gt;
&lt;p&gt;이제 클라우드 서버에 전송된 jar 파일을 실행시키면 스프링부트 애플리케이션이 배포될 겁니다. 저희는 서버가 다운되는 일이 없도록 nohup 을 통해 무중단배포를 실행할겁니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;이떄 프로필명을 구분지어서 각 서버에서 다른 프로필에 기반하여 각각의 다른 설정정보를 기반으로 스프링부트 애플리케이션을 실행시켜주셔야 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;아래와 같이 각 3개의 서버에서 프로필명을 각각 server1, server2, server3 로 실행시켜주시면 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ nohup java &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;jar &lt;span class=&quot;token class-name&quot;&gt;Dspring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;profiles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;active&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;프로필명&quot;&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jar &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;

ex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; $ nohup java &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;jar &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Dspring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;profiles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;active&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;server1 test&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jar &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;아래와 같이 실행되면 정상입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/866dce55-963c-4f4a-a281-a8e40e783ff7/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;또 백그라운드에서 스프링부트 애플리케이션이 잘 실행되고 있는지를 확인해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ jobs&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx-로드밸런서-서버-구축&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%84%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95&quot; aria-label=&quot;nginx 로드밸런서 서버 구축 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 로드밸런서 서버 구축&lt;/h2&gt;
&lt;p&gt;다음으로 Nginx 로 로드밸런싱을 해주도록, 인스턴스 서버 하나에 Nginx 를 설치하고 Reverse Proxy 를 셋팅해주겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;nginx-설치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EC%84%A4%EC%B9%98&quot; aria-label=&quot;nginx 설치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 설치&lt;/h3&gt;
&lt;p&gt;아래와 같이 Nginx 를 설치해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ apt install nginx     &lt;span class=&quot;token comment&quot;&gt;// nginx 설치&lt;/span&gt;
$ serivce nginx start   &lt;span class=&quot;token comment&quot;&gt;// 설치한 Nginx 가 정상적으로 반영되도록 재시작&lt;/span&gt;
$ ps &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ef &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; grep nginx        &lt;span class=&quot;token comment&quot;&gt;// 정상적으로 Nginx가 실행되었는지 확인&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;nginx-설정파일-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EC%84%A4%EC%A0%95%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;nginx 설정파일 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 설정파일 생성&lt;/h3&gt;
&lt;p&gt;그러고 Nginx 가 로드밸런싱을 하기위한 설정파일을 새롭게 생성해주고, 그 설정파일을 기반으로 뒷단의 스프링부트 애플리케이션에 트래픽이 분산되도록 해야합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;기본적으로 Nginx 는 http 요청이 들어왔을때 sites-enabled 디렉토리 안에 있는 파일들의 설정정보를 기반으로 로드밸런싱과 같은 일련의 작업을 수행합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;따라서 아래와 같이 sites-enabled 로 이동해서 로드밸런싱을 해주기 위한 새로운 파일을 생성하고, 설정정보를 기입해줍시다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;default 파일은 말그대로 http 요청이 들어왔을때 Nginx 에 어떤 작업을 수행할지에 대한 디폴트 설정정보 파일입니다. 이 디폴트 파일은 삭제시키고 저희만의 새로운 로드밸런싱 관련 파일을 만들어줍시다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ cd &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;sites&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;enabled
$ rm &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;rf &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//  default 파일 삭제&lt;/span&gt;
$ vi myapp      &lt;span class=&quot;token comment&quot;&gt;// nginx 로드밸럱싱 파일에 대한 새로운 파일생성&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;cf) Nginx 에 대한 설정정보 파일을 스캔하는 디렉토리를 새롭게 설정할 수 있습니다. 즉 sites-enabled 가 아닌 다른 디렉토리로도 설정이 가능합니다. 이는 nginx.conf 에서 수정해주시면 됩니다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx-로드밸런싱-설정하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0&quot; aria-label=&quot;nginx 로드밸런싱 설정하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 로드밸런싱 설정하기&lt;/h2&gt;
&lt;h3 id=&quot;myapp-설정파일-구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#myapp-%EC%84%A4%EC%A0%95%ED%8C%8C%EC%9D%BC-%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;myapp 설정파일 구성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;myapp 설정파일 구성&lt;/h3&gt;
&lt;p&gt;다음으로 이번 포스팅의 핵심인 Nginx 의 로드밸런싱 설정입니다. 직전에 생성한 myapp 에서 아래와 같이 구성해주시면 됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;클라이언트의 모든 요청은 80 포트로 들어오게 됩니다. 80 포트로 들어온 요청을 upstream 에 지정해준 3개의 스프링부트 서버로 로드밸런싱이 진행됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Nginx upstream 이란?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;=&gt; upstream 서버는 말로 Origin 서버라고도 부르비다. proxy_pass 를 통해 Nginx 웹서버가 받은 요청들을 여러대의 스프링부트 애플리케이션 서버로 넘겨주는 역할을 수행합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;upstream backend&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 로드밸런싱 디폴트 알고리즘 : Round Robin&lt;/span&gt;
        server &lt;span class=&quot;token number&quot;&gt;111.111&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 스프링부트 애플리케이션이 실행되고 있는&lt;/span&gt;
        server &lt;span class=&quot;token number&quot;&gt;111.222&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 서버 3개에 대한 IP 주소를 명시해주자!&lt;/span&gt;
        server &lt;span class=&quot;token number&quot;&gt;333.333&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.333&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

server&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        listen &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 클라이언트가 요청하는 포트&lt;/span&gt;

        location &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                proxy_pass http&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;backend&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 설정한 이름으로 요청 보내기&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;설정정보-다시-불러오기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%A4%EC%A0%95%EC%A0%95%EB%B3%B4-%EB%8B%A4%EC%8B%9C-%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0&quot; aria-label=&quot;설정정보 다시 불러오기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;설정정보 다시 불러오기&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ nginx -s reload&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;설정파일인 myapp 을 변경했다면 정상적으로 반영이 되도록, 위 명령을 통해 Nginx 의 설정 파일을 다시 로드하도록 합시다!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;트래픽-분산-눈으로-확인해보기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%98%ED%94%BD-%EB%B6%84%EC%82%B0-%EB%88%88%EC%9C%BC%EB%A1%9C-%ED%99%95%EC%9D%B8%ED%95%B4%EB%B3%B4%EA%B8%B0&quot; aria-label=&quot;트래픽 분산 눈으로 확인해보기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트래픽 분산 눈으로 확인해보기&lt;/h2&gt;
&lt;p&gt;위와 같이 Nginx 설정정보가 반영되었다면 Nginx 서버에 API 요청을 보내봅시다. 그러면 아래와 같은 결과를 확인할 수 있을겁니다!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;보시듯이 3대의 서버에 트래픽이 골구로 분산되는 모습을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;{
    &quot;ServerName:&quot;: &quot;my-server1&quot;,
    &quot;visitedCount:&quot;: &quot;1&quot;
}
{
    &quot;ServerName:&quot;: &quot;my-server2&quot;,
    &quot;visitedCount:&quot;: &quot;1&quot;
}
{
    &quot;ServerName:&quot;: &quot;my-server3&quot;,
    &quot;visitedCount:&quot;: &quot;2&quot;
}
{
    &quot;ServerName:&quot;: &quot;my-server1&quot;,
    &quot;visitedCount:&quot;: &quot;2&quot;
}
{
    &quot;ServerName:&quot;: &quot;my-server2&quot;,
    &quot;visitedCount:&quot;: &quot;2&quot;
}

{
    &quot;ServerName:&quot;: &quot;my-server3&quot;,
    &quot;visitedCount:&quot;: &quot;2&quot;
}
{
    &quot;ServerName:&quot;: &quot;my-server1&quot;,
    &quot;visitedCount:&quot;: &quot;3&quot;
}
        // ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;로드밸런싱-알고리즘&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98&quot; aria-label=&quot;로드밸런싱 알고리즘 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로드밸런싱 알고리즘&lt;/h2&gt;
&lt;p&gt;사실 로드밸런싱을 하는 방법이 다양하다는 사실 알고 계신가요?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;위에서 진행한 방식은 Round Robin(라운드 로빈) 알고리즘을 사용해서 트래픽을 분산시킨 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;별도로 알고리즘을 지정해주지 않으면 라운드 로빈 알고리즘을 사용하는 것이죠.&lt;/p&gt;
&lt;h3 id=&quot;알고리즘-종류&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%A2%85%EB%A5%98&quot; aria-label=&quot;알고리즘 종류 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;알고리즘 종류&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;알고리즘 종류&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Round Robin (Defualt)&lt;/td&gt;
&lt;td&gt;요청을 순서대로 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;least_conn&lt;/td&gt;
&lt;td&gt;현재 연결수가 가장 적은 서버로 요청을 전송&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;ip_hash&lt;/td&gt;
&lt;td&gt;클라이언트 IP 를 해싱하여 특정 특정 클라이언트가 특정 서버로 연결되게 한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Random&lt;/td&gt;
&lt;td&gt;트래픽을 무작위로 분배&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;least_time&lt;/td&gt;
&lt;td&gt;연결수가 가장 적으면서 + 평균 응답시간이 가장 적은 서버를 선택해서 분배 (Nginx Plus) 에서만 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;알고리즘과-어떻게-적용시키지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EA%B3%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%A0%81%EC%9A%A9%EC%8B%9C%ED%82%A4%EC%A7%80&quot; aria-label=&quot;알고리즘과 어떻게 적용시키지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;알고리즘과 어떻게 적용시키지?&lt;/h3&gt;
&lt;p&gt;알고리즘 적용은 앞서 적용했던 Nginx 설정파일에서 upstream 에 지정해주시면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;upstream backend&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        leaast_conn&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// least_conn 알고리즘을 적용&lt;/span&gt;
        server &lt;span class=&quot;token number&quot;&gt;111.111&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        server &lt;span class=&quot;token number&quot;&gt;111.222&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        server &lt;span class=&quot;token number&quot;&gt;333.333&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.333&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

server&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        listen &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        location &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                proxy_pass http&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;backend&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;server-에-대한-다양한-옵션&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#server-%EC%97%90-%EB%8C%80%ED%95%9C-%EB%8B%A4%EC%96%91%ED%95%9C-%EC%98%B5%EC%85%98&quot; aria-label=&quot;server 에 대한 다양한 옵션 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;server 에 대한 다양한 옵션&lt;/h2&gt;
&lt;p&gt;알고리즘 외에도 다양한 server 지시어에 다양한 옵션을 부여해서 트래픽을 분산시킬 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;weight&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#weight&quot; aria-label=&quot;weight permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;weight&lt;/h3&gt;
&lt;p&gt;특정 서버에 가중치를 설정 가능합니다. 가중치가 설정된 서버는 다른 서버보다 가중치의 배수만큼 트래픽을 받게됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;upstream backend &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  server &lt;span class=&quot;token number&quot;&gt;111.111&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt; weight&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 다른 서버보다 4배의 트래픽을 받는다.&lt;/span&gt;
  server &lt;span class=&quot;token number&quot;&gt;222.222&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  server &lt;span class=&quot;token number&quot;&gt;333.333&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.333&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;down&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#down&quot; aria-label=&quot;down permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;down&lt;/h3&gt;
&lt;p&gt;down 옵션을 부여하면 특정 서버로 트래픽이 분배되지 않도록 할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;upstream backend &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  server &lt;span class=&quot;token number&quot;&gt;111.111&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt; down&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 2, 3번째 서버만 트래픽을 분배받는다.&lt;/span&gt;
  server &lt;span class=&quot;token number&quot;&gt;222.222&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  server &lt;span class=&quot;token number&quot;&gt;333.333&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.333&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;max_conns&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#max_conns&quot; aria-label=&quot;max_conns permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;max_conns&lt;/h3&gt;
&lt;p&gt;서버와의 최대 동시 커넥션 수를 지정된 숫자만큼으로 제한하는 옵션입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;upstream backend &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  server &lt;span class=&quot;token number&quot;&gt;111.111&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt; max_conns&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 최대 10개의 트래픽을 분배받는다.&lt;/span&gt;
  server &lt;span class=&quot;token number&quot;&gt;222.222&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  server &lt;span class=&quot;token number&quot;&gt;333.333&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.333&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금까지 Nginx 를 통해 로드밸런싱을 진행하고, 뒷단에 존재하는 3대의 스프링부트 서버에 대해 트래픽을 분산시키는 다양한 알고리즘과 옵션에 대해 알아봤습니다. Nginx 를 학습하시는 분들에게 이번 포스팅이 도움이 되었으면 하는 바람입니다 😉&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://hays99.tistory.com/250&quot;&gt;[Nginx] Load Balancing 설정하기&lt;/a&gt;
&lt;a href=&quot;https://velog.io/@kimjiwonpg98/Nginx-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EA%B5%AC%EC%B6%95&quot;&gt;[Nginx] 로드밸런싱 개념 및 구축&lt;/a&gt;
&lt;a href=&quot;https://kamang-it.tistory.com/entry/WebServernginxnginx%EB%A1%9C-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1-%ED%95%98%EA%B8%B0&quot;&gt;WebServer[nginx] nginx 로 로드밸런싱 하기&lt;/a&gt;
&lt;a href=&quot;https://www.blog.ecsimsw.com/entry/Nginx-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot;&gt;Nginx 로드밸런싱 적용하기&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://solbel.tistory.com/1433&quot;&gt;[springboot] 스프링부트 jar 파일 백그라운드 실행방법&lt;/a&gt;
&lt;a href=&quot;https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;#x26;blogId=edy5016&amp;#x26;logNo=221280993916&quot;&gt;스프링부트 Profile 사용하기&lt;/a&gt;
&lt;a href=&quot;https://velog.io/@dabeen-jung/cannot-find-symbol-method-value-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0&quot;&gt;cannot find symbol method value() 에러 해결&lt;/a&gt;
&lt;a href=&quot;https://blog.acronym.co.kr/555&quot;&gt;nohup 명령어 활용하기&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[프로세스 및 쓰레드 동기화를 위한 여러 상호배제 기법 (mutex, semaphore, monitor)]]></title><description><![CDATA[스프링 애플리케이션은 멀티 쓰레드 기반으로 동작한다. 이로 인해 공유 자원에 대한 동시성 이슈는 언제든 발생할 수 있다. 경쟁 상태가 발생할 수 있는 조건에 대해 경쟁 상태의 2가지 패턴 - Read-Modify-Write, Check-Then-Act…]]></description><link>https://haon.site/cs/os/mutex-semaphore-monitor/index.md/</link><guid isPermaLink="false">https://haon.site/cs/os/mutex-semaphore-monitor/index.md/</guid><pubDate>Sun, 27 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;스프링 애플리케이션은 멀티 쓰레드 기반으로 동작한다. 이로 인해 공유 자원에 대한 동시성 이슈는 언제든 발생할 수 있다. 경쟁 상태가 발생할 수 있는 조건에 대해 &lt;a href=&quot;https://haon.blog/database/race-condition-pattern/&quot;&gt;경쟁 상태의 2가지 패턴 - Read-Modify-Write, Check-Then-Act&lt;/a&gt; 에서 다룬적이 있으니, 경쟁상태와 임계영역에 대한 설명은 생략한다.&lt;/p&gt;
&lt;p&gt;이번 포스팅에선 경쟁상태에 대한 동시 접근 제어 메커니즘 3가지인 뮤텍스, 세마포어, 모니터에 대해 학습해보도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;상호-배제mutual-exclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%81%ED%98%B8-%EB%B0%B0%EC%A0%9Cmutual-exclusion&quot; aria-label=&quot;상호 배제mutual exclusion permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;상호 배제(Mutual Exclusion)&lt;/h2&gt;
&lt;p&gt;공유 자원에 대해 다량의 프로세스 또는 쓰레드가 접근하면 경쟁 상태가 발생할 수 있다. 따라서 프로세스나 쓰레드를 동기화(synchornization) 할 수 있는 방법이 필요하다. 이때 동기화란, 공유 자원에 대해 동시에 접근하지 못하도록 접근 순서를 제어하는 방법을 뜻한다.&lt;/p&gt;
&lt;p&gt;프로세스 및 쓰레드간 동기화는 일반적으로 &lt;strong&gt;상호 배제(Mutual Exclustion)&lt;/strong&gt; 을 통해 구현할 수 있다. 아래 3가지 방법은 모두 Lock 메커니즘을 기반으로 동작한다. 자원에 접근한 뒤 해당 자원을 잠그고, 자원을 모두 사용하면 잠금을 헤제하는 방식으로 동작한다.&lt;/p&gt;
&lt;h3 id=&quot;뮤텍스-mutex&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AE%A4%ED%85%8D%EC%8A%A4-mutex&quot; aria-label=&quot;뮤텍스 mutex permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;뮤텍스 (Mutex)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/223a781995464c93fbb63bcef2aede6c/21b4d/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.65030674846626%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABzUlEQVR42o2S72tSYRTH7/9Vb6Ja9CboD+iP6HdUsEWRG7gWjhFbFGyEuchaJqjXsSnlTEO9TCfeHGvth4qyuXS73qve++m5dwar3nTge57Dec75Hp7neyTLsvhtJ+P/sb97bUhmz6LRqFOrV+gaffvKKTDNjPABtrdesVZ4zs6uVzQEBSICR06N0evSqDcE6hhdw8lJ4ZSX0ehlXEuX8MXH0TVjMFEW3sPWj0ckElfJF+6LUVMi91LgkG7fRFlaoeRbRvXFyMmf6eg60j3/ECPyWR4uXuD2winkFT/aYY+cMiMmj7G5OUqrNUm16qayO4ZacrG9s05RKZCefs+6L87Gm08oL0KUV4tID6LnGQ5c5ObsEHc+nGYu+IRceg3/22G+pG6RTN51SPP5EWKx64RD11hcDiEvhFHnoiSm3xH1eB3CYjKL9Cxyg8epM7iVc7jDV6jWKs6T+/0wHc2NYUzRbk+gaZMiFtBnxO0RzYM2X+cjNAM5WsFVsq9loUUD6edBi3j2I3J6nlqtevx/NmEvzjfVRUmdEMJ4+L7xlHJ5nP3mrENo297+HoVkhkIiQ10I44jy56rY0puDFbDF6Qjog/M4tqzOiZp/1+YX7JQ0itCkBbUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/223a781995464c93fbb63bcef2aede6c/a6d36/image.png&quot;
        srcset=&quot;/static/223a781995464c93fbb63bcef2aede6c/222b7/image.png 163w,
/static/223a781995464c93fbb63bcef2aede6c/ff46a/image.png 325w,
/static/223a781995464c93fbb63bcef2aede6c/a6d36/image.png 650w,
/static/223a781995464c93fbb63bcef2aede6c/e548f/image.png 975w,
/static/223a781995464c93fbb63bcef2aede6c/21b4d/image.png 1280w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;뮤텍스는 Mutual Exclustion 의 약자로, 말 그대로 상호 배제를 위한 기법이다. 공유 자원을 사용하기전에 잠그고, 사용을 마쳤다면 잠금을 해제하는 간단한 방식이다. 이때, 잠금이 설정되면 다른 프로세스나 쓰레드는 해당 공유 자원에 접근할 수 없다. 뮤텍스는 (1) 잠김 (2) 잠기지 않음 2가지 상태 상태만을 갖는다.&lt;/p&gt;
&lt;p&gt;뮤텍스는 화장실이 하나밖에 없는 식당과 비슷하다. 화장실에 가려면 카운터에서 열쇠를 받아가야 한다. 만약 화장실을 가려는데 카운터에 키가 있으면 사람이 없다는 뜻이고, 그 열쇠를 이용해서 화장실 잠금을 풀고 들어갈 수 있는 것과 유사하다.&lt;/p&gt;
&lt;h3 id=&quot;세마포어-semaphore&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B8%EB%A7%88%ED%8F%AC%EC%96%B4-semaphore&quot; aria-label=&quot;세마포어 semaphore permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;세마포어 (Semaphore)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9f37c386f0264fb63965629a248bc8e4/c2d9c/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.48466257668711%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC8UlEQVR42nWUbU8TWRTH+82M7gtfmo2J2eynMJEQWXR1BUHQVYPRNVYI+kaTlWVhy9JSyoO1dnhwSmkLA32EbQuFSmmndKbTdn57O4MxRj3JnXvnnnvPOf/zP+c6TNOkLcXDj+Rze5gtTqVOw5DY3/dSLM5zcDAn5jkMY8nStaWsVkgpCXEvh2VFfBztObDu5sHizzwIXGIy6MRstner1OuPWF7uICh1IIkRCnVSrgwJnU7puML6xCKFiVUSb/xshWKWE4eqqtwav8DN2bP0zp/nuuscK2vviW9FyGQGyeXukUz0k84MUCg8RNeHUWsHSDN+wk4X8ug0my+8yK+96EbdNtgz8SPdrjN0/nmG7okfcHnGmHH/g2fmCrJ8nUT8Njs7A4TDv+LzdTI2/po5l4foqJv1lzNER9x8GPNhtBo2ZH/4XwH5Jx4GLjIpPaNp2JBbrWHS6X7LYD5/j2j0BrFYH6WjHNWaRmxOIvlXgNjfb9lNZWzIpsik2TCRwxG8/gDV4xM74eUcGxu3WFjoEIZuEhNrRflNkPJCaGvWmaqms5vNUvhYPCXFtCP0rUW5H93jSdbAGVyjXDpmamqc6enLJJJ9AuoNEiKPfn+ncHCVZDJCTW+yuTJFYdVJ6v0IO4lNO8LayQmDs0G6PMt0uRb5ZV7mjfctud0NNO0PUinboLLVS3y7l6Wla7iEs8CCh+3ZO+SlxxwuDRH2PaduNHEYmsaTdyEGlEN+Vw7okRTC22mr1lrmMNnsIMpmj4i0XxBzh0ZjlBPtCDk4T3phkNXJPmLu20R8T0UFGDbk+M5/PJUiDH2I45EjIqcti5Ti4V1C8lXC691ExIhGukSh3xc6DbWqs/HuFVpshMKKk+3o8ikpp53SqOtoVZV2q9g7TZrNjIgoI9jetYZhpK1/02zYpNR0UaOKqNWcQGO3isMmx/zUb1+s21Iqqezli+ztH1Gr1b/Qfeue43sHPomua1QqZVTRt4ZR/0pvthF9fgD4H+50lNBpWcv/AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/9f37c386f0264fb63965629a248bc8e4/a6d36/image-1.png&quot;
        srcset=&quot;/static/9f37c386f0264fb63965629a248bc8e4/222b7/image-1.png 163w,
/static/9f37c386f0264fb63965629a248bc8e4/ff46a/image-1.png 325w,
/static/9f37c386f0264fb63965629a248bc8e4/a6d36/image-1.png 650w,
/static/9f37c386f0264fb63965629a248bc8e4/e548f/image-1.png 975w,
/static/9f37c386f0264fb63965629a248bc8e4/3c492/image-1.png 1300w,
/static/9f37c386f0264fb63965629a248bc8e4/c2d9c/image-1.png 1326w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;세마포어는 공유 자원에 접근할 수 있는 여러 프로세스 또는 쓰레드의 &lt;strong&gt;최대 허용치를 관리하는 정수 타입 변수&lt;/strong&gt;이다. 만약 정수 값이 5라면, 최대 5개의 프로세스 또는 쓰레드가 공유 자원에 접근할 수 있다. (마치 화장실에 최대 5명의 사용자가 들어갈 수 있는 경우와 비슷하다.)&lt;/p&gt;
&lt;p&gt;프로세스 하나가 이 공유 자원에 접근하면 정수 값을 1 감소시키고, 반대로 공유 자원을 모두 사용한 뒤 임계영역에서 프로세스가 하나 나왔다면 다시 정수값을 1 증가시킨다. 여기서 각 과정을 &lt;code class=&quot;language-text&quot;&gt;wait()&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;signal()&lt;/code&gt; 이라고 한다. 만약 세마포어 값이 0이라면 이 이상 임계영역에 프로세스 또는 쓰레드가 진입이 불가능한 상태가 되며, 1 이상인 양수일 때만이 접근 가능하다.&lt;/p&gt;
&lt;p&gt;즉, &lt;code class=&quot;language-text&quot;&gt;wait()&lt;/code&gt; (또는 P) 가 호출되면 세마포어 정수 값을 1 감소시킨다. 만약 감소시키려는데 세마포어 값이 0이라면, 해당 프로세스는 세마포어 대기열에서 기다려야한다. 반면 &lt;code class=&quot;language-text&quot;&gt;signal()&lt;/code&gt; (또는 V) 은 세마포어 값을 1 증가시키고, 세마포어 대기열에서 기다리고 있는 맨 앞의 프로세르르 하나 깨워서 임계영역에 진입하도록 한다.&lt;/p&gt;
&lt;h4&gt;이진 세마포어(Binary Semaphore) vs 계수 세마포어(Counting Semaphore)&lt;/h4&gt;
&lt;p&gt;세마포어는 정수 값 크기에 따라 이진 세마포어와 계수 세마포어 2가지 종류로 나뉜다. 이진 세마포어는 0 또는 1 값만 가질 수 있다. 이진 세마포어는 마치 뮤텍스와 비슷하게 동작한다. 다만, 뮤텍스는 잠금 메커니즘이고, 세마포어는 신호 기반의 상호 배제 방법이다. 계수 세마포어는 1보다 큰 세마포어 정수를 가질 수 있는 방식이다.&lt;/p&gt;
&lt;h3 id=&quot;모니터-monitor&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A8%EB%8B%88%ED%84%B0-monitor&quot; aria-label=&quot;모니터 monitor permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;모니터 (Monitor)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b8890322752e0314a207a10c39a39bdf/0d0e4/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.89570552147239%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABaklEQVR42o2TTW+CQBCG/ec99Td4US/Vg016ozdjGompIRpO8nVAGwRUBAVFwKivOxtpWqyWScgku7PPvPNBBQU7n8+4Z8fjkd+Tv2eVIuhwOGA0GqHf76PT6WC73eYBmC8W+LIsRNezv5LfAIfDIer1OhqNBlqtFsIw/A42xmOI7wLWnvc/8HQ6cU/KqtUqarUaJEn69dCSBhCfn+BNJuUVBkEA13UhyzJmsxlPlKYpsixDuNkgSxL4LGa32z0G/jTf97Fmjw1Vhes4iPd73ktKtnZsRFGEDbvfs/Oi0huFcRwjYEAyRxlDEz8wXy5hsjI9BpffXrlaglLsXWBuNGXKnl29PPiEIAh8QM2XJgxd460wTfOxwmLJFlsP27ah6jra7TZEUUSv14OiKDAMgwMT1s9SwNVqxZeXvMr6SKtEACqRdrTb7XJfGki7R+WSQk3TMJ1OeZl0TsOhj8otPeX898ofPPodi8AL7Y2U9SoJgNQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/b8890322752e0314a207a10c39a39bdf/a6d36/image-2.png&quot;
        srcset=&quot;/static/b8890322752e0314a207a10c39a39bdf/222b7/image-2.png 163w,
/static/b8890322752e0314a207a10c39a39bdf/ff46a/image-2.png 325w,
/static/b8890322752e0314a207a10c39a39bdf/a6d36/image-2.png 650w,
/static/b8890322752e0314a207a10c39a39bdf/e548f/image-2.png 975w,
/static/b8890322752e0314a207a10c39a39bdf/0d0e4/image-2.png 1230w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;세마포어를 사용하기 위해건 &lt;strong&gt;임계 영역에 명시적으로 상호 배제 로직을 우리가 직접 구현해야한다. 모니터는 이 상호 배제 로직을 제거하고, 공유 자원 접근에 대한 고수준의 인터페이만을 제공한다.&lt;/strong&gt; 또한 공유 자원도 외부로부터 캡슐화하여 숨긴다. 즉, 모니터를 사용하며 세마포어를 직접 구현하지 않아도 되고 제공되는 인터페이스만을 사용하여 간편히 상호배제를 보장할 수 있다. 따라서 모니터를 사용하며 세마포어보다 구현 난이도가 매우 쉽고, 실수가 줄어든다. 자바의 &lt;code class=&quot;language-text&quot;&gt;synchornized&lt;/code&gt; 의 경우가 모니터를 사용하여 구현되었다.&lt;/p&gt;
&lt;p&gt;공유 자원에 접근하고자 하는 프로세스 또는 쓰례드는 모니터 내부로 진입해야한다. &lt;strong&gt;모니터는 모니터 큐에 작업 이벤트를 순차적으로 쌓아두고, 한번에 한 프로세스 또는 쓰레드만 임계 영역에 접근&lt;/strong&gt;할 수 있도록 한다. 즉, 한번에 하나의 프로세스만 모니터에서 작업할 수 있도록 해준다. 개념적으로는 이진 세마포어와 같은 기능을 제공하는 셈이다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;혼자 공부하는 컴퓨터구조, 운영체제 (한빛미디어 강민철 지음)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://heeonii.tistory.com/14&quot;&gt;https://heeonii.tistory.com/14&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/race-condition-critical-section-mutual-exclusion/#%EB%AE%A4%ED%85%8D%EC%8A%A4-mutex&quot;&gt;https://hudi.blog/race-condition-critical-section-mutual-exclusion/#%EB%AE%A4%ED%85%8D%EC%8A%A4-mutex&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@leesfact/%EB%AE%A4%ED%85%8D%EC%8A%A4-%EC%84%B8%EB%A7%88%ED%8F%AC%EC%96%B4-%EB%AA%A8%EB%8B%88%ED%84%B0&quot;&gt;https://velog.io/@leesfact/뮤텍스-세마포어-모니터&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[[가상 면접 사례로 배우는 대규모 시스템 설계 기초] 제 1장. 사용자 수에 따른 규모 확장성]]></title><description><![CDATA[…]]></description><link>https://haon.site/virtual-interview/chap01/</link><guid isPermaLink="false">https://haon.site/virtual-interview/chap01/</guid><pubDate>Fri, 25 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;수백만 사용자르 지원하는 시스템을 설계하는 것은 도전적인 과제이다. 이번 포스팅에서는 한 명의 사용자를 지원하는 시스템에서 시작하여, 최종적으로는 몇 백만 사용자를 지원하는 시스템을 설계하는 방법에 대해 학습해보도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;단일-서버&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%9D%BC-%EC%84%9C%EB%B2%84&quot; aria-label=&quot;단일 서버 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단일 서버&lt;/h2&gt;
&lt;p&gt;모든 컴포넌트가 단 1대의 서버에서 실행되는 간단한 시스템부터 설계해보자. 일반적으로 아래와 같은 절차로 수행될 것이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f8e0cbc93d6eef29bc806d875982422d/b4904/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.122699386503065%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABg0lEQVR42o1Tu6rCQBTc1l+xN59hIQgp/AAbSetPiEq6qBAL24BgKYogpLNS0VjYmQhR8ZX4nnvPwkpici8ObM7u7GaYs+cswy+ezycFTCYTtFot6LqO+XwOsfd6vUKDsFgs0O/3YVkWgmD0eTwefEFiqVQKkiTBMAwE9wSEYL1eRzabRa1WC/EsuFgul5hOpxiPx5jNZpy7XC7Y7XbY7/fYbDY4HA6cbzabSCaTaDQaCGYZEqQ0TdNEr9fDaDTinOd5OB6POJ1OPJ7PZ84rigLGGIrFYlRQiPq+j+FwiHa7je12+xYkVyRGkc4QyPVqtYpcCcMHbrfbOy3h/D98nmGCGAwGUFUVlUoF1WoV5XIZmqbher3G/hiseKzDUqmETCYDWZaRz+eRTqeRy+V4qt+6DQlSNQndbpc7o6rS/fzl7LMvI4L3+53HQqHAq9fpdELV+xYRQWqDRCLxbmzBi7lt27wf1+s1HMeB67rxgsI6tQM9p7i7ozkViYSpG2jQOnjmB/XRdh7wo33fAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/f8e0cbc93d6eef29bc806d875982422d/a6d36/image.png&quot;
        srcset=&quot;/static/f8e0cbc93d6eef29bc806d875982422d/222b7/image.png 163w,
/static/f8e0cbc93d6eef29bc806d875982422d/ff46a/image.png 325w,
/static/f8e0cbc93d6eef29bc806d875982422d/a6d36/image.png 650w,
/static/f8e0cbc93d6eef29bc806d875982422d/e548f/image.png 975w,
/static/f8e0cbc93d6eef29bc806d875982422d/3c492/image.png 1300w,
/static/f8e0cbc93d6eef29bc806d875982422d/b4904/image.png 1768w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 사용자는 도메인 이름으로 DNS 에 질의하여 IP 주소를 얻어낸다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 이 IP 주소로 사용자는 HTTP 요청을 전송한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 웹 서버는사용자에게 HTML 웹 페이지를 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;데이터베이스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4&quot; aria-label=&quot;데이터베이스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8de9cfaf41331b325b9f211f3bd4636b/2dc7d/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.963190184049076%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABuklEQVR42p1Ty6qBURjlMfAEPIEy5AlMlZEkl4zIbSDFE5h5AwNRIhNMlZkiIhLKLbfIfZ2+r76/4/ynMzirVnu3/7XX3t+39q95v98gnM9nZLNZpNNp5HI5vF4vXqfvQlnbbrcoFosoFAo8Fx1BI5Pj8QiHwwGr1Qq3243n84mfEO1oNILH44HX68VkMvk0FPH1ekWv18PlckGn0+GRcDqdsNvtcDgcsNlseG25XMJiscBsNmM+n/9ueLvdUKvVUCqVkM/nFcPVasVGRJoTxuMxdDodjEYjptOp2lB6RLdoNBqYzWbcLyKVLnMi6R6PB5MgawLNzz6RqYj/AzakxmYyGSQSCUSjUWYgEECz2VRuIQlTBZFIBKlUisd4PP5RNhtSCMFgEH6/H6FQCLFYDE6nE9VqlYVSNoH66/P5+ALJZJINKfUPQxFTeuFwGPV6XTH6zvv9rjwbl8vFHA6H6lDEsN/vQ6vVwm63c+okomTX6zVzsViwrlKpwGAwQK/Xo1wuK4erDOk0k8nE5UrC9CfQo6ew9vs969rtNmw2G7PVaqkNBWRCm2jzX6Cbd7tdDAYDroBaRaQefgELy1TrIp9yNQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/8de9cfaf41331b325b9f211f3bd4636b/a6d36/image-1.png&quot;
        srcset=&quot;/static/8de9cfaf41331b325b9f211f3bd4636b/222b7/image-1.png 163w,
/static/8de9cfaf41331b325b9f211f3bd4636b/ff46a/image-1.png 325w,
/static/8de9cfaf41331b325b9f211f3bd4636b/a6d36/image-1.png 650w,
/static/8de9cfaf41331b325b9f211f3bd4636b/e548f/image-1.png 975w,
/static/8de9cfaf41331b325b9f211f3bd4636b/3c492/image-1.png 1300w,
/static/8de9cfaf41331b325b9f211f3bd4636b/2dc7d/image-1.png 1760w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;사용자가 늘면 서버 하나로는 충분하지 않다. 따라서 여러 서버를 배치해야 한다. 한 서버는 웹/모바일 트래픽 처리 서버이고, 다른 하나는 데이터베이스 서버이다. 이렇게 각 서버를 용도별로 분리하면 각자 독립적으로 확장해 나갈 수 있게 된다.&lt;/p&gt;
&lt;h3 id=&quot;어떤-데이터베이스를-사용할-것인가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%96%A4-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%A0-%EA%B2%83%EC%9D%B8%EA%B0%80&quot; aria-label=&quot;어떤 데이터베이스를 사용할 것인가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어떤 데이터베이스를 사용할 것인가?&lt;/h3&gt;
&lt;p&gt;전통적인 RDBMS 에는 MySQL, Oracle DB, PostgreSQL 등이 있다. RDBMS 는 자료를 테이블과 열, 컬럼으로 표현하며, SQL 을 사용하면 여러 테이블에 있는 데이터를 그 관계에 따라 JOIN 하여 합칠 수 있다.&lt;/p&gt;
&lt;p&gt;반면 NoSQL 도 에는 CouchDB, DynamoDB 등이 있다. NoSQL 은 다시 4가지 분류로 나눌 수 있는데, key-value 저장소, 그래프 저장소, 컬럼 저장소, 그리고 문서 저장소로 분류할 수 있다.&lt;/p&gt;
&lt;h4&gt;NoSQL 을 사용해야 하는 일부 케이스&lt;/h4&gt;
&lt;p&gt;대부분의 상황에서는 시장에서 40년간 잘 살아남은 RDBMS 를 사용하는 것이 최선이다. 그렇다면, 언제 NoSQL 을 사용하는 것이 바람직할까?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;아주 낮은 Latency 가 요구되는 상황&lt;/li&gt;
&lt;li&gt;다루는 데이터가 비정형(unstructured) 이라서 관계형 데이터가 아닌 상황&lt;/li&gt;
&lt;li&gt;데이터(JSON, XML 등) 를 직렬화하거나 역직렬화 할 수 있기만 하면 되는 상황&lt;/li&gt;
&lt;li&gt;아주 많은 양의 데이터를 저장해야 하는 상황&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;수직적-규모-확장-vs-수평적-규모-확장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%98%EC%A7%81%EC%A0%81-%EA%B7%9C%EB%AA%A8-%ED%99%95%EC%9E%A5-vs-%EC%88%98%ED%8F%89%EC%A0%81-%EA%B7%9C%EB%AA%A8-%ED%99%95%EC%9E%A5&quot; aria-label=&quot;수직적 규모 확장 vs 수평적 규모 확장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;수직적 규모 확장 vs 수평적 규모 확장&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;수직적 규모 확장(scale up)&lt;/strong&gt; 은 서버에 고사양 자원을 추가(더 좋은 CPU, 더 많은 RAM 등) 을 추가하는 행위를 뜻한다. 반면 &lt;strong&gt;수평적 규모 확장(Scale Out)&lt;/strong&gt; 은 더 많은 서버를 추가하여 성능을 개선하는 행위를 뜻한다.&lt;/p&gt;
&lt;p&gt;서버로 유입되는 트래픽의 양이 적을때는 수직적 확장이 좋은 선택이다. 하지만, 수직적 확장에는 아래와 같은 심각한 단점이 존재하여 대규모 애플리케이션에는 수평적 확장이 더 적절하다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;한 대의 서버에 CPU 나 메모리를 무한대로 증설할 방법은 존재하지 않기에, 언젠가는 분명 서버 사양을 올리는데 한계점에 도달한다.&lt;/li&gt;
&lt;li&gt;FailOver 이나 다중화 방안을 제시하지 않는다. 서버에 장애가 발생하면 웹사이트는 완전히 중단된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;로드밸런서&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%84%9C&quot; aria-label=&quot;로드밸런서 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로드밸런서&lt;/h3&gt;
&lt;p&gt;앞서 살펴본 설계에서는 사용자가 웹 서버에 직접 바로 연결된다. 이 설계 구조는 웹 서버가 다운될 경우 사용자자는 웹 사이트에 접속할 수 없게도니다. 또한, 트래픽이 너무 많아져서 서버가 한계에 도달하면 Latency 가 증가하거나 접속이 불가능해진다. 이 문제를 해결하기 위해 로드밸런서를 도입해야한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9d5c806ab7d24d37d130cf8dfaa9190b/098d4/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.668711656441715%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABtklEQVR42pVSu4rCUBBNIwhWfouNPyD4B1biD1iIvbCClYUgCCKCWtiIKwjiA/wACy1MJ4pYiBY+8B1dozmbGbiXG7DYPXC4ySRzZ+bM0e6GAaJxu8F8PvFdr6NQKEDEmfc7k/6xXi/o4zE6nQ5+Hg/nfzY12LAsiw5O6na7aDQaOBwOHHu/35JPuyCh1WrhK5mUeSocF67Xa8xmMywWC8znc44dj0fsdjumKBIMBuF2uzGZTGRRx4UC1AF1WKvVcD6fOUbn9XrF5XLBwx6RoOs6hsPh5w5N05QaUQK9n04nqJ3/BxqJG4vFkEqlkEgkWB8xrjqKClVXolpYo5G22y2TNKrbWy6VSvzxZW9UJP25Q/FAyUSxGMO2wKexxft+v0e/32cul0v5TSsWi8jlcqhUKqyfwHQ6RTabRT6fR7vdlgnqhSRRKBTCaDSSUmjlchmRSAS9Xs+RsNlskMlkEI1GeaNUjGIkDZ032+ThcBher5d9K6bUaAkulwvNZlMGRafpdBoej0f6jboiXxLJFfF4HD6fj60mLxwMBggEAtyhqiWhWq3C7/ez79StiylWqxWPqxr/F4YfZ0Ne55zIAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/9d5c806ab7d24d37d130cf8dfaa9190b/a6d36/image-2.png&quot;
        srcset=&quot;/static/9d5c806ab7d24d37d130cf8dfaa9190b/222b7/image-2.png 163w,
/static/9d5c806ab7d24d37d130cf8dfaa9190b/ff46a/image-2.png 325w,
/static/9d5c806ab7d24d37d130cf8dfaa9190b/a6d36/image-2.png 650w,
/static/9d5c806ab7d24d37d130cf8dfaa9190b/e548f/image-2.png 975w,
/static/9d5c806ab7d24d37d130cf8dfaa9190b/3c492/image-2.png 1300w,
/static/9d5c806ab7d24d37d130cf8dfaa9190b/098d4/image-2.png 2142w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;로드밸랜서는 부하 분산 집합(load balancing set) 에 속한 웹 서버들에게 트래픽 부하를 고르게 분산하는 역할을 한다. &lt;strong&gt;사용자는 웹 서버에 직접 접근하는 대신에 로드밸런서의 공개 IP 주소로 접근하게 된다.&lt;/strong&gt; 따라서, 웹 서버는 클라이언트의 접속을 직접 처리하지 않는다. 일반적으로 보안을 위해 서버간 통신에는 사설(비공개) IP 주소를 사용한다. &lt;strong&gt;따라서, 로드밸런서와 웹 서버는 이 사설 주소를 사용하여 통신한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;부하 분산 집합에 또 하나의 웹 서버를 추가하고 나면 FailOver 가 안되는 문제를 해결할 수 있으며, 웹 계층의 가용성(Availability) 은 향상된다. 서버1이 다운되면(offline) 모든 트래픽은 서버2로 전송된다. 따라서 웹 사이트 전체가 다운되는 일이 없어진다. 또한 웹 사이트로 유입되는 트래픽에 가파르게 등장하면, 부하를 분산하기 위해 새로운 서버를 더 추가할 수도 있다.&lt;/p&gt;
&lt;p&gt;이제 웹 계층은 괜찮아졌다. 그렇다면 데이터베이스 서버는 가용성 측면에서 안전한가? 아직 데이터베이스 서버는 한 대이고, FailOver 나 다중화를 지원하지 않는다. 데이터베이스 다중화는 이런 문제를 해결한다.&lt;/p&gt;
&lt;h3 id=&quot;데이터베이스-다중화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%8B%A4%EC%A4%91%ED%99%94&quot; aria-label=&quot;데이터베이스 다중화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 다중화&lt;/h3&gt;
&lt;p&gt;많은 데이터베이스는 다중화를 지원한다. 보통은 서버 사이에 &lt;strong&gt;주(master) - 부(slave) 관계를&lt;/strong&gt; 설정하고 데이터 원본은 주 서버에, 사본은 부 서버에 저장하는 방식으로 구성한다. 또한 쓰기 연산은 master 서버에서만 지원한다. slave 서버는 master 서버로부터 그 사본을 전달받고, 읽기 연산만을 지원한다.&lt;/p&gt;
&lt;p&gt;대부분의 애플리케이션은 읽기 연산의 비중이 쓰기 연산보다 훨씬 높다. 따라서 통상 slave 데이터베이스의 수가 master 서버의 수 보다 많다.&lt;/p&gt;
&lt;p&gt;그렇다면, 데이터베이스 다중화를 통해 어떤 이점을 얻을 수 있을까?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 부하 분산을 통한 성능 개선 : master-slave 다중화 모델에서 쓰기 연산은 master 서버로만 전달디는 반면, 읽기 연산은 slave 서버들로 분산된다. 따라서 병렬로 처리되는 쿼리 수가 늘어나므로 성능이 향상된다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 지리적 분산 : 자연 재해 등의 이유로 데이터베이스 서버 가운데 일부가 파괴되어도 데이터는 보존된다. 지리적으로 떨어져있는 곳으로 서버를 다중화할 수 있기 때문이다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 가용성 : 데이터를 여러 지역에 복제해 둠으로써, 하나의 데이터베이스 서버에 장애가 발생하더라도 다른 서버에 있는 데이터를 가져와 계속 서비스 할 수 있게된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;또한 데이터베이스를 다중화한다면 아래와 같은 상황에 대처할 수 있게 된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;한 대뿐인 slave 서버가 다운되었다면, 읽기 연산은 일시적으로 master 서버에게 전달된다. 또한 즉시 새로운 slave 서버를 올려서 장애 서버를 대체(FailOver) 할 수 있을 것이다. slave 서버가 여러대인 경우라면 읽기 연산이 정상 동작하는 slave 서버들에게 분산되고, 이후 새로운 master 서버가 장애 서버를 대체할 수 있을 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;master 서버가 다운되었고, slave 서버가 1대뿐인 경우 : slave 서버가 새로운 master 서버로 승격하고, 일시적으로 읽기/쓰기 연산을 모두 처리한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;프로덕션 환경에서 벌어지는 일은 이것보다는 사실 더 복잡하다고 한다. slave 서버에 보관된 데이터가 최신 상태가 아닐 수 있기 때문이다. 없는 데이터는 복구 스크립트를 돌려서 추가해야 한다. &lt;strong&gt;다중 마스터(Multi Master) 나 원형 다중화(Circular Replica-tion)&lt;/strong&gt; 방식을 도입하면 이 복잡한 상황에 대처할 수 있는데, 이는 너무 어려운 내용이라 책에서 다루지 않는다.&lt;/p&gt;
&lt;h2 id=&quot;캐시&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BA%90%EC%8B%9C&quot; aria-label=&quot;캐시 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;캐시&lt;/h2&gt;
&lt;p&gt;캐시란 값 비싼 연산 결과 또는 자주 참조되는 데이터를 메모리 안에 두고, 뒤이은 요청이 보다 빨리 처리될 수 있도록 하는 저장소다. 애플리케이션 성능은 &lt;strong&gt;데이터베이스를 얼마나 자주 호출하느냐에 따라 크게 좌우&lt;/strong&gt;되는데, 캐시는 그런 문제를 완화할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;캐시-계층cache-tier&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BA%90%EC%8B%9C-%EA%B3%84%EC%B8%B5cache-tier&quot; aria-label=&quot;캐시 계층cache tier permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;캐시 계층(cache tier)&lt;/h3&gt;
&lt;p&gt;캐시 계층은 데이터가 잠시 보관되는 곳으로 데이터베이스보다 훨씬 빠르다. 데이터베이스는 데이터를 디스크에 저장하고, 캐시는 RAM 에 저장하기 때문이다. 별도의 캐시 계층을 두면 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 성능이 개선되고 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 데이터베이스 부하가 줄어들고 &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 캐시 계층을 독립적으로 확장할 수 있다.&lt;/p&gt;
&lt;h4&gt;읽기 주도형 캐시 전략&lt;/h4&gt;
&lt;p&gt;웹 서버는 캐시 서버에 데이터가 있다면(Cache Hit) 캐시 서버에서 데이터를 읽는다. 반대로 없다면(Cache Miss) 데이터베이스에서 데이터를 읽고, 캐시에 저장한 뒤 사용자에게 응답을 반환한다. 이런 캐시 전략을 **읽기 주도형 캐시 전략(read-through caching strategy)**이라고 한다. 이 외에도 데이터 종류, 크기, 엑세스 패턴에 맞는 다양한 캐시 전략이 있다고 한다.&lt;/p&gt;
&lt;h3 id=&quot;캐시-사용시-유의할-점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BA%90%EC%8B%9C-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%9C%A0%EC%9D%98%ED%95%A0-%EC%A0%90&quot; aria-label=&quot;캐시 사용시 유의할 점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;캐시 사용시 유의할 점&lt;/h3&gt;
&lt;p&gt;캐시를 사용할 때는 아래 사항들을 고려해야 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;캐시는 어떤 상황에 바람직한가? : 데이터 갱신은 자주 일어나지 않지만 참조가 빈번할 때&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;어떤 데이터를 캐시에 두어야 하는가? : 캐시는 데이터를 휘발성 메모리에 두므로, 영속적으로 보관할 데이터는 캐시에 두지 않는 것이 바람직하다. 중요한 데이터는 데이터베이스와 같이 &lt;strong&gt;지속적 저장소(persistent data store)&lt;/strong&gt; 에 두어야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;캐시에 보관된 데이터는 어떻게 만료(expire) 되는가? : 적절한 만료 정책이 필요하다. 만료된 데이터는 캐시에서 사라져야 한다. 만료 정책이 없으면 데이터는 영원히 캐시를 점유하게 된다. 만료 기한은 너무 짧으면 곤란한데, 데이터베이스를 너무 자주 읽게 될 것이기 때문이다. 너무 길어도 곤란한데, 실제 원본 데이터와 캐싱된 데이터간에 차이가 날 수 있기 떄문이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;일관성(consistency)은 어떻게 유지되는가? : 일관성이란 데이터 저장소의 원본과 캐시 내의 사본이 같은지 여부이다. &lt;strong&gt;저장소의 원본을 갱신하는 연산과 캐시를 갱신하는 연산이 단일 트랜잭션으로 처리되지 않는 경우, 이 일관성이 깨질 수 있다.&lt;/strong&gt; 원본 데이터 갱신을 성공하고, 캐시 갱신에는 실패했을 때 이 두 연산이 단일 트랜잭션으로 묶이지 않았다면 원본과 캐시의 일관성이 깨진다. 여러 지역에 걸쳐 시스템을 확장하는 경우 캐시와 저장소 사이의 일관성을 유지하는 것은 어려운 문제이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;장애에는 어떻게 대처할 것인가? : 케시 서버를 한 대만 배치하면 &lt;strong&gt;SPOF(Single Point Of Failure)&lt;/strong&gt; 가 될 수 있다. SPOF 를 피하려면, 여러 지역에 걸쳐 캐시 서버를 분산해야한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;캐시 메모리를 얼마나 크게 잡을 것인가? : 캐시 메모리가 너무 작다면, 엑세스 패턴에 따라 &lt;strong&gt;캐시 메모리에서 너무 자주 밀려나버려(eviction)&lt;/strong&gt; 성능이 떨어지게 된다. 이를 막을 한 가지 방법은, 캐시 메모리를 &lt;strong&gt;과할당(over-provision)&lt;/strong&gt; 하는 것이다. 갑자기 캐시에 보관할 데이터가 많아졌을 때 생길 문제를 방지할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;데이터 방출(eviction) 정책은 무엇인가? : 캐시가 꽉 차버리면 추가로 캐시에 데이터를 넣어야 할 경우 기존 데이터를 캐시에서 내보내야 한다. 이것을 캐시 데이터 방출 정책이라고 하는데, 가장 널리 쓰이는건 마지막으로 사용된 시점이 가장 오래된 데이터를 먼저 내보내는 &lt;strong&gt;LRU(Least Recently Used)&lt;/strong&gt; 이다. 다른 정책으로는 사용된 빈도가 가장 낮은 데이터를 내보내는 &lt;strong&gt;LFU(Least Frequently Used)&lt;/strong&gt; 이고, 가장 먼저 캐시에 들어온 데이터를 가장 먼저 내보내는 정책인 &lt;strong&gt;FIFO(First In First Out)&lt;/strong&gt; 도 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;콘텐트-전송-네트워크-cdn&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BD%98%ED%85%90%ED%8A%B8-%EC%A0%84%EC%86%A1-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-cdn&quot; aria-label=&quot;콘텐트 전송 네트워크 cdn permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;콘텐트 전송 네트워크 (CDN)&lt;/h2&gt;
&lt;p&gt;CDN 은 정적 콘텐츠(HTML, JS 파일, 이미지, 비디오 등) 를 전송하는데 쓰이는, 지리적으로 분산된 서버의 네트워크이다. 사용자가 네트워크를 방문하면, 그 사용자에게 가장 가까운 CDN 서버가 정적 콘텐츠를 전달한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2187eec94587848794479d87754fe63b/6e29b/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.282208588957054%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABiklEQVR42o1Sy8qBURQ9Yy/iBTyHmZFXMGYo3sJAyQApmUhKSSl9IxEpiqFrLrnfl3/tOqdD9Fu1+75z9j7rrL32UfhDu91GMBhENpvlEo/Hw3ztuN/vst9sNpFKpZDP55FMJtHpdEy94k+j0UA4HBZCTfYJmrBYLCIUCiEajSISiaBarZq8ul6vcBwHm80G/X4fw+FQkrfbDavVSmK9XmM+n2O325kLS6USPB4PMpnMS1eKBwuFAur1OiqVCnq9niTO5zO22y32+70EyS6Xi1GZSCSglILP55Pal5a73S5isRjK5TKm06kkj8cjFouFqKN6KuVBTUgPXS4X/H6/scQQ6hapiCTL5dIcZBGJmLfBvclkIrU2lD1N+knC2WwmnhGcYCAQEDtsr75B2XJ1sEXezvZPpxNyuRxarZaZ5Ptzsi9R7zfopB5IOp2G2+1GrVYz1vyk8BMpEY/H4fV6MRgMZM1pj0Yj8Y4dMMbjsbyAr4Q2KVukt/bwSEorDoeDBNdaucI/0J79iifpOIAIjfFz3wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/2187eec94587848794479d87754fe63b/a6d36/image-3.png&quot;
        srcset=&quot;/static/2187eec94587848794479d87754fe63b/222b7/image-3.png 163w,
/static/2187eec94587848794479d87754fe63b/ff46a/image-3.png 325w,
/static/2187eec94587848794479d87754fe63b/a6d36/image-3.png 650w,
/static/2187eec94587848794479d87754fe63b/e548f/image-3.png 975w,
/static/2187eec94587848794479d87754fe63b/3c492/image-3.png 1300w,
/static/2187eec94587848794479d87754fe63b/6e29b/image-3.png 1610w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;참고로 동적 콘텐츠 캐싱은 요청 경로, Query String, 쿠기, 요청 헤더등의 정보에 기반하여 HTML 페이지를 캐싱하는 것이다. 이는 책에서 다루지 않는다.&lt;/p&gt;
&lt;p&gt;CDN 은 어떻게 동작할까? 어떤 사용자가 웹사이트를 방문하면, &lt;strong&gt;그 사용자에게 가장 가까운 CDN 서버가 정적 콘텐츠를 전달&lt;/strong&gt;하게 된다.당연한 말이지만, CDN 서버가 서버로부터 멀면 벌수록 웹 사이트는 천천히 로딩될 것이다. 세부 동작 과정은 보통 다음과 같이 동작하게 된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 사용자가 이미지 URL 을 이용해 이미지에 접근한다. 이 URL 의 도메인은 CDN 서비스 사업자가 제공한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; CDN 서버에 해당 이미지가 없는 경우, CDN 서버는 원본 서버에 요청에 이미지 파일을 가져온다. 이때 원본 서버는 웹 서버일수도 있고, AWS S3 와 같은 저장소일 수도 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 원본 서버가 CDN 서버에 파일을 리턴한다. HTTP 응답 헤더에는 해당 파일이 얼마나 오래 캐시될 수 있는지 나타내는 &lt;strong&gt;TTL(Time-To-Live)&lt;/strong&gt; 값이 들어있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; CDN 서버는 파일을 캐시하고 사용자에게 리턴한다. 이 이미지 파일은 TTL 에 명시된 시칸만큼 캐시된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; 다른 사용자가 동일한 이미지를 CDN 서버에 요청한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(6)&lt;/code&gt; 해당 이미지가 만료되지 않았다면 CDN 서버가 캐시해 둔 파일을 리턴한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;cdn-사용시-고려해야-할-사항&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cdn-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC-%ED%95%A0-%EC%82%AC%ED%95%AD&quot; aria-label=&quot;cdn 사용시 고려해야 할 사항 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CDN 사용시 고려해야 할 사항&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;비용 : CND 은 보통 제 3사업자에 의해 운영되며, 데이터 전송량에 따라 요금을 지불한다. 따라서 자주 사용되지 않는 컨텐츠를 캐싱하는 것은 비용 낭비이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;적절한 만료 시한 설정 : 시의성이 중요한 (time-sensitive) 컨텐츠는 만료 시점을 잘 설정하는게 중요하다. 만료 시한이 너무 길면 컨텐츠의 신선도가 떨어지고, 너무 짧으면 원본 서버에 빈번한 접속 요청이 들어올 것이다. 캐시 서버의 만료 정책과 비슷하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CDN 장애에 대한 대처 방안 : CDN 이 응답하지 않는 경우 이 문제를 감지하여 원본 서버로부터 컨텐츠를 가져올 수 있도록 클라이언트를 구성할 필요가 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;컨텐츠 무효화(invalidation) 방법 : 아직 만료되지 않은 컨텐츠라고 하더라도, 아래 방법을 통해 CDN 에서 제거할 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; CDN 서비스 사업자가 제공하는 API 를 사용한 무효화&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 컨텐츠의 다른 버전을 서비스하도록 오브젝트 비저닝을 이용한다. 컨텐츠의 새로운 버전을 지정하기 위해서는 URL 마지막에 버전 번호를 인자로 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;지금까지의 시스템 설계를 그려보면 아래와 같을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/24589517b513063e763a81c8e760e18c/098d4/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.05521472392638%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB4ElEQVR42mWTS8txURTHKZ/AWGbMjEz5Bj4AQ+UjKIqUTJWZzFxKDFwnUq4DM8mAIsklcsslcr/939Z6O+fxeFbtztnrrP1b/7X2OpLdbodQKITZbAayxWIBl8uFRqPB+9frBcHe7zc/x+MxEokErtfrLz+ZZDqdwm63Y7VasaPf76PX66HT6TCMgukgrePxyDHRaBRarZZjv5NKCoUCDAYD9vs9ns8n6vU6HyiVShxwuVywXC454Xw+Z5/P54NGo0G73f4LJAWCQ5A+GAwYJASTX/hGStfrtVjutzGwWq0inU4jmUyiXC6D+kpqP00AUq/NZjPy+fwfdQwkRy6XQywWQzweRyqVQjAYZKgA+lT4eDxgMpnESxsOh9xvUs3Ab8mHwwGZTIaDPkFkp9OJE41GIy6Z3gOBAPeUqmTgZDJBpVJBNptFt9sVVdBqNpuo1WpotVoMO5/PYol0SXq9HjqdDsVi8adkCna73fB4PHzDQl/u9zsikQicTif8fj+roSRUwe1249FSq9WQSqVQKBRwOBwsiEu2Wq2cjQI/G019UalUCIfD4n673TKcpoBGy2g0Mlgmk8Hr9f4H2mw2KJVKbDYbPijcMJUll8s5uzAy9I2gtAhK80t/msVi4Qn5B3w5Adj6ZxydAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/24589517b513063e763a81c8e760e18c/a6d36/image-5.png&quot;
        srcset=&quot;/static/24589517b513063e763a81c8e760e18c/222b7/image-5.png 163w,
/static/24589517b513063e763a81c8e760e18c/ff46a/image-5.png 325w,
/static/24589517b513063e763a81c8e760e18c/a6d36/image-5.png 650w,
/static/24589517b513063e763a81c8e760e18c/e548f/image-5.png 975w,
/static/24589517b513063e763a81c8e760e18c/3c492/image-5.png 1300w,
/static/24589517b513063e763a81c8e760e18c/098d4/image-5.png 2142w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;무상태stateless-웹-계층&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B4%EC%83%81%ED%83%9Cstateless-%EC%9B%B9-%EA%B3%84%EC%B8%B5&quot; aria-label=&quot;무상태stateless 웹 계층 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;무상태(stateless) 웹 계층&lt;/h2&gt;
&lt;p&gt;이제 웹을 수평 확장(Scale Out) 하는 방법을 고민해 볼 차례이다. 이를 위해서는 사용자 세션 데이터와 같은 상태 정보를 웹 계층에서 제거해야한다.&lt;/p&gt;
&lt;h4&gt;상태 정보에 의존적인 아키텍처&lt;/h4&gt;
&lt;p&gt;상태정보를 보관하는 서버는 클라이언트 정보, 즉 상태를 유지하여 요청들 사이에 공유(쿠기와 세션등을 사용하여) 되도록 해야한다. 무상태 서버에는 이런 장치가 없다.&lt;/p&gt;
&lt;p&gt;문제는 같은 클라이언트로부터의 요청은 항상 같은 서버로 전송되어야 한다는 점이다. 대부분의 로드밸런서가 이를 해결하기 위해 &lt;strong&gt;고정 세션(Sticky Seession)&lt;/strong&gt; 기능을 제공하고 있는데, 이는 로드밸런서에 큰 부담을 준다. 게다가 로드밸런서 뒷단에 서버를 추가하거나 제거하기도 까다로워진다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;사실 왜 로드밸런서 뒷단에 서버를 추가하거나 제거하기도 까다로워지는지 잘 이해되지 않아서, 이는 추가 학습이 필요하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;무상태-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B4%EC%83%81%ED%83%9C-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;무상태 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;무상태 아키텍처&lt;/h3&gt;
&lt;p&gt;무상태 아키텍처는 세션과 같은 상태 정보를 별도의 &lt;strong&gt;공유 저장소(Shared Storage)&lt;/strong&gt; 로 부터 데이터를 저장하고 가져온다. 즉, 상태 정보는 웹 서버로부터 물리적으로 분리되어 있다. 이런 구조는 단순하고, 안정적이고, 규모 확장이 쉽다. 이 공유 저장소는 Redis 와 Mecached 같은 캐시 서버를 사용할 수도 있고, NoSQL 서버를 사용할 수도 있다. 다만, 책에서는 규모 확장이 간편한 NoSQL 을 선택했다고 한다.&lt;/p&gt;
&lt;p&gt;세션과 같은 상태를 Redis 와 같은 별도의 상태 저장소에 보관하면 &lt;strong&gt;오토스케일링(AutoScaling)&lt;/strong&gt; 도 가능해진다. 오토스케일링은 트래픽 양에 따라 유연하게 웹 서버를 자동 추가하거나 삭제하는 기능이다. 상태 정보가 각 웹 서버에서 관리하는 방식이 아니므로, 트래픽 양에 따라 웹 서버를 넣었다 뺄 수 있는 유연한 구조로 개선된다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/afbaf12fbdb2b17b1dc3b312442f7fe2/2b984/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.44171779141104%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACKUlEQVR42mWSTYtSYRTH1e/gwo24E9sEgYswKPJTBLaMWgQuGhhc+EpCTDlTENLGlxEchQR1GSiIzCguNDOQlFETJ0V8z3ev+s/zyL3j0APn3ufc5z6/8z8vosViAbfbjXq9DlqdTgcmkwmZTIb5m80G/Nput+xdrVYRDofvnPFLNBwOYTabBWCtVkOpVGLGcRyDLJdLZpPJhP1zfu7Fk8eP0Pxzsw+04SjaHkhKtFotU0YRs9ksgsEgYrEY8+fzOVqtFtrtNgaDAbtke/sO9+4/ROm69l8WIlKwXq/vpFQulzEej9mezuj7arVC6uoSP37+AkbfgZsvwCwMDALYXn8Cpnu4iB6pVIrVhCwej6Pf7wtBDgMdHb3B2cfPu10L899fYXzxAGfHT3fQbwDXvQVSen6/H4FAAKFQCB6PB71eT4Dx8JOT97i48LN9uzvAs+cv8er1Mcq1NvrDv7fAQxXT6RTRaBTFYlH4Rs2hEtB7OBrt9nR5i9lsBq/XjdPTD0in03tgs9lkaUYiERQKBQFAqqhBiUSC/UzNIaNu89OgVquh0WiQTCYFUaJKpQK73Q6r1cpqyXeNjMpgMBjgdDox2ikjGCmlBuVyOahUKojFYsjlclgsFhCLpWyz2dghXTocA/JlMhlcLhfzu90uGx2aXVJLtdfpdFAqlZBIJPD5fHugw+GAQqFAo9EQRoUHSqVSGI1G5pNCOqMpIKMaEpxAer0e+Xwe/wD6Zfhow0kSAQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/afbaf12fbdb2b17b1dc3b312442f7fe2/a6d36/image-6.png&quot;
        srcset=&quot;/static/afbaf12fbdb2b17b1dc3b312442f7fe2/222b7/image-6.png 163w,
/static/afbaf12fbdb2b17b1dc3b312442f7fe2/ff46a/image-6.png 325w,
/static/afbaf12fbdb2b17b1dc3b312442f7fe2/a6d36/image-6.png 650w,
/static/afbaf12fbdb2b17b1dc3b312442f7fe2/e548f/image-6.png 975w,
/static/afbaf12fbdb2b17b1dc3b312442f7fe2/3c492/image-6.png 1300w,
/static/afbaf12fbdb2b17b1dc3b312442f7fe2/2b984/image-6.png 2164w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜 오토스케일링을 가능하게 만드는지에 대한 추가 학습이 필요하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;데이터-센터&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%84%BC%ED%84%B0&quot; aria-label=&quot;데이터 센터 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터 센터&lt;/h2&gt;
&lt;p&gt;만약 우리 서비스가 전 세계적으로 사용하게 되었다면 어떨까? &lt;strong&gt;가용성을 높이고 전 세계 어디서든 빠르게 사용하기 위해 데이터 센터를 지원&lt;/strong&gt;해야한다.&lt;/p&gt;
&lt;p&gt;지리적으로 분리된 2개의 데이터 센터가 있다고 해보자. 장애가 없는 상황에서 사용자는 가장 가까운 데이터 센터로 라우팅된다. 이 절차를 &lt;strong&gt;지리적 라우팅(geoDNS-routing 또는 geo-routing)&lt;/strong&gt; 이라고 한다. 지리적 라우팅에서 geoDNS 는 사용자의 위치에 따라 도메인 이름을 어떤 IP 주소로 변환할지 결정해주는 DNS 서비스이다. 만약 데이터 센터 중 하나에 장애가 발생하면, 모든 트래픽은 장애가 발생안한 다른 데이터 센터로 라우팅된다.&lt;/p&gt;
&lt;p&gt;다중 데이터 센터를 구축하려면 몇 가지 기술적 난제를 해결해야한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;트래픽 우회 : 올바른 데이터 센터로 트래픽을 보내는 효과적인 방법을 찾는다. &lt;strong&gt;geoDNS 는 사용자에게서 가장 가까운 데이터 센터로 트래픽을 보낼 수 있도록&lt;/strong&gt; 해준다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;데이터 동기화 : 데이터 센터마다 별도로 데이터베이스를 사용하고 있는 상황이라면, 장애가 자동으로 복구되어(failover) 트래픽이 다른 데이터베이스로 우회된다 해도, 해당 데이터 센터에는 찾는 데이터가 없을 수 있다. 이런 상황을 방지하는 보편적 전략은, 동일한 데이터를 여러 데이터센터에 걸쳐 똑같이 다중화 하는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/37ef35401d8a6b4435d648ea84fecdc9/95fa1/image-7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.48466257668711%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACDUlEQVR42o1TPYsaURSd0trG3trCxj+wwU5B0B/gX7CSRJAkWAopElCwFUtZdCUGwqYQ/CKFX6AoinEWxW8z6OrM6JzMvZsZNpCsOfBm3uNdzj333PsETdNAqFaraLVavFdVFZlMBv1+HwYOhwMKhQLO5zNegkCfy+WCSCSCUqmE4/GI1WqFSqWC0WiE+XyO5XKJXq8Ht9vNZ4Ih5K+EiiIjn89jNpuaCpvNJhORMlqS9FNPUubYqwqfgzIrisJK9vs9q/99g/+BwIGaCkV+hKqcmCCbzSIejyOZSGAymXCgIh9xVnV12uUKobwCBh+AH5+A9R1Op5Pu44n9fHgQoah61KPeHPHjU9z2+4uKBZC5Zxn9XgdfPt+h025z2d1uF9vtFtPpFPf3X1EufYMqH66Wbnr4+k0Eb9+9x26347MsP5k/Ho9xc/MKt7c50+N/dZgJjUuv1wur1Yq2rpCw2Wz4P5vNIAgCfD7fH40jkD1UxfPZNAkHgwH8fj9qtRqfaWSM4GQyiWAwyETr9dqsolwuIxwOc6yRiEvudDpI6B0l3+g11Ot1MyspbjQaWCwWyOVyHEN7Io7FYrDb7RgOhzAeCBOmUikuy2KxwOPxQBRFnkVCIBDgO4fDgWg0yupo8OlFSZLEy/DbbAoNMWVzOp0IhUJ8YSgsFotwuVyw2WxIp9OmHaSQfKY9LcO6X6SaqMs1CUZmAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/37ef35401d8a6b4435d648ea84fecdc9/a6d36/image-7.png&quot;
        srcset=&quot;/static/37ef35401d8a6b4435d648ea84fecdc9/222b7/image-7.png 163w,
/static/37ef35401d8a6b4435d648ea84fecdc9/ff46a/image-7.png 325w,
/static/37ef35401d8a6b4435d648ea84fecdc9/a6d36/image-7.png 650w,
/static/37ef35401d8a6b4435d648ea84fecdc9/e548f/image-7.png 975w,
/static/37ef35401d8a6b4435d648ea84fecdc9/3c492/image-7.png 1300w,
/static/37ef35401d8a6b4435d648ea84fecdc9/95fa1/image-7.png 1816w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;메시지-큐&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%94%EC%8B%9C%EC%A7%80-%ED%81%90&quot; aria-label=&quot;메시지 큐 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메시지 큐&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;시스템을 더 큰 규모로 확장하기 위해서는 시스템의 컴포넌트를 분리하여, 각기 독립적으로 확장될 수 있도록 설계&lt;/strong&gt;해야한다. 메시지 큐(message queue) 는 많은 실제 분산 시스템이 이 문제를 해결하도록 지원한다.&lt;/p&gt;
&lt;p&gt;메시지 큐는 비동기 통신을 지원하는 컴포넌트잉다. 메시지 큐는 무손실을 보장하는 특징이 있는데, 이는 메시지 큐에 한번 저장된 메시지는 소비자가 꺼낼때까지 안전히 보관된다는 점이다. 메시지 큐는 메시지의 버퍼 역할을 하며, 비동기적으로 전송한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ProduceR (또는 Publisher)&lt;/strong&gt; 라고 불리는 서비스가 메시지를 만들어 메시지 큐에 메시지를 발생(publish) 한다. 메시지 큐에는 &lt;strong&gt;Consumer 또는 Subscriber&lt;/strong&gt; 라고 불리는 서비스 또는 서버가 연결되어 있는데, 메시지를 받고 동작을 수행한다.&lt;/p&gt;
&lt;p&gt;메시지 큐를 사용하면 서비스 또는 서버 간 결합이 느슨해져서, 규모 확장성이 보장되어야 하는 안정적인 애플리케이션을 구성하기 좋다. 또, 결함에 대한 내성을 높여준다. &lt;strong&gt;생산자는 소비자가 다운되어 있을 때도 메시지를 발행할 수 있고, 반대로 소비자는 생산자 서비스가 다운되어 있더라도 메시지를 수신&lt;/strong&gt;할 수 있다. 즉, 생산자 소비자는 서로의 서버가 살아있던 죽어있던 간에 상관없이 메시지를 발행하기만 하면 되기 떄문에, 비동기적으로 동작할 수 있다는 것이고 서버간 결합이 느슨해지며 규모를 확장하기 쉽다는 것이다.&lt;/p&gt;
&lt;p&gt;사용 예로 시간이 오래 걸리는 이미지 보정 작업을 들 수 있다. 생산자인 웹 서버는 이미지 보정 작업을 메시지 큐에 넣는다. 사진 보정 작업 프로세스는 메시지 큐에서 메시지를 꺼내 해당 비동기적으로 이미지 보정 작업을 수행한다. 이런 구조에서는 생산자와 소비자 서비스의 규모는 각기 독립적으로 확장될 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;로그-메트릭-그리고-자동화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EA%B7%B8-%EB%A9%94%ED%8A%B8%EB%A6%AD-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%9E%90%EB%8F%99%ED%99%94&quot; aria-label=&quot;로그 메트릭 그리고 자동화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로그, 메트릭 그리고 자동화&lt;/h2&gt;
&lt;p&gt;소규모 서비스에서는 로그, 메트릭(metric), 자동화(automation)은 좋지만 필수는 아니다. 하지만 사업 규모가 커지면 이런 도구에도 투자해야한다.&lt;/p&gt;
&lt;h2 id=&quot;데이터베이스의-규모-확장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EA%B7%9C%EB%AA%A8-%ED%99%95%EC%9E%A5&quot; aria-label=&quot;데이터베이스의 규모 확장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스의 규모 확장&lt;/h2&gt;
&lt;p&gt;저장할 데이터가 많아지면 데이터베이스에 대한 부하도 커진다. 그때는 데이터베이스를 증설할 방법을 찾아야한다. 데이터베이스 규모를 확장하는 방법에는 수직적 확장과 수평적 확장 2가지로 나뉜다.&lt;/p&gt;
&lt;h3 id=&quot;수직적-확장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%98%EC%A7%81%EC%A0%81-%ED%99%95%EC%9E%A5&quot; aria-label=&quot;수직적 확장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;수직적 확장&lt;/h3&gt;
&lt;p&gt;데이터베이스 서버의 CPU, RAM, 디스크와 같은 자원을 증설하는 방법이다. 하지만, 이 방법에는 몇가지 심각한 단점이 존재한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버 하드웨어를 무한 증설할 수 없으므로 한계점에 언제가 반드시 도달한다.&lt;/li&gt;
&lt;li&gt;하나의 데이터베이스를 수직 확장하면 SOF 로 인한위험성이 크다.&lt;/li&gt;
&lt;li&gt;고성능 서버로 갈수록 가격이 올라가므로 비용이 많이 발생한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;수평적-확장---데이터베이스-샤딩&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%98%ED%8F%89%EC%A0%81-%ED%99%95%EC%9E%A5---%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%83%A4%EB%94%A9&quot; aria-label=&quot;수평적 확장   데이터베이스 샤딩 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;수평적 확장 - 데이터베이스 샤딩&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 데이터베이스 샤딩에 대한 자세한 이론은 &lt;a href=&quot;https://haon.blog/database/partitioning-sharding/&quot;&gt;데이터베이스 분산 스토리지를 위한 파티셔닝과 샤딩&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;데이터베이스의 수평적 확장을 &lt;strong&gt;샤딩(Sharding)&lt;/strong&gt; 이라고 한다. 더 많은 서버를 증설함으로써 성능을 개선할 수 있다. 샤딩은 대규모 데이터베이스를 &lt;strong&gt;샤드(shared)&lt;/strong&gt; 라는 작은 단위로 분할한다. 모든 샤드는 같은 스키마를 사용하지만, 샤드에 보관되는 데이터 사이에는 중복이 없다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/73f4aacff9c1d6c01c75b1c281e5e981/1320e/image-8.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.89570552147239%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABIUlEQVR42p2TfY+DIAzG/f4f8i65mdsfzhfEASLY61NXtngzWSR5IrTwa2mxooOxrqsIo207GsdR5jk/7e9GdeSY55khlpqmYd2o6zoBD4OhlJZzwBCCgJy7yxwwaydalnQO6L2XrKZpYqijW9s+MjwBzDlzJstDkQME+cYYxfcREMVOKRfhoJ8TfdcNfbFCzLzn6YP2Dar214SQifeB5ciHSD/1L13qq8xxdexBpvo9BKJmGhF1wgHNXAeao1fGF+u3QDha7uidMwAY767ve6mhXg8B0HVrrexBs7COcfkPxAYAtKPGGO7oUJqALLenM0gw57yAcUZvUoBbM1L5O/bSDBX8ans9W4AwIBoiQwYym0a2qw+PWmzFbzbxGqUB8A9Omq73vsFZWgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/73f4aacff9c1d6c01c75b1c281e5e981/a6d36/image-8.png&quot;
        srcset=&quot;/static/73f4aacff9c1d6c01c75b1c281e5e981/222b7/image-8.png 163w,
/static/73f4aacff9c1d6c01c75b1c281e5e981/ff46a/image-8.png 325w,
/static/73f4aacff9c1d6c01c75b1c281e5e981/a6d36/image-8.png 650w,
/static/73f4aacff9c1d6c01c75b1c281e5e981/e548f/image-8.png 975w,
/static/73f4aacff9c1d6c01c75b1c281e5e981/1320e/image-8.png 1292w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;샤딩 방식에는 여러가지가 있다. 그 중 &lt;strong&gt;해시 샤딩(hash sharding)&lt;/strong&gt; 의 경우 샤드의 개수에 따라서 해시 함수를 사용하여 데이터가 보관되는 샤드를 결정한다. 예를들어 샤드가 4개라면, &lt;code class=&quot;language-text&quot;&gt;user_id % 4&lt;/code&gt; 를 해시 함수로 사용하여 데이터가 보관됭 샤드를 결정할 수 있다.&lt;/p&gt;
&lt;p&gt;샤딩 전략에서 가장 중요하게 고려한 것은 &lt;strong&gt;샤딩 키(sharding key, 즉 Partitioning Key)&lt;/strong&gt; 를 어떻게 정하는가에 있다. 샤딩 키는 데이터가 어떤 샤드에 저장되는지 결정하는 하나 이상의 컬럼으로 구성된다. 위 예제의 경우 user_id 가 샤딩 키이다. 샤딩 키를 정할때는 데이터가 고르게 분산될 수 있도록 하는것이 중요하다.&lt;/p&gt;
&lt;p&gt;샤딩을 도입하면 시스템이 복잡해지고 풀어야할 새로운 문제도 생긴다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;데이터의 재 샤딩(re-sharding)&lt;/strong&gt; : 데이터가 너무 많아져서 하나의 샤드로는 감당이 어렵거나, 또는 균등하게 데이터 분포가 되지 않아 특정 샤드에 데이터가 몰려 할당된 공간 소모가 다른 샤드에 비해 빠르게 될 때 재 샤딩을 고려해야한다. 이를 &lt;strong&gt;샤드 소진(shard exhaustion)&lt;/strong&gt; 이라고 하는데, 이런 현상을 해결하기 위해 샤드 키를 계산하는 함수를 변경하고 재배치 해야한다. (이는 안정 해시라는 기법을 사용하면 해결할 수 있다고 한다.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;유명인사(celebrity) 문제&lt;/strong&gt; : 핫스팟 키 문제라고도 부른다. 특정 샤드에 질의가 집중되어 서버에 과부하가 걸리는 문제이다. 페이스북 같은 서비스에서 유명 인사 유저가 모두 한 샤드에 집중적으로 저장되어 있다면 해당 샤드에만 과부하가 걸릴 것이다. 이를 해결하기 위해 유명인사 각각에 샤드 하나씩을 할당해야 할 수도 있고, 더 잘게 샤드를 쪼개야할 수도 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;조인과 비정규화(join and de-normalization)&lt;/strong&gt; : 하나의 데이터베이스를 여러 서버로 쪼개면, 여러 샤드에 걸친 데이터를 조인하기 힘들다. 이를 해결하는 방법 중 하나는 데이터베이스를 비정규화 하여 조인 없이 하나의 테이블에서 질의가 수행될 수 있도록 하는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;지금까지 학습한 내용을 정리하면 다음과 같다.&lt;/p&gt;
&lt;ul&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;정적 컨텐츠는 CDN을 통해 서비스할 것&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;h2 id=&quot;더-학습해야-할-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EC%95%BC-%ED%95%A0-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해야 할 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해야 할 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Sticky Session&lt;/li&gt;
&lt;li&gt;왜 상태를 별도의 저장소에 저장하면 부하 분산 집합내의 &lt;strong&gt;AutoScaling&lt;/strong&gt; 이 가능해지는가?&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[스프링부트 애플리케이션 run 메소드가 실행되기까지의 동작원리]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/spring/run-inner-implementation/</link><guid isPermaLink="false">https://haon.site/haon/spring/run-inner-implementation/</guid><pubDate>Fri, 25 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;지난 프론트 컨트롤러 구현에 이어서, 스프링부트 애플리케이션을 실행하기 까지의 전 과정에 대해 다루어보고자 한다. 또한 한 단계씩 거쳐가며 애플리케이션 로직을 리팩토링 해보겠다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;프론트 컨트롤러에 대한 설명은 &lt;a href=&quot;https://velog.io/@msung99/%ED%94%84%EB%A1%A0%ED%8A%B8-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC%EB%A5%BC-%EA%B5%AC%ED%98%84%ED%95%98%EC%97%AC-%EA%B3%B5%ED%86%B5-%EC%9B%B9-%EC%9A%94%EC%B2%AD%EC%9D%84-%EC%B2%98%EB%A6%AC%ED%95%B4%EB%B3%B4%EC%9E%90&quot;&gt;서블릿 컨테이너로 프론트 컨트롤러(Front Controller) 를 구현하여 공통 요청을 처리해보자!&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;assembler&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#assembler&quot; aria-label=&quot;assembler permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Assembler&lt;/h2&gt;
&lt;p&gt;스프링 컨테이너는 DI 컨테이너로써 &lt;code class=&quot;language-text&quot;&gt;Assembler&lt;/code&gt; 역할을 수행한다. &lt;code class=&quot;language-text&quot;&gt;Assembler&lt;/code&gt; 란 원래는 직접 의존관계가 없는 클래스들의 오브젝트를 가져다가 가져다가 서로 관계를 연결시켜주고 사용 가능하도록 만들어주는 &lt;strong&gt;&quot;제 3자&quot;&lt;/strong&gt; 역할이다.&lt;/p&gt;
&lt;p&gt;스프링 컨테이너는 우리가 제공한 메타 정보를 기반으로 클래스의 싱글톤 Bean 오브젝트를 생성하고, &lt;strong&gt;각 오브젝트간의 의존 관계를 주입(연결) 해주는 작업을 수행한다.&lt;/strong&gt; 이때 관례적으로 Bean 오브젝트 간의 의존관계를 생성하는 것이 &quot;생성자 주입&quot; 일텐데, 생성자의 파라미터는 인터페이스로 추상화를 시켜놓는다.&lt;/p&gt;
&lt;p&gt;인터페이스로 파라미터 객체를 받는 이유는 유연한 확장을 위함일 것이다. 개발자의 구현 코드 레벨에선 인터페이스로 추상화를 시켜놓고 확장을 위해 여러 구체 클래스를 만들었을텐데, 이 구체 클래스로 어떤 것을 &quot;주입&quot; 시켜줄지를 스프링 컨테이너에 등록해주는 것이다. 특정 Bean 클래스가 사용하고 싶은 Bean 구체 클래스를 바로 생성자 주입 통해 주입한다는 것이다.&lt;/p&gt;
&lt;h3 id=&quot;appplicationcontext&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#appplicationcontext&quot; aria-label=&quot;appplicationcontext permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AppplicationContext&lt;/h3&gt;
&lt;p&gt;기존 프론트 컨트롤러에 스프링 컨테이너를 도입했다. 컨테이너에 등록할 Bean 객체는 &lt;code class=&quot;language-text&quot;&gt;AppplicationContext&lt;/code&gt; 로 등록한다. 이 인터페이스를 구현한 &lt;code class=&quot;language-text&quot;&gt;GenericApplicationContext&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;registerBean()&lt;/code&gt; 메소드를 통해 등록해줄 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;GenericApplicationContext&lt;/span&gt; applicationContext &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenericApplicationContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SimpleHelloService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 로 넣겨준다&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
		applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;refresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt; serverFactory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt; helloController &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token class-name&quot;&gt;WebServer&lt;/span&gt; webServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; serverFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWebServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servletContext &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			servletContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token comment&quot;&gt;// 인증, 보안, 다국어등의 공통 처리 코드 부분 (생략함)&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestURI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
						&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

						&lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt; helloController &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
						&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; ret &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; helloController&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

						resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setContentType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MediaType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TEXT_PLAIN_VALUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
						resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWriter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ret&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestURI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
						resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
						resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NOT_FOUND&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		webServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;dispatcherservlet&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dispatcherservlet&quot; aria-label=&quot;dispatcherservlet permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DispatcherServlet&lt;/h2&gt;
&lt;p&gt;한편 이전까지 개발했던 서블릿 컨테이너는 Containerless 하지 못하다. 개발자 입장에선 프론트 컨트롤러를 매번 직접 구현하는 과정이 꽤나 번거롭다. &lt;code class=&quot;language-text&quot;&gt;매핑 코드&lt;/code&gt; (ex. URL 경로, HTTP 메소드), &lt;code class=&quot;language-text&quot;&gt;파라미터&lt;/code&gt; (ex. DTO) 등 애플리케이션 로직과 긴밀하게 연결된 코드들이 서블릿 컨테이너 내부 구현 코드로 등장한다는 것이다.&lt;/p&gt;
&lt;h3 id=&quot;servlet-containerless&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#servlet-containerless&quot; aria-label=&quot;servlet containerless permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Servlet Containerless&lt;/h3&gt;
&lt;p&gt;이 떄문에 &lt;code class=&quot;language-text&quot;&gt;Servlet Containerless&lt;/code&gt; 를 만드는 것의 필요성이 느껴찐다. 그래서 등장한 것이 바로 &lt;code class=&quot;language-text&quot;&gt;DispatcherServlet&lt;/code&gt; 이다. 기존의 서블릿 컨테이너에서 직접 하드코딩했던 수고가 줄어들게 되었다.&lt;/p&gt;
&lt;h3 id=&quot;어노테이션으로-메타-정보를-전달하는-방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9C%BC%EB%A1%9C-%EB%A9%94%ED%83%80-%EC%A0%95%EB%B3%B4%EB%A5%BC-%EC%A0%84%EB%8B%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;어노테이션으로 메타 정보를 전달하는 방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어노테이션으로 메타 정보를 전달하는 방법&lt;/h3&gt;
&lt;p&gt;기존에 하드 코딩했던 서블릿은 URL 매핑 정보, 파라미터, 스프링 컨테이너와의 매핑 정보등의 &quot;메타 정보&quot; 를 직접 구현했었다. 하지만 반면 DispatcherServlet 은 이런 직접 일일이 구현해야했던 수고를 없애도록 &lt;code class=&quot;language-text&quot;&gt;어노테이션&lt;/code&gt; 을 활용한 방법이 등장했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestMapping&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloService&lt;/span&gt; helloService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HelloService&lt;/span&gt; helloService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;helloService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; helloService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ResponseBody&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; helloService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sayHello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requireNonNull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;매핑 정보를 서블릿 코드에 직접 넣는 대신에, 그 요청을 처리할 Controller 클래스 내부에다 매핑 정보를 대신 집어넣는 방법이다. 클래스 레벨에선 &lt;code class=&quot;language-text&quot;&gt;@RequestMapping&lt;/code&gt; 을, 메소드 레벨에선 &lt;code class=&quot;language-text&quot;&gt;@GetMapping&lt;/code&gt; 등의 어노테이션을 간단히 붙임으로써 DispatcherServlet 은 메타 정보를 인식할 수 있게된다.&lt;/p&gt;
&lt;p&gt;위와 같이 개선하면 DispatcherServlet 은 먼저 클래스 레벨에서 매핑 정보를 찾고, 그 클래스 내부의 메소드 레벨에서 추가 정보를 찾는다.&lt;/p&gt;
&lt;p&gt;참고로 hello() 메소드에서 그냥 String 데이터를 리턴해버리면 View 를 랜더링하게 된다. 즉 Stirng 리턴 값을 View 이름 값으로 인식하는 것이다. hello.html 을 찾고 랜더링 하고자 하므로 404 에러가 발생하는데, 이는 &lt;code class=&quot;language-text&quot;&gt;@ResponseBody&lt;/code&gt; 를 붙여줌으로써 해결된다. 사실 &lt;code class=&quot;language-text&quot;&gt;@RestController&lt;/code&gt; 를 클래스에 붙인다면 기본적으로 해당 클래스 내부의 모든 메소드에 &lt;code class=&quot;language-text&quot;&gt;@ResponseBody&lt;/code&gt; 가 붙어있지만, 여기선 &lt;code class=&quot;language-text&quot;&gt;@RestController&lt;/code&gt; 를 활용하지 않았기 떄문에 이 어노테이션을 붙여준 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;스프링-컨테이너-서블릿-컨테이너-초기화-코드-최소화하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%84%9C%EB%B8%94%EB%A6%BF-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%B4%88%EA%B8%B0%ED%99%94-%EC%BD%94%EB%93%9C-%EC%B5%9C%EC%86%8C%ED%99%94%ED%95%98%EA%B8%B0&quot; aria-label=&quot;스프링 컨테이너 서블릿 컨테이너 초기화 코드 최소화하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링 컨테이너, 서블릿 컨테이너 초기화 코드 최소화하기&lt;/h2&gt;
&lt;p&gt;앞서 설명한 내용을 기반으로 코드를 구성한다면 크게 2개의 파트로 구분된다. 우선 &lt;code class=&quot;language-text&quot;&gt;(1) 스프링 컨테이너 초기화 작업&lt;/code&gt; 일 것이다. 스프링 컨테이너를 생성하고 Bean 을 등록하여 &lt;code class=&quot;language-text&quot;&gt;초기화&lt;/code&gt; 작업을 해주는 부분이다. 또한 &lt;code class=&quot;language-text&quot;&gt;(2) 서블릿 컨테이너 초기화 작업&lt;/code&gt; 도 발생한다. 그렇게 만들어진 스프링 컨테이너를 활용하면서 서블릿 컨테이너를 코드에서 생성하고, 그 내부에 프론트 컨트롤러 역할을 하는 &lt;code class=&quot;language-text&quot;&gt;DispatcherServlet&lt;/code&gt; 을 등록하는 부분이다.&lt;/p&gt;
&lt;p&gt;이렇게 과정 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 로 구분된 &quot;초기화&quot; 작업 코드를 최소화해보고 싶다. &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에서 발생하는 서블릿 컨테이너를 생성하고 초기화하는 하드 코딩 을 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 를 실행하는 중간에 발생하도록 개선해보겠다. &lt;strong&gt;즉, 스프링 컨테이너를 초기화하는 중간에 서블릿 컨테이너 생성 및 초기화도 발생하도록 한다는 것이다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;genericwebapplicationcontext-를-활용한-초기화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#genericwebapplicationcontext-%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%B4%88%EA%B8%B0%ED%99%94&quot; aria-label=&quot;genericwebapplicationcontext 를 활용한 초기화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GenericWebApplicationContext 를 활용한 초기화&lt;/h3&gt;
&lt;p&gt;스프링 컨테이너의 초기화 작업은 &lt;code class=&quot;language-text&quot;&gt;GenericWebApplicationContext&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;refresh()&lt;/code&gt; 메소드를 호출하여 발생한다. 이때 refesh() 를 호출하여 초기화 작업이 일어나는 중간에 부가적인 추가 작업을 수행하기 위해선 &lt;code class=&quot;language-text&quot;&gt;GenericWebApplicationContext&lt;/code&gt; 클래스를 상속한 새로운 클래스를 하나 만들고, &lt;code class=&quot;language-text&quot;&gt;onRefesh()&lt;/code&gt; 라는 메소드를 재정의 해줘야한다.&lt;/p&gt;
&lt;p&gt;우리는 이 새로운 클래스를 익명 클래스로 상속하겠다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;GenericWebApplicationContext&lt;/span&gt; applicationContext &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenericWebApplicationContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onRefresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onRefresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 익명클래스가 onRefresh() 를 오버라이딩 한다.&lt;/span&gt;

				&lt;span class=&quot;token comment&quot;&gt;// 서블릿 컨테이너 초기화 코드는 아래와 같이 삽입해줬다.&lt;/span&gt;
				&lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt; serverFactory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				&lt;span class=&quot;token class-name&quot;&gt;WebServer&lt;/span&gt; webServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; serverFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWebServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servletContext &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					servletContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dispatcherServlet&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
							&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				webServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SimpleHelloService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;refresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;서블릿 컨테이너를 초기화하는 코드를 &lt;code class=&quot;language-text&quot;&gt;onRefresh()&lt;/code&gt; 재정의시에 넣어주면 된다. 이렇게 되면 스프링 컨테이너의 초기화 작업이 발생할 때 동시에 서블릿 컨테이너의 생성 및 초기화 작업도 일어날 수 있게된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;자바-팩토리-메소드로-bean-오브젝트-구성하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EB%B0%94-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%86%8C%EB%93%9C%EB%A1%9C-bean-%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0&quot; aria-label=&quot;자바 팩토리 메소드로 bean 오브젝트 구성하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자바 팩토리 메소드로 Bean 오브젝트 구성하기&lt;/h2&gt;
&lt;p&gt;앞서 살펴봤던 &lt;code class=&quot;language-text&quot;&gt;ApplicationContext&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;registerBean()&lt;/code&gt; 를 통해 Bean 오브젝트를 등록했지만, &lt;code class=&quot;language-text&quot;&gt;@Bean&lt;/code&gt; 어노테이션이 붙은 자바 팩토리 메소드를 통해서도 스프링 컨테이너에 빈 등록이 가능하다.&lt;/p&gt;
&lt;p&gt;이후 한 가지 작업을 더 해줘야한다. &lt;code class=&quot;language-text&quot;&gt;@Configuration&lt;/code&gt; 을 클래스 레벨에 붙이는 것이다. 그러면 스프링 컨테이너가 해당 클래스 내부 내용을 통해 Bean 오브젝트를 가진, 즉 자바 팩토리 메소드 구성정보를 가짐을 알 수 있게된다.&lt;/p&gt;
&lt;p&gt;요약하자면, &lt;strong&gt;스프링 컨테이너는&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;@Configuration&lt;/code&gt; &lt;strong&gt;이 붙은 클래스를 인식하여 @Bean 어노테이션이 붙은 팩토리 메소드를 기반으로 빈을 구성한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;annotationconfigwebapplicationcontext&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#annotationconfigwebapplicationcontext&quot; aria-label=&quot;annotationconfigwebapplicationcontext permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AnnotationConfigWebApplicationContext&lt;/h3&gt;
&lt;p&gt;또한 기존 &lt;code class=&quot;language-text&quot;&gt;GenericWebApplicationContext&lt;/code&gt; 는 자바 코드로 만든 구성 정보를 인식하지 못한다. 이는 &lt;code class=&quot;language-text&quot;&gt;AnnotationConfigWebApplicationContext&lt;/code&gt; 로 대체하면 된다. 이 클래스 객체는 어노테이션이 붙은, 즉 &lt;code class=&quot;language-text&quot;&gt;@Bean&lt;/code&gt; 이 붙은 자바 코드를 기반으로 구성 정보를 읽어오고 빈을 구성한다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;AnnotationConfigWebApplicationContext&lt;/code&gt; 은 기존의 &lt;code class=&quot;language-text&quot;&gt;registerBean()&lt;/code&gt; 메소드를 지원하지 않는다. 이를 자바 코드로 구성 정보를 가지고 있는 클래스 (즉 &lt;code class=&quot;language-text&quot;&gt;@Configuation&lt;/code&gt; 이 붙은 클래스) 를 등록해줘야한다. 이를 &lt;code class=&quot;language-text&quot;&gt;register()&lt;/code&gt; 메소드로 아래와 같이 구성해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;helloController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HelloService&lt;/span&gt; helloService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;helloService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloService&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;helloService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SimpleHelloService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;AnnotationConfigWebApplicationContext&lt;/span&gt; applicationContext &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnnotationConfigWebApplicationContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onRefresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onRefresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				&lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt; serverFactory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				&lt;span class=&quot;token class-name&quot;&gt;WebServer&lt;/span&gt; webServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; serverFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWebServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servletContext &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					servletContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dispatcherServlet&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
							&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				webServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;refresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;컴포넌트-스캔으로-리팩토링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%EC%BA%94%EC%9C%BC%EB%A1%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81&quot; aria-label=&quot;컴포넌트 스캔으로 리팩토링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컴포넌트 스캔으로 리팩토링&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@Component&lt;/code&gt; 를 활용하면 &lt;code class=&quot;language-text&quot;&gt;@Configuration&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@Bean&lt;/code&gt; 등으로 직접 등록하던 팩토리 메소드를 제거한다. 즉 설정 정보 클래스를 제거하고 간단히 클래스에다 &lt;code class=&quot;language-text&quot;&gt;@Component&lt;/code&gt; 를 붙일 수 있다. 또한 &lt;code class=&quot;language-text&quot;&gt;@Component&lt;/code&gt; 가 붙은 클래스를 찾아서 빈으로 등록해 달라는 것을 &lt;code class=&quot;language-text&quot;&gt;@ComponentScan&lt;/code&gt; 을 붙임으로써 컨테이너에게 전달할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@ComponentScan&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;AnnotationConfigWebApplicationContext&lt;/span&gt; applicationContext &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnnotationConfigWebApplicationContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onRefresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onRefresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				&lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt; serverFactory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				&lt;span class=&quot;token class-name&quot;&gt;WebServer&lt;/span&gt; webServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; serverFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWebServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servletContext &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					servletContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dispatcherServlet&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
							&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				webServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;refresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위처럼 applicationContext 가 등록된 HellobootApplication 클래스 위에 &lt;code class=&quot;language-text&quot;&gt;@ComponentScan&lt;/code&gt; 이 붙어있으면, 이 클래스가 붙어있는 패키지부터 시작해서 그 하위 패키지를 뒤져서 &lt;code class=&quot;language-text&quot;&gt;@Component&lt;/code&gt; 라는 어노테이션이 붙은 모든 클래스를 Bean 으로 등록한다. Bean 으로 등록할 때 필요하다면 의존 오브젝트를 찾아나고 이를 생성자를 호출할 때 파라미터로 넘겨주기도 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;tomcatservletwebserverfactory-dispatcherservlet-를-bean-으로-등록&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tomcatservletwebserverfactory-dispatcherservlet-%EB%A5%BC-bean-%EC%9C%BC%EB%A1%9C-%EB%93%B1%EB%A1%9D&quot; aria-label=&quot;tomcatservletwebserverfactory dispatcherservlet 를 bean 으로 등록 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TomcatServletWebServerFactory, DispatcherServlet 를 Bean 으로 등록&lt;/h2&gt;
&lt;p&gt;당연히도 &lt;code class=&quot;language-text&quot;&gt;TomcatServletWebServerFactory&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;DispatcherServlet&lt;/code&gt; 이 없다면 애플리케이션 서버를 시작할 수 없다. 이 2개의 으브젝트도 Bean 으로 등록해줘야 한다.&lt;/p&gt;
&lt;p&gt;앞서 말했길, &lt;code class=&quot;language-text&quot;&gt;DispatcherServlet&lt;/code&gt; 은 자신이 이용할 컨트롤러를 찾아야하기 때문에 &lt;code class=&quot;language-text&quot;&gt;ApplicationContext&lt;/code&gt; 을 파라미터로 넘겨줘야 한다고 했었다. 이에 따라, DispatcherServlet 이 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;setAppplicationContext()&lt;/code&gt; 라는 setter 를 호출하도록 한다. 파라미터로 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; 키워드를 넣었다. 즉 ApplicationContext 를 파라미터로 넘겨준 것이라 볼 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@ComponentScan&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletWebServerFactory&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;servletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;AnnotationConfigWebApplicationContext&lt;/span&gt; applicationContext &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnnotationConfigWebApplicationContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onRefresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onRefresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				&lt;span class=&quot;token class-name&quot;&gt;ServletWebServerFactory&lt;/span&gt; serverFactory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ServletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt; dispatcherServlet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				dispatcherServlet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setApplicationContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;//setter 호출&lt;/span&gt;

				&lt;span class=&quot;token class-name&quot;&gt;WebServer&lt;/span&gt; webServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; serverFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWebServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servletContext &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					servletContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dispatcherServlet&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
							&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				webServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;refresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;applicationcontextaware&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#applicationcontextaware&quot; aria-label=&quot;applicationcontextaware permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ApplicationContextAware&lt;/h3&gt;
&lt;p&gt;하지만 이렇게 setter 를 호출할 필요가 없다. 이 이유를 알기 위해선 먼저 &lt;code class=&quot;language-text&quot;&gt;ApplicationContextAware&lt;/code&gt; 라는 인터페이스에 대해 알아야한다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;ApplicationContextAware&lt;/code&gt; 는 인터페이스 타입으로, 대표적인 구현 클래스로 &lt;code class=&quot;language-text&quot;&gt;DispatcherServlet&lt;/code&gt; 가 해당된다. ApplicationContextAware 의 정의문에 따르면, 이 인터페이스를 구현한 구현 클래스가 스프링의 빈으로 등록이 되면 자동으로 setApplication 메소드를 자동 호출하여 주입해준다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;즉, 우리 대신에 스프릴 컨테이너가 DispatcherServlet 은 ApplicationContext 가 필요하다고 생각하고 자동으로 주입한다.&lt;/strong&gt; 따라서 굳이 setter 를 직접 호출하지 않아도 자동으로 setter 가 호출된다. 위 코드에서 setter 호출문은 제거해주자.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;최종-springbootapplication-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B5%9C%EC%A2%85-springbootapplication-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;최종 springbootapplication 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;최종 SpringBootApplication 구현&lt;/h2&gt;
&lt;p&gt;마지막으로 기존 코드, 즉 서블릿 컨테이너와 서블릿 컨테이너를 초기화 후 애플리케이션을 구동하는 코드를 &lt;code class=&quot;language-text&quot;&gt;run()&lt;/code&gt; 이라는 메소드로 따로 분리시켜보자.&lt;/p&gt;
&lt;h3 id=&quot;run-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#run-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;run 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;run() 생성&lt;/h3&gt;
&lt;p&gt;현재 클래스 HellobootApplication 뿐만 아니라, 다른 메인이 되는 여러 클래스에서도 서블릿 컨테이너를 코드에서 자동으로 띄워주면서 스프링 컨테이너에서 필요한 정보를 받아와서 기본적인 기능을 수행할 수 있도록 만드는 메소드라고 볼 수 있다. &lt;strong&gt;즉, 스프링 컨테이너 초기 준비작업을 해주는데 이 메소드를 재사용할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@ComponentScan&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletWebServerFactory&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;servletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; applicationClass&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;AnnotationConfigWebApplicationContext&lt;/span&gt; applicationContext &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnnotationConfigWebApplicationContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onRefresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onRefresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				&lt;span class=&quot;token class-name&quot;&gt;ServletWebServerFactory&lt;/span&gt; serverFactory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ServletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt; dispatcherServlet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				&lt;span class=&quot;token class-name&quot;&gt;WebServer&lt;/span&gt; webServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; serverFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWebServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servletContext &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					servletContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dispatcherServlet&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
							&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				webServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;refresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;최종-run-분리-springbootapplication-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B5%9C%EC%A2%85-run-%EB%B6%84%EB%A6%AC-springbootapplication-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;최종 run 분리 springbootapplication 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;최종 run() 분리, SpringBootApplication 생성&lt;/h3&gt;
&lt;p&gt;최종적으로 MySpringBootApplication 이라는 클래스로 run() 를 분리했다. 이 결과는 우리가 평소에 자주봤던 SpringBootApplication 시작을 위한 &lt;code class=&quot;language-text&quot;&gt;run()&lt;/code&gt; 메소드와 매우 유사하다. 이러한 내부 동작원리로 스프링부트 애플리케이션을 가동할 수 있었던 것이다 🙂&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@ComponentScan&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletWebServerFactory&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;servletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;MySpringBootApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;



&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MySpringBootApplication&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; applicationClass&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;AnnotationConfigWebApplicationContext&lt;/span&gt; applicationContext &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnnotationConfigWebApplicationContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onRefresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onRefresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;token class-name&quot;&gt;ServletWebServerFactory&lt;/span&gt; serverFactory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ServletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt; dispatcherServlet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;token class-name&quot;&gt;WebServer&lt;/span&gt; webServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; serverFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWebServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servletContext &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    servletContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dispatcherServlet&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatcherServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

                webServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        applicationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;refresh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이렇게까지 SpringBootApplication 의 &lt;code class=&quot;language-text&quot;&gt;run()&lt;/code&gt; 을 어떻게 구동시킬 수 있는지를 직접 코드를 구현하여 내부 원리를 이해할 수 있었다. 이번 학습은 정말정말 대만족스럽다 😆 항상 서블릿과 JSP 를 등한시 해왔던 내가 드디어 &quot;왜&quot; 중요하다는지 에 대한 명확한 설명을 할 수 있게 되었다.&lt;/p&gt;
&lt;p&gt;하지만 아직 학습해야 할 내용이 많이 남았다. 특히 구현한 코드는 실제 &lt;code class=&quot;language-text&quot;&gt;run()&lt;/code&gt; 메소드와 다르게 &lt;code class=&quot;language-text&quot;&gt;DispatcherServlet&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;TomcatServletWebServerFactory&lt;/code&gt; 에 대한 Bean 오브젝트를 스프링 컨테이너에 등록해줘야 한다. 이에 대한 해결방안은 천천히 찾아보고자 한다 🙂&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ServletWebServerFactory, DispatcherServlet 에 대한 Bean 오브젝트 없이 어떻게 run() 메소드를 동일하게 구현할 수 있는가?&lt;/li&gt;
&lt;li&gt;Jetty&lt;/li&gt;
&lt;li&gt;ApplicationContext (아직 더 깊은 이해도가 필요한건 사실이다 🥹)&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[데이터베이스 클러스터링 인덱스와 세컨더리 인덱스]]></title><description><![CDATA[만약 인덱스가 없었더라면,  이 발생하여 원하는 데이터를 찾는데 꽤 오랜 시간이 걸릴것이다. 인덱스가 없는 경우엔 데이터가 단순히 삽입된 순서대로 담기게 된다. 이 떄문에 순차 탐색을 해야하고, 최악의 경우 O(n…]]></description><link>https://haon.site/database/cluster-secondary-index/</link><guid isPermaLink="false">https://haon.site/database/cluster-secondary-index/</guid><pubDate>Thu, 24 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/60b635efc0ae0f1ef071c04098118438/9c701/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.03067484662577%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAADjElEQVR42i2S+U8TZhzG+/9sybJsy+ZEiboJuOBctgVNtpldKhMY10CkiBwt0E4yKpciZ1uQs5ajhVJaekC5KQgla6EIdOAgch9CDJ+9kv3w/PB9f3ie532eR9Kgt2O2jWPpHcFiHWB8ah63Z4GZ2QCz8wECy6usrq2zvrHN9vYuW9t7bGzusHFy73Hw+ojj42MBODx8g6SpzYauzcoDZQFlj8v5U6misqaJDlMfo8/nmPYuMuNbYNYfYGHppcAqvvl/mfT+g3/plRDYF6SH7O8fnghJ7iifcueuHLPZSo4sjz7XEP0C1dW1lD6qQd/Ry8jkHB7fCmPC/fCEnwnPIt0uL9aROaa8y4J4jcXlV0JsDUn49TQM7QYKC0vRaurYPDhCIVOwGFjhxdIyLc2twnk1Tc1GnIPTdA/MoTFOUqkfQdMxRoPJTbvdg3nAS6N5CsnZiFT0LToUciXrWzusrG+REB2Pe9LDrshERMPGzh42ez8aTT1KlZqY3GakpTYyyp2kP7aQV9WLvNJO9IMuJO9cLSQmSU5tjQZH3xDrOwekJKYwNjFNYUExFWVVuFwjHAnm12/ANztPjbqRtNxyvksu50pSAxH327mW3sY3Uh2Sj25oef/bbEqKH1FZoeZF4CVxt2MZHX9OblYuvb3OE+L+/mGcQtBo7EbXpGNT/KZPvGXnV/N1TBHnomoI+UMQBv1Wy+XEWqKTcigvLmJwYBijwYTPv0i+4i/mF5bR1tShVT9Flf+Q9rZOikXe0zNiAR4f+weH+Lx+nqif8UVCHZKL8Y1cvddKcGQlEZHZpMtUooA2hobdJ+Q7u/tYe2w4nYPUahpEli4KVcUnrkselmLqsjLu9uD3+bie8QzJmfhWTv2uJyi+g09i2/jwdhPviQhCLn1JgjSXVHkJak0j42Nu3COjtNQ3YhETa9UbKC0qo0/EkCdTYtC384vCJFpO7uJscifByUaCk4ycT7Pywa8V3IyJ416FmXdvNvKpcH/uhoofkkuQq7TUiwnZResmQycej5fmBh02q4OflWYkF1ItnP8fF1J7+Py+g49vVREZm0hmtYUzKT2EZtj5LKOfYKmNU4mdnI5t5mK8lp+kT7grzaK+pYv2VgPfy41IQrOcvEVI5ls4CMsZ5nSUmltRsWSq7QSn2wmTOQiVObmU4yJcMUS4cpTLBX8TFNfAVz/Gck1hISylhSs5Dv4DflEI6U86vhYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/60b635efc0ae0f1ef071c04098118438/a6d36/image.png&quot;
        srcset=&quot;/static/60b635efc0ae0f1ef071c04098118438/222b7/image.png 163w,
/static/60b635efc0ae0f1ef071c04098118438/ff46a/image.png 325w,
/static/60b635efc0ae0f1ef071c04098118438/a6d36/image.png 650w,
/static/60b635efc0ae0f1ef071c04098118438/e548f/image.png 975w,
/static/60b635efc0ae0f1ef071c04098118438/3c492/image.png 1300w,
/static/60b635efc0ae0f1ef071c04098118438/9c701/image.png 1774w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;만약 인덱스가 없었더라면, &lt;code class=&quot;language-text&quot;&gt;테이블 풀 스캔(Table Full Scan)&lt;/code&gt; 이 발생하여 원하는 데이터를 찾는데 꽤 오랜 시간이 걸릴것이다. 인덱스가 없는 경우엔 데이터가 단순히 삽입된 순서대로 담기게 된다. 이 떄문에 순차 탐색을 해야하고, 최악의 경우 O(n) 시간이 소요된다.&lt;/p&gt;
&lt;p&gt;이를 개선하기 위해 인덱스가 등장했다고 여러 포스팅에 걸쳐 설명했었다. 이 인덱스는 사실 2종류로 나뉜다. 이번 포스팅에선 인덱스의 2가지 종류인 클러스터링 인덱스와 비클러스터링 인덱스에 대해 다루어본다.&lt;/p&gt;
&lt;h2 id=&quot;클러스터링-인덱스-clustering-index&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4-clustering-index&quot; aria-label=&quot;클러스터링 인덱스 clustering index permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클러스터링 인덱스 (Clustering Index)&lt;/h2&gt;
&lt;p&gt;클러스터링 인덱스는 데이터가 테이블에 물리적으로 저장되는 순서를 정의하는 인덱스이다. 즉, &lt;strong&gt;클러스터링 인덱스는 특정 컬럼을 기준으로 데이터들을 정렬&lt;/strong&gt;시킨다. 이때, &lt;strong&gt;클러스터링 인덱스는 실제 데이터와 무리(Cluster) 를 지어 인덱싱&lt;/strong&gt; 되므로 클러스터링 인덱스라고 부른다. 클러스터링 인덱스의 주 목적은 실제 데이터를 정렬하는 것이다. 실제 데이터들은 클러스터링 인덱스를 기준으로 하여 함께 묶여서 정렬되기에, 원하는 데이터를 탐색시 더 빠르게 정렬된 구조에서 데이터를 찾을 수 있게 해준다.&lt;/p&gt;
&lt;p&gt;클러스터링 인덱스는 &lt;strong&gt;테이블당단 1개만 생성할 수 있다.&lt;/strong&gt; 다시말해, 테이블 당 데이터는 오직 1가지의 클러스터링 인덱스로 정렬 기준을 갖고 실제 데이터들이 정렬된다. 정렬 기준으로 오직 1개의 컬럼만을 선택할 수 있다는 뜻이다.&lt;/p&gt;
&lt;p&gt;따라서 정렬 기준으로 가장 적합한 컬럼 하나만을 클러스터링 인덱스로 지정할 수 있는데, 대표적으로 PK(Primary Key) 가 클러스터링 인덱스로 자동 지정된다. 만약 PK 가 아니라 다른 컬럼을 지정하고 싶다면, &lt;strong&gt;Unique + Not NULL&lt;/strong&gt; 제약조건을 부여한 컬럼은 클러스터링 인덱스가 자동 생성된다. 만약 PK 와  &lt;strong&gt;Unique + Not NULL&lt;/strong&gt; 을 부여한 컬럼이 동시에 존재한다면, PK 를 우선순위로 하여 클러스터링 인덱스를 생성한다. (클러스터링 인덱스는 단 1개의 컬럼만 허용하기 떄문에, 어떤 것을 선택할지 우선순위를 두고 결정한다.) 만약 이 2가지 모두 없는 경우, InnoDB 는 내부적으로 &lt;code class=&quot;language-text&quot;&gt;GEN_CLUST_INDEX&lt;/code&gt; 라는 컬럼을 생성하여 클러스터링 인덱스를 생성한다. &lt;code class=&quot;language-text&quot;&gt;GEN_CLUST_INDEX&lt;/code&gt; 는 행이 생성된 순서대로 값을 부여한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/91460cf84e16d907adf686367253c16a/56e36/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.828220858895705%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABv0lEQVR42o2TWW/TQBDH8+X5BEg88YAESkpJgVblkR6B5kJQUQmR0CSNmkN27NhOvGuvzx9rJ0UEIcJKo5VmVjP/Y7bCnpNlGaFKkEFMluf7nlP5WzLPs/KejG9ontep1x5z+uYJXzqvcay7siaEwJjPcS0L27ZRSv1Hw8EFZ+1HDHo13MkJ5uiA2B+UtTAMWZomqfBJNYt8i77yL/hhEDEOpohEEscJSZIW4/ZTjlSE8AW+v0ZqKtJfEejJSq41Ih/PsTTVOaFYIaXQgxSBlCXtNEnI/0Q4W5g0rrr0+8PNGKNPevuRsH9GNrshkj6B723lyIm3khS6PWi3g/Drt3d0m1XaFy/4dHXIdesAtV7Qu31Pq/GU7ocarfNnfG4d4i0n9IYN2s3ndC6rdHTtul3HNjfaVhIN2Z1dYgyPcKcnGKNjZj9eoYIF9rTLtP+S1fwU8+6Y+aCO8EZY923uv1e3+bc6f0Qsx5uGcRTjuh5BqCiax0mxc0rrkmM7Lt5alC6qKCKMtDFpjmU7LB2vNEmpmEgbtutyoclv8bA25aOd/MblLEtZLAwsyyxv09D76Dj712bfDyoiTdNfUTT8CfPDRnb5nhi7AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/91460cf84e16d907adf686367253c16a/a6d36/image-1.png&quot;
        srcset=&quot;/static/91460cf84e16d907adf686367253c16a/222b7/image-1.png 163w,
/static/91460cf84e16d907adf686367253c16a/ff46a/image-1.png 325w,
/static/91460cf84e16d907adf686367253c16a/a6d36/image-1.png 650w,
/static/91460cf84e16d907adf686367253c16a/e548f/image-1.png 975w,
/static/91460cf84e16d907adf686367253c16a/3c492/image-1.png 1300w,
/static/91460cf84e16d907adf686367253c16a/56e36/image-1.png 1750w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;클러스터링 인덱스는 &lt;strong&gt;B+ Tree&lt;/strong&gt; 형태로 구성되어 있는데, 각 페이지(노드) 는 고유의 페이지 번호(Page ID)를 가지고 있다. 페이지 번호는 마치 집 주소 번지값이라고 생각하면 쉽다.&lt;/p&gt;
&lt;p&gt;위 예제를 살펴보자. 각 데이터는 리프 페이지에서 PK 를 기준으로 정렬되어 있다. 루트 페이지는 Key 로 각 페이지에 대한 고유 페이지 번호를 갖고 있으며, Value 로 각 페이지가 보유한 PK 중 가장 앞단의 PK 를 가지고 있다. (간단한 예시를 들기위해, 루트와 리프 페이지 중간 계층의 페이지가 있는것은 생략했다.) 보듯이 PK 를 기준으로 정렬되어 있으며, 리프 페이지엔 인덱스가 실제 데이터와 함께 군집(Cluster) 를 이루어 저장된다. 클러스터링 인덱스를 기준으로 실제 데이터들이 정렬된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; MEMBER
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;PK 값을 WHERE 절에 넣어 데이터틑 탐색하는 과정을 생각해보자. PK 값은 7이기 떄문에, 루프 페이지에서 5 와 9 사이에 존재하는 값임을 알 수 있다. 앞서 설명했듯이 각 루프 페이지는 각 페이지가 보유한 PK 중 가장 앞단 (가장 작은) PK 를 저장한다고 했으므로, id = 5 를 가진 10002번지에 대한 리프 페이지로 내려간다. 그러고 원하는 데이터를 해당 리프 페이지에서 찾아낼 수 있게된다.&lt;/p&gt;
&lt;h3 id=&quot;단점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90&quot; aria-label=&quot;단점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;p&gt;클러스터링 인덱스를 기준으로 실제 데이터들이 함께 군집을 이루어 정렬 된 상태를 유지하기 떄문에 검색 속도가 매우 빨라진다. 하지만, 순서는 오직 하나의 컬럼을 기준으로 정렬되기 떄문에 중간에 새로운 데이터가 삽입된다면 이후의 모든 컬럼을 한 칸씩 이동(재정렬) 해줘야 한다. 즉, 데이터를 삽입, 변경, 삭제할 떄 마다 전체 데이터 페이지에 재정렬이 필요하므로 속도가 매우 느리다는 점을 유의해야 한다.&lt;/p&gt;
&lt;p&gt;만약 PK = 2 를 가진 신규 데이터를 삽입한다고 해보자. 그리고 데이터 이후에 1만개의 데이터가 존재한다고 해보자. 그렇다면 기존의 PK = 3 ~ 10001 까지의 데이터들은 모두 한 칸씩 뒤로 이동시켜줘야하는 셈이다. 인덱스가 군집(Cluster) 를 이루기 떄문이다. 이렇듯 새로운 데이터를 삽입할 떄는 많은 비용이 소모된다는 점을 유의하자.&lt;/p&gt;
&lt;h2 id=&quot;비클러스터링-인덱스-non-clustering-index&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4-non-clustering-index&quot; aria-label=&quot;비클러스터링 인덱스 non clustering index permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비클러스터링 인덱스 (Non Clustering Index)&lt;/h2&gt;
&lt;p&gt;비클러스터링 인덱스(Non Clustering Index) 는 &lt;strong&gt;보조 인덱스(Secondary Index)&lt;/strong&gt; 라고도 불리며, 클러스터링 인덱스와 달리 인덱스들이 실제 데이터들과 함께 군집을 이루어 저장되는 방식이 아니다. 그 대신에 정렬된 별도의 인덱스 페이지로 저장되고 관리된다. &lt;strong&gt;실제 데이터와 함꼐 군집을 이루지 않고, 별도의 분리된 인덱스 페이지 저장 공간에서 실제 데이터 페이지들을 포인터로 가리키는 구조를 취한다.&lt;/strong&gt; 만약 &lt;code class=&quot;language-text&quot;&gt;UNIQUE&lt;/code&gt; 제약 조건을 걸은 컬럼이 있다면, 해당 컬럼은 자동으로 비클러스터링 인덱스가 생성된다. 또는 &lt;code class=&quot;language-text&quot;&gt;INDEX&lt;/code&gt; 쿼리를 넣어서 직접 인덱스를 생성하는 방법도 있다. 아래 3가지 방법은 모두 비클러스터링 인덱스가 생성된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; member
&lt;span class=&quot;token keyword&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;CONSTRAINT&lt;/span&gt; name &lt;span class=&quot;token keyword&quot;&gt;UNIQUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// unique 제약 조건&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;UNIQUE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; name &lt;span class=&quot;token comment&quot;&gt;// 중복을 허용하지 않는 인덱스를 생성&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; member &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 중복을 허용하는 인덱스 생성&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; member &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8441e03c81b887e57d46b423e1af4ce6/dd507/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.41717791411043%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACFklEQVR42pWTzU/bQBDF+at765l7e2zVQ6VW6qUnpMIBKbSGqgoBgWgBmXw5xsQ2TmI7/lp799fdhEAoqFVHGq88u/tm3pvZDbRJKRe+bkqpey+LijhOSdM5TdPwN9swnyzLKMvyHshYnuc4joMzHNI9/0jH2uTs+yvmsx7LIhp0uucBhRBPMq+OSqmI4muS/IYsCRBV/u8KDWCSJItKV9TNxVHvlJNOi3O7TXMXD8Yjfh7vcXFhkWbRI1b3gFVV0e/36fW6zKaR1ithGl7Rbr/UNDdxLz/pW0sGof+Dg/0XXLRfk6ej5wHXKU9mCa4XMA5u6aUDClEiCk1TLveDWUA/GdAIudBxKc6fgHWjL8Wo6SnE2lPt0yPUbQcZX6KqDBX2kGFX+xVyMkRGA1Qa3umtTJkPgPN5SqG7rJo5InHJ5mPqYoosY5paINIJZTCgnvoaTO/dXlP6XfJwSB15NCYu1wCLosCyLL5a+3p8BHZ/j5Ojz5wdb3F1vquzS7r2LofWG04OPtA/20HmMw7b7zlsvaXTekfg/XrcFNd1GeqZGzlDYv8I195iZH/Bs7cXB5PJJW53G8feIRof6yQ10egbvtPCG+ySxYPnB3s1tELUulGSuhILlaTWyPyvmmc6u4w1yxe13pQnz09TNGOizKioh5i662qaxnieRxSFjMcevn9DGPiPAf/HTGLDyIybWY1kK4a/Aak34/DRqTKcAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/8441e03c81b887e57d46b423e1af4ce6/a6d36/image-2.png&quot;
        srcset=&quot;/static/8441e03c81b887e57d46b423e1af4ce6/222b7/image-2.png 163w,
/static/8441e03c81b887e57d46b423e1af4ce6/ff46a/image-2.png 325w,
/static/8441e03c81b887e57d46b423e1af4ce6/a6d36/image-2.png 650w,
/static/8441e03c81b887e57d46b423e1af4ce6/e548f/image-2.png 975w,
/static/8441e03c81b887e57d46b423e1af4ce6/3c492/image-2.png 1300w,
/static/8441e03c81b887e57d46b423e1af4ce6/dd507/image-2.png 1528w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;인덱스 페이지를 항상 정렬된 상태를 유지하지만, 실제 데이터 페이지는 정렬되지 않아서 클러스터링에 비해 삽입, 수정, 삭제 작업이 더 느리다. 데이터 페이지를 무작위로 삽입하고, 정렬 순서를 유지하지 않기 떄문이다.&lt;/p&gt;
&lt;p&gt;루트 페이지는 클러스터링 인덱스와 비슷하게 인덱스로 지정한 컬럼들과 페이지 번호를 갖고 있다. 반면 리프 노드에서 인덱스는 &lt;strong&gt;RID(데이터 페이지의 번호 + offset)&lt;/strong&gt; 을 조합한 값을 저장하여, 특정 데이터 페이지의 특정 행을 가지킨다. (RID = RowID, 데이터 주소) 이 방식을 통해 인덱스 페이지에서 특정 데이터 페이지로 이동하고, 원하는 실제 데이터를 찾을 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;클러스터링-인덱스와-비클러스터링-인덱스의-혼합&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EC%99%80-%EB%B9%84%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EC%9D%98-%ED%98%BC%ED%95%A9&quot; aria-label=&quot;클러스터링 인덱스와 비클러스터링 인덱스의 혼합 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클러스터링 인덱스와 비클러스터링 인덱스의 혼합&lt;/h2&gt;
&lt;p&gt;그런데 현실적으로, 기본적으로 생성되는 클래스터링 인덱스 외에도 비클러스터링 인덱스를 생성해야하는 경우가 대다수일 것이다. 즉, 하나의 테이블에 클러스터링 인덱스와 비클러스터링 인덱스가 혼합되어 있는 경우가 많다. &lt;strong&gt;PK 는 기본적으로 항상 존재하며, 추가로 조회가 자주 발생하는 컬럼에 대해 인덱스를 추가&lt;/strong&gt;하기 떄문이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7e2d3e5c374f3e25e6d135944a0622a8/1e093/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.828220858895705%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACG0lEQVR42n2SS2/TUBCF85e7QGIBKixAAiF+ASxhU1QVqTxES1QeggaakkKhbZqkztNObMexa8e+tuOPsd1UlSr1SiNb586ce+bMVEzTRCnFYrEgSZLiG4YhnU6bf0cndE5qbL66zdb6LX7t3MW1mmQguTFpml6Lynw+L0iunizLWKSJJCSQKjT7N35gMvcMwSNuOpVcTa4wjuOCqCQEVwXYjs/E9IQsLPBwHmHbLv2Bhel5hEl4nXA6nVKtVqnX60WrriQGgc/Pb8/ZeLnC1w9PSWOfi7fQe3tsvFjh/fodXKd/2dEloe/7OEIaSeuObdPVztCHAwJniKUf4zs9qUhZlsSRKHYHOOYZWSLtL+Qmt+zCtspkMiGJotxlyVZykYpvCZlgwczFNW2sQZvuyT7d5gFmvyOYhQoCUNFFqDJEaSWSwiQtFeTSlxEJeT4w7/wc7XiD+ucnbK6t0th7TZyUapaqM660nBMup3zVC8uy6J71GBsGkSrJh8MRnnh845Tzwlqthq7rBeD7QaHKNqdoRoexYzAST1unR7Saxxy29jFsHUMfSU2fqTVBG5/iq/OSMH+x0WjQarexTIPm/jN2tx/w5d19qm9kkWcdBqdv2d26x4+PD9ndeSRYj+bBGt+3Vqnl2KfHslLjktBxnOJH07r8PfxDHNrMZiNp0cabjQuHVDQn8GdEoS+r5RdYmiiZQ1BErEKWe/UfAQM9KQ0bAVAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7e2d3e5c374f3e25e6d135944a0622a8/a6d36/image-3.png&quot;
        srcset=&quot;/static/7e2d3e5c374f3e25e6d135944a0622a8/222b7/image-3.png 163w,
/static/7e2d3e5c374f3e25e6d135944a0622a8/ff46a/image-3.png 325w,
/static/7e2d3e5c374f3e25e6d135944a0622a8/a6d36/image-3.png 650w,
/static/7e2d3e5c374f3e25e6d135944a0622a8/e548f/image-3.png 975w,
/static/7e2d3e5c374f3e25e6d135944a0622a8/3c492/image-3.png 1300w,
/static/7e2d3e5c374f3e25e6d135944a0622a8/1e093/image-3.png 1376w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이렇게 혼합된 경우에선 &lt;strong&gt;비클러스터링 인덱스를 먼저 거치고, 이어서 클러스터링 인덱스를 거쳐서 실제 데이터를 찾는 방식&lt;/strong&gt;으로 동작한다. 이때, 비클러스터링 인덱스의 리프노드는 실제 데이터에 대한 주소값(데이터 위치 값) 대신, 클러스터링 인덱스에 대한 컬럼 값(PK 값) 을 갖는다. 실제로 위 예시를 보면 비클러스터링 인덱스의 인덱스 페이지는 앞서 살펴봤던 &lt;strong&gt;RID(데이터 페이지의 번호 + offset)&lt;/strong&gt; 대신에 PK 값을 가지고 있다. 이 PK 값에 매핑되는 클러스터링 인덱스를 찾아내고, 이후 앞서 살펴본 클러스터링 인덱스에서의 동작과정이 동일하게 일어난다.&lt;/p&gt;
&lt;h3 id=&quot;비클러스터링-인덱스는-왜-pk-값을-들고-있어야할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%8A%94-%EC%99%9C-pk-%EA%B0%92%EC%9D%84-%EB%93%A4%EA%B3%A0-%EC%9E%88%EC%96%B4%EC%95%BC%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;비클러스터링 인덱스는 왜 pk 값을 들고 있어야할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비클러스터링 인덱스는 왜 PK 값을 들고 있어야할까?&lt;/h3&gt;
&lt;p&gt;그런데 이상하다. 왜 비클러스터링 인덱스는 PK 값을 가지고 있는 것일까? 그냥 RID 값을 들고있고, 바로 원하는 실제 데이터를 찾아내도록 구조를 취하면 안될까? 만약 그렇게 된다면, 테이블에 데이터가 추가, 변경 및 삭제될 때 마다 테이블 데이터의 페이지 번호와 페이지 내 순서가 모두 변경되어야 한다. 클러스터링 인덱스는 데이터를 직접 가지고 있고, 테이블 전체가 정렬된 인덱스라고 했었다. 이런 구조에서 비클러스터링 인덱스가 RID 값을 직접 들고 있다면, 비클러스터링 인덱스를 모두 수정해야하는 문제가 발생한다. 그래서 비클러스터링 인덱스가 PK 값을 가지고 있는 구조를 취해야하는 것이다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=edpYzFgHbqs&quot;&gt;https://www.youtube.com/watch?v=edpYzFgHbqs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=NkZ6r6z2pBg&quot;&gt;https://www.youtube.com/watch?v=NkZ6r6z2pBg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gwang920.github.io/database/clusterednonclustered/#%EA%B7%B8%EB%9E%98%EC%84%9C-%EC%99%9C-clustered-%EC%9D%B8%EA%B0%80-&quot;&gt;https://gwang920.github.io/database/clusterednonclustered/#%EA%B7%B8%EB%9E%98%EC%84%9C-%EC%99%9C-clustered-%EC%9D%B8%EA%B0%80-&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developers-haven.tistory.com/55&quot;&gt;https://developers-haven.tistory.com/55&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://golf-dev.tistory.com/67&quot;&gt;https://golf-dev.tistory.com/67&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[자바 리플렉션 (Java Reflection) 다루기]]></title><description><![CDATA[자바 리플렉션 (Java Reflection)  자바는 캄파일이 시작되면, 컴파일러가 자바 코드로 작성된 클래스 정보들을 바이트 코드로 변환해준다. 그리고 클래스 로더(Class Loader) 는 변환된 이 바이트 코드를 읽어들여서 JVM…]]></description><link>https://haon.site/java/reflection/</link><guid isPermaLink="false">https://haon.site/java/reflection/</guid><pubDate>Tue, 22 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;자바-리플렉션-java-reflection&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EB%B0%94-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98-java-reflection&quot; aria-label=&quot;자바 리플렉션 java reflection permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자바 리플렉션 (Java Reflection)&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5696fd056f2da4f1b405dd5eb7332266/1d499/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.73619631901841%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABwklEQVR42pVS2W7bMBDM//9UHoIEaNG0BdxajiwnPhpalhxdFHVQx3SWdRSjOYAQIFYiubM7s3OBD5aKYszmHqI0c//DOLo4nvZb6+I9sIE7qSrMVyvERQHb93g8RJjNfmGj9u7+U4DSwTaOcX17C3/3B23XI9Ma69USqS7Qvwc4kkZTN69300Kxo5/sKDwe0VmLtCwR5QUSxqZ5eVuRieW9A+xJRQ4EeBiGact5z1gzQejKeio0fD9w0fJuPL0VMEOMCVCqvaVhlKZQjwp7Us9Lw84MQg5Ktxb2bEAyrKqqXwOO48vsen6Xbes61Lxv+wGHLMfy/gER47mG0mX9DOh+6tqBndOWb6EVM7nuOqmGnAV2UQRtO3SnN//sNDjZJsCSIh+TBL89DyUr1UwMmZjTLqJNxvhE+i3Z7El5SxkyDmenFObeAt6dD00HTIApHxcEXW+2aCiwDOHIM8POszyHHwTwFgtXRIor+lDLpKnlZruDCg8oWHTyoTEGmgeVqVAQIM8yGCaIXW6+fMXl1RWC9QYhExPeecEK377/gEfTl2QguS1ZfWhsV4hmDtjBHQch+snwKkt/ijz+EiHB/zf4X1zRoJzDy/nCAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/5696fd056f2da4f1b405dd5eb7332266/a6d36/image.png&quot;
        srcset=&quot;/static/5696fd056f2da4f1b405dd5eb7332266/222b7/image.png 163w,
/static/5696fd056f2da4f1b405dd5eb7332266/ff46a/image.png 325w,
/static/5696fd056f2da4f1b405dd5eb7332266/a6d36/image.png 650w,
/static/5696fd056f2da4f1b405dd5eb7332266/e548f/image.png 975w,
/static/5696fd056f2da4f1b405dd5eb7332266/3c492/image.png 1300w,
/static/5696fd056f2da4f1b405dd5eb7332266/1d499/image.png 1632w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;자바는 캄파일이 시작되면, 컴파일러가 자바 코드로 작성된 클래스 정보들을 바이트 코드로 변환해준다. 그리고 클래스 로더(Class Loader) 는 변환된 이 바이트 코드를 읽어들여서 JVM 내에 메모리 영역에 저장한다. &lt;strong&gt;리플렉션은 컴파일 된 이후 런타임 시점에 이 JVM 내의 메모리 영역에 저장된 클래스 객체 정보들을 꺼내온 뒤, 그 중 우리에게 필요한 상세 정보들(생성자, 필드, 메소드 등)을 추출하여 사용할 수 있게 해주는 기술&lt;/strong&gt;이다. 즉, 리플렉션을 사용하면 특정 클래스에 대한 생성자, 메소드, 필드 등의 아주 상세한 정보들을 알아낼 수 있다.&lt;/p&gt;
&lt;p&gt;이는 다시말해, 객체의 타입을 컴파일 시점 전까진 몰라도 동적으로 객체를 생성할 수 있게 해주기도 한다. 이 장점을 살려서 프레임워크나 라이브러리가 내부적으로 리플렉션을 매우 유용하게 사용하고 있다. 우리가 직접 코딩을 할때는 객체의 타입을 모르는 일이 거의 없는 반면에, 프레임워크나 라이브러리는 컴파일을 돌리기 전까진 객체의 타입을 알 수 없다. 이런 문제를 해결하기 위해 리플렉션을 사용한다. 리플렉션을 사용하면 프레임워크나 라이브러리 입장에서 컴파일 되기 전 시점에 객체의 타입을 몰라도 전혀 무관하며, 런타임 시점에 동적으로 객체의 타입을 읽어올 수 있기 때문이다.&lt;/p&gt;
&lt;h2 id=&quot;리플렉션의-유용한-실제-활용-사례&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98%EC%9D%98-%EC%9C%A0%EC%9A%A9%ED%95%9C-%EC%8B%A4%EC%A0%9C-%ED%99%9C%EC%9A%A9-%EC%82%AC%EB%A1%80&quot; aria-label=&quot;리플렉션의 유용한 실제 활용 사례 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리플렉션의 유용한 실제 활용 사례&lt;/h2&gt;
&lt;p&gt;리플렉션은 실제로 많은 프레임워크와 라이브러리에서 활용되고 있다. 어떤 부분에서 실제로 사용되고 있을까?&lt;/p&gt;
&lt;h3 id=&quot;리플렉션과-기본-생성자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98%EA%B3%BC-%EA%B8%B0%EB%B3%B8-%EC%83%9D%EC%84%B1%EC%9E%90&quot; aria-label=&quot;리플렉션과 기본 생성자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리플렉션과 기본 생성자&lt;/h3&gt;
&lt;p&gt;JPA 엔티티에서도, 단순한 DTO 에서도 모두 기본 생성자를 항상 필수로 생성할 것을 요구한다. 이렇게 기본 생성자를 필수로 생성해야하는 이유는 리플렉션 때문이다. 왜 리플렉션 떄문일까?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 위처럼 기본 생성자가 없다면 생성자 종류가 많은 경우에 어떤 생성자를 선택해서 객체를 생성할지 프레임워크가 판단하기 어렵기 떄문이다. 또한 아래와 같이 파라미터의 타입이 같은 경우, 필드와 이름이 다르다면 값을 올바르게 넣어주기 힘들기 떄문이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 기본 생성자를 사용한다면 위에서 언급한 문제점들을 제거할 수 있다. 리플렉션이 기본 생성자로 간단히 객체를 생성한 다음에, 필드 이름에 맞춰서 알맞은 값을 넣어주기만 하면 끝이다.&lt;/p&gt;
&lt;h3 id=&quot;어노테이션의-동작-원리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%98-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&quot; aria-label=&quot;어노테이션의 동작 원리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어노테이션의 동작 원리&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@Component&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@Bean&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@Service&lt;/code&gt; 와 같은 어노테이션은 대표적으로 프레임워크에서 리플렉션을 사용한 사례이다. 만약 리플렉션이 없었다면, 어노테이션 그 자체로는 아무런 역할도 하지 못한다. 스프링 프레임워크는 내부적으로 리플렉션을 사용하여 어노테이션이 붙어있는 클래스(또는 메소드, 파라미터 등) 의 정보를 가져온다. 그 다음 후술할 &lt;code class=&quot;language-text&quot;&gt;Class&lt;/code&gt; 타입의 객체 메소드 중 &lt;code class=&quot;language-text&quot;&gt;getAnnotation()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;getDeclaredAnnotation()&lt;/code&gt; 등을 호출하여 어떤 어노테이션이 붙어있는지를 확인하고, 그에 따른 로직을 수행한다.&lt;/p&gt;
&lt;h2 id=&quot;class-클래스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#class-%ED%81%B4%EB%9E%98%EC%8A%A4&quot; aria-label=&quot;class 클래스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Class 클래스&lt;/h2&gt;
&lt;p&gt;앞서 설명했기를, 리플렉션을 사용하여 객체의 타입을 컴파일 시점 전까진 몰라도 동적으로 객체를 생성할 수 있게 해준다. 동적인 객체 타입을 수용할 수 있게 해주는 것이 바로 &lt;code class=&quot;language-text&quot;&gt;Class&lt;/code&gt; 클래스이다. 즉, &lt;code class=&quot;language-text&quot;&gt;Class&lt;/code&gt; 타입 객체를 선언하면 런타임 시점에 주입될 실제 객체 타입이 그 무엇이던간에 모두 생성할 수 있게된다.&lt;/p&gt;
&lt;p&gt;Class 타입의 객체를 통해 클래스에 붙은 어노테이션을 조회할 수 있고, 클래스가 가진 생성자나 필드, 메소드에 대한 정보를 조회할 수 있다. 심지어 클래스의 부모 클래스 또는 구현하고 있는 인터페이스까지 정보를 조회할 수 있다. 그리고 Class 타입 객체는 public 생성자가 존재하지 않고, JVM 에 의해 자동으로 객체가 생성된다.&lt;/p&gt;
&lt;h3 id=&quot;class-객체-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#class-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;class 객체 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Class 객체 생성&lt;/h3&gt;
&lt;p&gt;그렇다면 Class 객체를 어떻게 생성할 수 있을까? 우선 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 클래스의 &lt;code class=&quot;language-text&quot;&gt;.class&lt;/code&gt; 속성을 통해 획득할 수 있다. 그리고 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 객체의 &lt;code class=&quot;language-text&quot;&gt;getClass()&lt;/code&gt; 메소드를 사용하는 방법도 있으며, &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; Class 클래스의 &lt;code class=&quot;language-text&quot;&gt;forName()&lt;/code&gt; 메소드에 패키지 경로를 전달하여 해당 경로에 대응하는 클래스에 대한 &lt;code class=&quot;language-text&quot;&gt;Class&lt;/code&gt; 타입의 객체를 얻을 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; clazz1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; member1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; clazz2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; member1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; clazz3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;moheng.member.domain&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (3)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;getxxx-getdelaredxxx&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#getxxx-getdelaredxxx&quot; aria-label=&quot;getxxx getdelaredxxx permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;getXXX(), getDelaredXXX()&lt;/h3&gt;
&lt;p&gt;Class 객체의 메소드는 크게 &lt;code class=&quot;language-text&quot;&gt;getFields()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;getMethods()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;getAnnotations()&lt;/code&gt; 와 같은 형태와 &lt;code class=&quot;language-text&quot;&gt;getDeclaredFields()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;getDelcaredMethods()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;getDeclaredAnnotations()&lt;/code&gt; 와 같은 형태 2가지로 메소드가 정의되어 있다. 이 메소드들은 클래스에 정의된 필드, 메소드, 어노테이션 목록을 가져오기 위해 사용된다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;getXXX()&lt;/code&gt; 형태의 메소드는 &lt;strong&gt;상속받은 클래스와 인터페이스를 포함하여 모든 public 요소를 가져온다.&lt;/strong&gt; 예를들어 &lt;code class=&quot;language-text&quot;&gt;getAnnotations()&lt;/code&gt; 는 파라미터로 넣어준 클래스 타입이 붙어있는 모든 어노테이션을 가져온다. &lt;code class=&quot;language-text&quot;&gt;getMethods()&lt;/code&gt; 는 파라미터로 넘겨준 클래스가 상속받은, 그리고 구현한 인터페이스에 대한 모든 public 메소드를 가져온다.&lt;/p&gt;
&lt;p&gt;반면 &lt;code class=&quot;language-text&quot;&gt;getDeclaredXXX()&lt;/code&gt; 형태의 메소드는 상속받은 클래스와 인터페이스를 제외하고 해당 클래스에 직접 정의된 내용만 가져온다. 또한 접근 제어자와 무관하게 요소에 접근한다. 예를들어 &lt;code class=&quot;language-text&quot;&gt;getDelcaredMethods()&lt;/code&gt; 는 해당 클래스에만 직접 정의된 private, protected, public 메소드를 모두 가져온다.&lt;/p&gt;
&lt;h2 id=&quot;어노테이션-가져오기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0&quot; aria-label=&quot;어노테이션 가져오기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어노테이션 가져오기&lt;/h2&gt;
&lt;p&gt;앞서 게속 설명했듯이, 스프링은 리플렉션을 사용하여 어노테이션이 붙어있는 클래스(또는 메소드, 파라미터 등) 의 정보를 Clsss 타입의 객체로 가져온다. 이후 &lt;code class=&quot;language-text&quot;&gt;getAnnotation()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;getDeclaredAnnotation()&lt;/code&gt; 등을 호출하여 어떤 어노테이션이 붙어있는지를 확인하고, 그에 따른 로직을 수행한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; member &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Entity&lt;/span&gt; entityAnnotation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; member&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAnnotation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Entity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// @Entity 어노테이션을 가져옴&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entityAnnotation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;value = &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;getAnnotation()&lt;/code&gt; 은 위와 같이 사용할 수 있다. 위처럼 메소드에 직접 어노테이션 타입을 넣어주면, 클래스에 붙어있는 어노테이션을 가져올 수 있다. 또한 어노테이션이 가지고 있는 필드에도 접근할 수 있는 것을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;Class 를 사용하면 어노테이션 외에도 생성자를 &lt;code class=&quot;language-text&quot;&gt;Constructor&lt;/code&gt; 타입으로 가져올 수 있고, 객체 필드를 &lt;code class=&quot;language-text&quot;&gt;Field&lt;/code&gt; 로 접근할 수 있으며, 객체 메소드를 &lt;code class=&quot;language-text&quot;&gt;Method&lt;/code&gt; 타입으로 접근할 수 있다. 자세한 설명은 생략한다.&lt;/p&gt;
&lt;h2 id=&quot;entitymanager-와-리플렉션을-활용하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#entitymanager-%EC%99%80-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0&quot; aria-label=&quot;entitymanager 와 리플렉션을 활용하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EntityManager 와 리플렉션을 활용하기&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://haon.blog/test/isolated-active/&quot;&gt;격리된 테스트(Isolated Test) 구축과 빌드 최적화 여정 - 실전편&lt;/a&gt; 에서 다루었듯이, 테스트 격리 환경에서 자바 리플렉션을 사용했던 경험이 있다. 각 테스트가 실행되기 직전에 호출되어 DB를 초기 상태로 만들어주는 DB Cleaner 에서 리플렉션이 사용되었다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DatabaseCleaner&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EntityManager&lt;/span&gt; entityManager&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; tableNames&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DatabaseCleaner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EntityManager&lt;/span&gt; entityManager&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;entityManager &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entityManager&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tableNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entityManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMetamodel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntities&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getJavaType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;javaType &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; javaType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAnnotation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Table&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        entityManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flush&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        entityManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createNativeQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;SET foreign_key_checks = 0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; tableName &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; tableNames&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            entityManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createNativeQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;TRUNCATE TABLE &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; tableName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        entityManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createNativeQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;SET foreign_key_checks = 1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 DB Cleaner 는 EntityManager 를 통해 모든 Entity 를 가져오고, 자바 리플렉션을 통해 Entity 의 &lt;code class=&quot;language-text&quot;&gt;@Table&lt;/code&gt; 어노테이션이 달린 클래스의 테이블 명을 가져오는 방식으로 동작한다. 보듯이 &lt;code class=&quot;language-text&quot;&gt;getEntities()&lt;/code&gt; 로 모든 엔티티를 가져왔으며, &lt;code class=&quot;language-text&quot;&gt;getAnnotation()&lt;/code&gt; 을 통해 &lt;code class=&quot;language-text&quot;&gt;@Table&lt;/code&gt; 어노테이션이 달린 각 Table 어노테이션 객체들에 대해 name (테이블 명)을 추출하고 있다. 그렇게 가져온 테이블 명 리스트를 활용하여 TRUNATE TABLE 쿼리를 날려서 깨끗하게 데이터베이스를 초기화한다.&lt;/p&gt;
&lt;h2 id=&quot;리플렉션의-단점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98%EC%9D%98-%EB%8B%A8%EC%A0%90&quot; aria-label=&quot;리플렉션의 단점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리플렉션의 단점&lt;/h2&gt;
&lt;p&gt;리플렉션을 사용시 많은 단점을 가지고 있기 떄문에, 일반적인 상황에서 사용할 일이 거의 없다. 일반적으로 메소드를 호출하면, 컴파일 시점에 분석된 클래스를 사용하지만 리플렉션은 런타임 시점에 클래스를 분석하므로 속도가 매우 느리다. 또한 타입 체크가 컴파일 타이밍에 불가능하여 애플리케이션의 신뢰도를 깨뜨릴 원인이 될 수 있다. 그리고 객체의 추상화가 깨진다는 점도 존재한다.&lt;/p&gt;
&lt;p&gt;보통 리플렉션은 라이브러리나 프레임워크를 개발할 때 사용된다. 따라서 정말 필요한 곳에만 리플렉션을 한정적으로 사용하도록 하자.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=67YdHbPZJn4&quot;&gt;https://www.youtube.com/watch?v=67YdHbPZJn4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=RZB7_6sAtC4&amp;#x26;t=197s&quot;&gt;https://www.youtube.com/watch?v=RZB7_6sAtC4&amp;#x26;t=197s&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hamait.tistory.com/317&quot;&gt;https://hamait.tistory.com/317&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ysiksik.github.io/elegant-tekotok/2023-11-08-HOPK-JAVA-Reflection/&quot;&gt;https://ysiksik.github.io/elegant-tekotok/2023-11-08-HOPK-JAVA-Reflection/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[HTTP 웹 서버를 직접 구현하여 프로토콜 스펙을 이해해보자!]]></title><description><![CDATA[학습 배경 HTTP…]]></description><link>https://haon.site/haon/spring/was-implementation/</link><guid isPermaLink="false">https://haon.site/haon/spring/was-implementation/</guid><pubDate>Sat, 19 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습-배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5-%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습 배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습 배경&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/Durable-developers/Next-Step-HTTP-Web-Server-Implementation.git&quot;&gt;HTTP 웹 서버 직접 구현하기 미션&lt;/a&gt; 을 진행하면서 많은 시간을 할애했다. 고민했던 과정들이 정말 많았기에 재밌는 실습을 진행할 수 있었다 🙂 이번에 구현에 대한 내용과 과정들을 자세히 다루어보고자 한다.&lt;/p&gt;
&lt;p&gt;요구사항에 대한 상세한 설명은 생략한다. 어떻게 요구사항을 준수하면서 HTTP 웹 서버를 구현했는지에 대해 중점으로 다루어 보고자 한다. 또 요구사항을 해결해가는 과정에서 HTTP 프로토콜 스펙 및 쿠키등 여러 지식에 대한 이론을 다루겠다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;웹서버를-구현할-메인-클래스-역할&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%B9%EC%84%9C%EB%B2%84%EB%A5%BC-%EA%B5%AC%ED%98%84%ED%95%A0-%EB%A9%94%EC%9D%B8-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%97%AD%ED%95%A0&quot; aria-label=&quot;웹서버를 구현할 메인 클래스 역할 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;웹서버를 구현할 메인 클래스 역할&lt;/h2&gt;
&lt;p&gt;HTTP 웹서버를 구현하기 위해, 2가지 클래스를 중점으로 구현해 나가도록 하겠다. 각 요구사항을 해결해나가는 과정속에서 HTTP 프로토콜에 대해 다루도록 한다.&lt;/p&gt;
&lt;h3 id=&quot;webserver&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webserver&quot; aria-label=&quot;webserver permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;WebServer&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;WebServer&lt;/code&gt; 클래스는 웹 서버를 시작하고, 클라이언트의 요청이 있을 때 까지 대기 상태에 있다가, &lt;strong&gt;요청이 들어올 경우 해당 요청을 RequestHandler 클래스에게 위임하는 역할을 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;사용자 요청이 발생할 때 까지 대기 상태에 있도록 지원하는 역할은 자바의 &lt;code class=&quot;language-text&quot;&gt;ServerSocket&lt;/code&gt; 클래스가 담당한다. ServerSocket 에 클라이언트 요청이 들어오는 순간, 클라이언트와 연결을 담당하는 Socket 을 RequestHandler 에 전달하면서 새로운 쓰레드를 실행하는 방식으로 멀티쓰레드 프로그래밍을 지원하고 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebServer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt; log &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;WebServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_PORT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;args &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_PORT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// 서버소켓을 생성한다. 웹서버는 기본적으로 8080번 포트를 사용한다.&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ServerSocket&lt;/span&gt; listenSocket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServerSocket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Web Application Server started {} port.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; port&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token class-name&quot;&gt;Socket&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Socket: 클라이언트와의 연결을 담당. 클라이언트가 연결될 때 까지 (요청이 들어올 때 까지) 대기한다.&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;connection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; listenSocket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            	&lt;span class=&quot;token comment&quot;&gt;// 요청이 들어오면 해당 요청을, 즉 Socket을 RequestHandler 에 전달하면서 요청에 대한 처리를 위임한다.&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;RequestHandler&lt;/span&gt; requestHandler &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;connection&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                requestHandler&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 요청 처리 시작&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;requesthandler&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#requesthandler&quot; aria-label=&quot;requesthandler permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RequestHandler&lt;/h3&gt;
&lt;p&gt;RequestHandler 클래스는 앞서 말했듯이 &lt;code class=&quot;language-text&quot;&gt;WebServer&lt;/code&gt; 클래스로 부터 전달받은 Socket 을 전달받고 클라이언트의 요청을 처리한다. 정확히는 이 클래스 내부의 &lt;code class=&quot;language-text&quot;&gt;run()&lt;/code&gt; 메소드에서 클라이언트 요청 처리 코드를 구현할 것이다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;InputStream&lt;/code&gt; 은 클라이언트(웹 브라우저) 에서 서버로 요청을 보낼 때 전달되는 데이터를 담당하는 스트림이다. 반면 &lt;code class=&quot;language-text&quot;&gt;OutputStream&lt;/code&gt; 은 반대로 서버에서 클라이언트에 응답을 보낼 때 전달하는 데이터를 담당하는 스트림이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestHandler&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt; log &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RequestHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;DataBase&lt;/span&gt; dataBase &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataBase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Socket&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Socket&lt;/span&gt; connectionSocket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; connectionSocket&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;New Client Connect! Connected IP : {}, Port : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInetAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InputStream&lt;/span&gt; in &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInputStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			 &lt;span class=&quot;token class-name&quot;&gt;OutputStream&lt;/span&gt; out &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOutputStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

         	&lt;span class=&quot;token comment&quot;&gt;// TODO: 사용자 요청에 대한 처리는 여기서 구현된다.&lt;/span&gt;

         &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;response200Header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DataOutputStream&lt;/span&gt; dos&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; lengthOfBodyContent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            dos&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;HTTP/1.1 200 OK \r\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            dos&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Type: text/html;charset=utf-8\r\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            dos&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Length: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; lengthOfBodyContent &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\r\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            dos&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\r\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;정리하자면, WebServer 는 클라이언트(웹 브라우저) 의 요청을 언제든지 받을 수 있도록 Socket 을 생성해두고 대기 상태에 있는다. 그러다 요청이 들어오면, 해당 요청을 RequestHandler 에게 처리하도록 떠넘기는 방식이다. 처리가 완료 되었다면, RequestHandler 는 응답을 WebServer 에게 보낸다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;http-프로토콜-스펙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C-%EC%8A%A4%ED%8E%99&quot; aria-label=&quot;http 프로토콜 스펙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP 프로토콜 스펙&lt;/h2&gt;
&lt;p&gt;요구사항을 해결하기 위해 HTTP 의 표준 스펙에 대해 이해해야한다. 웹 클라이언트는 웹 서버와 데이터를 주고받기 위해 HTTP 라는 서로간의 약속된 규약을 따른다. 웹 클라이언트가 웹 서버에 요청을 전송하기 위한 규약을 이해하기 위해, 아래 스펙을 예를 들어보곘다.&lt;/p&gt;
&lt;h3 id=&quot;요청request-메시지-규약&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%94%EC%B2%ADrequest-%EB%A9%94%EC%8B%9C%EC%A7%80-%EA%B7%9C%EC%95%BD&quot; aria-label=&quot;요청request 메시지 규약 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;요청(Request) 메시지 규약&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;c&quot;&gt;&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;POST &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;user&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;create HTTP&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// (1) 요청 라인(Request Line)&lt;/span&gt;
HOST&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; localhost&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// (2) 요청 헤더(Request Header)&lt;/span&gt;
Connection&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Length&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;59&lt;/span&gt;
Content&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; application&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;x&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;wwww&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;form&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;unlencoded
Accept&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/*

userId=msung99&amp;amp;password=password  // (3) 요청 본문(Request Body)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;(1) 요청 라인(Request Line)&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에 해당하는 내용이다. 요청 데이터의 첫번째 라인은 요청 라인(Request Line) 이라고 부른다. 요청 라인은 &lt;code class=&quot;language-text&quot;&gt;HTTP-메소드 URI HTTP-버전&lt;/code&gt; 의 형태로 구성된다. HTTP 메소드는 요청의 종류를 나타내며, HTTP 버전의 경우 현재 HTTP/1.1 을 기준으로 한다.&lt;/p&gt;
&lt;h4&gt;(2) 요청 헤더(Request Header)&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 에 해당하는 내용이다. 요청 헤더는 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;필드 이름&gt; : &amp;lt;필드 값&gt;&lt;/code&gt; 형태의 key-value 쌍으로 구성된다.&lt;/p&gt;
&lt;h4&gt;(3) 요청 본문(Request Body)&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 에 해당하는 내용이다. 헤더와 본문 사이에 빈 공백 라인 1줄을 두고 본문이 주어진다.&lt;/p&gt;
&lt;h3 id=&quot;응답response-메시지-규약&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%91%EB%8B%B5response-%EB%A9%94%EC%8B%9C%EC%A7%80-%EA%B7%9C%EC%95%BD&quot; aria-label=&quot;응답response 메시지 규약 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;응답(Response) 메시지 규약&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;c&quot;&gt;&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;HTTP&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt; OK  &lt;span class=&quot;token comment&quot;&gt;// (1) 상태 라인(Status Line)&lt;/span&gt;
Content&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; text&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;html&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;charset&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;utf&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// (2) 응답 헤더(Response Header)&lt;/span&gt;
Content&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Length&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Hello World&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (3) 응답 본문(Response Body)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;응답 메시지는 요청 메시지의 규약과 유사한 구조를 지닌다. 다만 다른점이라면 첫번째 라인 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 의 상태라인의 형식이 다르다는 것이다.&lt;/p&gt;
&lt;h4&gt;상태라인(Status Line)&lt;/h4&gt;
&lt;p&gt;응답 헤더의 첫번째 라인은 상태 라인이라고 부른다. &lt;code class=&quot;language-text&quot;&gt;HTTP-버전 상태코드 응답구문&lt;/code&gt; 의 구조를 취하고 있다.&lt;/p&gt;
&lt;p&gt;이렇게까지 HTTP 요청과 응답의 기본 구조(규약) 에 대해 살펴봤다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;요구사항1-indexhtml-을-응답한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD1-indexhtml-%EC%9D%84-%EC%9D%91%EB%8B%B5%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;요구사항1 indexhtml 을 응답한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;요구사항1. index.html 을 응답한다.&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;요구사항 : &lt;a href=&quot;http://localhost:8080/index.html&quot;&gt;http://localhost:8080/index.html&lt;/a&gt; 로 접속했을 때 webapp 디렉토리의 index.html 파일을 읽어 클라이언트에 응답한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;basiccode&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#basiccode&quot; aria-label=&quot;basiccode permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BasicCode&lt;/h3&gt;
&lt;p&gt;이를 만족시키기 위한 코드는 아래와 같이 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;New Client Connect! Connected IP : {}, Port : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInetAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    	connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

     &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InputStream&lt;/span&gt; in &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInputStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OutputStream&lt;/span&gt; out &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOutputStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token class-name&quot;&gt;DataOutputStream&lt;/span&gt; dos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataOutputStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;token class-name&quot;&gt;BufferedReader&lt;/span&gt; br &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BufferedReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
							    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InputStreamReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;in&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;//(1)&lt;/span&gt;
          &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; line &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; br&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
          log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;request line : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

           &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;line &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
           		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
           &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

           &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; tokens &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//  (3)&lt;/span&gt;
           &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (4)&lt;/span&gt;
             	line &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; br&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;header : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
           &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

           &lt;span class=&quot;token keyword&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; body &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readAllBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
           			&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./webapp&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; tokens&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (5)&lt;/span&gt;
           &lt;span class=&quot;token function&quot;&gt;response200Header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dos&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
           &lt;span class=&quot;token function&quot;&gt;responseBody&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dos&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      		log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;BufferedReader 로 헤더 값 읽어오기&lt;/h4&gt;
&lt;p&gt;조금씩 뜯어보자. 우선 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에서 &lt;code class=&quot;language-text&quot;&gt;BufferdReader&lt;/code&gt; 를 이용해 헤더 값을 읽는다.&lt;/p&gt;
&lt;h4&gt;요청 라인(Request Line) 읽어오기&lt;/h4&gt;
&lt;p&gt;곧바로 바로 아래 라인 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 에서 BufferedReader 를 활용해 &lt;code class=&quot;language-text&quot;&gt;요청 라인(Request Line)&lt;/code&gt; 을 읽어오게 되는데, 이렇게 읽어온 헤더의 첫번째 줄에는 HTTP 메소드, 요청 URL, HTTP 버전이 공백을 사이에 두고 들어온다. (ex. GET /index.html HTTP/1.1)&lt;/p&gt;
&lt;h4&gt;요청 라인을 split 하여 문자열 배열에 담기&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 에서는 요청 라인을 공백을 기준으로 자르는 모습을 볼 수 있다. 즉, tokens 라는 문자열 배열에 &quot;GET&quot;, &quot;/index.html&quot;, &quot;HTTP/1.1&quot; 에 담기게 될 것이다.&lt;/p&gt;
&lt;h4&gt;헤더 정보 출력하기&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 에서는 반복문을 계속 순환하면서 헤더의 나머지 데이터를 모두 출력한다. &quot;/index.html&quot; 로 요청을 보냈을 때 헤더를 출력한 결과는 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/bc3cac76-1cc7-452d-bef1-23f9e0330ba8/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그런데 이때 분명 index.html 로 요청을 단 1번을 보냈을 뿐인데 1번의 요청이 아니라 여러번의 추가 요청이 발생하는 것을 확인할 수 있다. 이렇게 많은 요청이 발생한 이유는 서버가 웹 페이지를 구성하는 모든 리소스(html, css, js, 이미지 등) 을 한번에 응답으로 보내지 않기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;웹 서버는 첫번쨰로 /index.html 요청에 대한 응답에 HTML 만을 보낸다. 응답을 받은 브라우저는 HTML 내용을 분석하여 css, js , 이미지등의 자원이 포함되어 있다면 서버에 해당 자원을 다시 요청하게 된다.&lt;/strong&gt; 따라서 하나의 웹 페이지를 사용자에게 정상적으로 서비스하려면 클라이언트와 서버간의 1번의 요청이 아닌 여러번의 요청과 응답을 주고받게 된다.&lt;/p&gt;
&lt;h4&gt;Response Body 구성하기&lt;/h4&gt;
&lt;p&gt;마지막으로 &lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; 에서는 URL 해당하는 파일을 가지고와서 byte array 로 변환 후 body에 넣어준다. 앞서 살펴봤듯이 tokens 배열에는 헤더의 &lt;code class=&quot;language-text&quot;&gt;요청 라인(Request Line)&lt;/code&gt; 이 공백을 기준으로 담기는데, 2번째 요소인 tokens[1] 에는 URL 이 담기게 된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;요구사항2-get-방식으로-회원가입한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD2-get-%EB%B0%A9%EC%8B%9D%EC%9C%BC%EB%A1%9C-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;요구사항2 get 방식으로 회원가입한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;요구사항2. GET 방식으로 회원가입한다.&lt;/h2&gt;
&lt;p&gt;이 요구사항을 요약하면 아래와 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&quot;회원가입&quot; 메뉴를 클릭하면 /user/form.html 로 이동하면서 회원가입을 진행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;회원가입을 진행하면 다음과 같은 형태로 사용자가 입력한 값이 서버로 전달된다.
&quot;/user/create?userId=msung99&amp;#x26;password=msung1234&amp;#x26;name=minsung&amp;#x26;email=&lt;a href=&quot;mailto:msung99@gmail.com&quot;&gt;msung99@gmail.com&lt;/a&gt;&quot;&lt;/li&gt;
&lt;li&gt;HTML 과 URL 을 비교해보고 사용자가 입력한 값을 파싱하여 User 클래스에 저장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;run( ) 메소드를 아래와 같이 개선해줘야하는데, 이를 위해 /user/create 에서 회원가입 버튼을 클릭시 전송되는 URL 는 아래와 같은 형식으로 전송된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;http&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;localhost&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;user&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;create&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;userId&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;msung99&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;password&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;msung1234&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;minsung&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;email&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;msung99&lt;span class=&quot;token annotation punctuation&quot;&gt;@gmail.com&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;쿼리 스트링(QueryStrig) 으로, 즉 URL 에 파라미터로 회원가입시 form 에 입력한 유저의 정보가 실리는 것을 확인할 수 있다. 우리는 이 URL 을 통해 전달된 유저 데이터를 기반으로 서버의 DB 에다 유저 데이터를 저장하는 로직을 구현해야한다.&lt;/p&gt;
&lt;p&gt;다시 말하자면, /user/create로 URL이 시작될때 URL의 뒷부분인 QueryString 값을 가져와서 parseQueryString 메소드를 이용해
각각의 값을 User 객체 생성자에 param값으로 넣어주는 로직을 구현하면 된다.&lt;/p&gt;
&lt;h3 id=&quot;get---signup&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#get---signup&quot; aria-label=&quot;get   signup permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GET - SignUp&lt;/h3&gt;
&lt;p&gt;물음표 &quot;?&quot; 이후에 유저 정보가 실리는 것을 볼 수 있는데, 이 URL 문자열을 파싱하여 입력된 유저 데이터를 추출하는 코드는 아래와 같이 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;New Client Connect! Connected IP : {}, Port : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInetAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

     &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InputStream&lt;/span&gt; in &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInputStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OutputStream&lt;/span&gt; out &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOutputStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     	&lt;span class=&quot;token class-name&quot;&gt;DataOutputStream&lt;/span&gt; dos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataOutputStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;BufferedReader&lt;/span&gt; br &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BufferedReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InputStreamReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;in&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; line &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; br&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;request line : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;line &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; tokens &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        	line &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; br&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;header : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tokens&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/user/create&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        	&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; queryString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpRequestUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseQueryString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;queryString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; userId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;userId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;password&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// TODO: 추후 DB 구현 필요&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
             &lt;span class=&quot;token keyword&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; body &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readAllBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./webapp&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; tokens&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
             &lt;span class=&quot;token function&quot;&gt;response200Header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dos&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
             &lt;span class=&quot;token function&quot;&gt;responseBody&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dos&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&quot;/user/create&quot; 로 요청이 들어올 경우 사용자가 입력한 값을 파싱하여 User 클래스에 저장하는 모습을 볼 수 있다. 추후 DB 가 구축됨에 따라 생성된 User 객체를 저장하는 로직을 구현할 예정이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;요구사항3-post-방식으로-회원가입한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD3-post-%EB%B0%A9%EC%8B%9D%EC%9C%BC%EB%A1%9C-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;요구사항3 post 방식으로 회원가입한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;요구사항3. POST 방식으로 회원가입한다.&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;요구사항 : /user/form.html 파일의 form 태그 메소드를 get 에서 post 로 수정한 후 회원가입 기능이 정상적으로 동작하도록 구현한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;GET 방식으로 요청했던 회원가입 요청을 POST 로 변경해야한다. 때문에 /user/form.html 의 question form 태그의 method 를 get 에서 post 로 변경해주었다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;question&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/user/create&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;변경해줬다면, GET 방식으로 요청할 때 URL 에 포함되어 있던 쿼리 스트링이 없어지고 method 가 GET 에서 POST 로 변경되었다. 요청 URL 에 포함되어 있던 쿼리 스트링의 내용물은 HTTP 요청의 &lt;code class=&quot;language-text&quot;&gt;Request Body(요청 본문)&lt;/code&gt; 을 통해 대신 전달된다. 또한 POST 방식으로 데이터를 전달하면서 헤더에 본문 데이터에 대한 길이가 &lt;code class=&quot;language-text&quot;&gt;Content-Length&lt;/code&gt; 라는 필드 이름으로 전달된다.&lt;/p&gt;
&lt;h3 id=&quot;post---signup&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#post---signup&quot; aria-label=&quot;post   signup permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Post - SignUp&lt;/h3&gt;
&lt;p&gt;이를 고려한 구현 코드는 아래와 같이 개선해줬다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;New Client Connect! Connected IP : {}, Port : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInetAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

     &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InputStream&lt;/span&gt; in &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInputStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OutputStream&lt;/span&gt; out &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOutputStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     	&lt;span class=&quot;token class-name&quot;&gt;DataOutputStream&lt;/span&gt; dos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataOutputStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;BufferedReader&lt;/span&gt; br &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BufferedReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InputStreamReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;in&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; line &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; br&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;request line : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;line &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; tokens &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; contentLength &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        	line &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; br&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;header : {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Length&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
            	contentLength &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getContentLength&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;line&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tokens&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/user/create&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        	&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; body &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;br&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; contentLength&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (3)&lt;/span&gt;

            &lt;span class=&quot;token comment&quot;&gt;// (4)&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpRequestUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseQueryString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; userId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;userId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;password&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// TODO: 추후 DB 구현 필요&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
             &lt;span class=&quot;token keyword&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; body &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readAllBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./webapp&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; tokens&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
             &lt;span class=&quot;token function&quot;&gt;response200Header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dos&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
             &lt;span class=&quot;token function&quot;&gt;responseBody&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dos&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;우선 요청 본문에 담을 데이터의 길이(크기) 를 저장할 변수를 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에서 선언해줬다. 이후 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 에서 헤더의 여러 key 값중 &lt;code class=&quot;language-text&quot;&gt;Content-Length&lt;/code&gt; 에 대한 value 값, 즉 요청 본문의 길이값을 추출한다.&lt;/p&gt;
&lt;p&gt;이렇게 구한 길이만큼 &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 처럼 본문을 읽는다. 본문을 읽는 기능은 &lt;code class=&quot;language-text&quot;&gt;IOUTils.readData()&lt;/code&gt; 로 구현했다. 이후 본문 데이터를 &lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 처럼 Map&amp;#x3C;String, String&gt; 형태로 변환하면 된다. 요구사항2 를 구현했을땐 유저 데이터를 URL 의 쿼리 스트링으로 전달 받았다면, 이젠 요청의 바디(Request Body) 로 부터 유저 데이터를 전달받는 방식이다.&lt;/p&gt;
&lt;p&gt;추가적으로 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 에서 본문의 길이값을 추출할 때 getContentLength 라는 메소드를 활용한 것을 볼 수 있다. 이에대한 구현은 아래와 같이 진행해줬다. 헤더의 한 줄을 읽어왔을 때 &quot;Content-Length: 80&quot; 과 같은 형식으로 읽어올텐데, 이를 &quot;:&quot; 로 split 한 후 80에 해당하는 값을 Integer.parseInt() 로 형식을 변환하여 리턴하는 기능이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getContentLength&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; headerTokens &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;headerTokens&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;요구사항4-302-status-code-를-적용한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD4-302-status-code-%EB%A5%BC-%EC%A0%81%EC%9A%A9%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;요구사항4 302 status code 를 적용한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;요구사항4. 302 Status Code 를 적용한다.&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;요구사항 : 회원가입을 완료하면 /index.html 페이지로 이동해야한다. 현재는 URL 이 /user/create 로 유지되는 상태로 읽어서 전달할 파일이 없다. 따라서 회원가입을 완료한 후 /index.html 페이지로 이동한다. 브라우저의 URL 도 /user/create 가 아니라 /index.html 로 변경해야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이를 구현하기 위해선, 선수지식으로 HTTP 의 상태코드 값을 이해하면 좋다. 이미 잘 알고있지만, 복습 겸 한번 되짚어보겠다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;200번대 : 성공. 클라이언트가 요청한 동작을 수신하여 이해하고 승인했으며 성공적으로 처리했음을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;300번대 : 리다이렉션. 클라이너트는 요청을 마치기 위해 추가 동작이 필요하다.&lt;/li&gt;
&lt;li&gt;400번대 : 요청 오류. 클라이언트에 오류가 있다.&lt;/li&gt;
&lt;li&gt;500번대 : 서버 오류. 서버가 유효한 요청을 명백하게 수행하지 못했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;회원가입을 처리하는 &quot;/user/create&quot; 요청과 첫 화면 &quot;/index.html&quot; 을 보여주는 요청을 분리한 후 HTTP 의 302 상태 코드를 활용해야한다. 즉, 웹 서버는 &quot;/user/create&quot; 요청을 받아 회원가입을 완료한 후 응답을 보낼 때 클라이언트(웹 브라우저) 에게 &quot;/index.html&quot; 로 이동하도록 할 수 있다. 이때 사용하는 상태 코드가 &lt;code class=&quot;language-text&quot;&gt;302&lt;/code&gt; 이다. &quot;/index.html&quot; 로 이동하도록 응답을 보낼 때 사용하는 응답 헤더는 &lt;code class=&quot;language-text&quot;&gt;Location&lt;/code&gt; 으로 다음과 같이 응답을 보내면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;HTTP&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;302&lt;/span&gt; Found
Location&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;html&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 처럼 응답을 보내면 클라이언트는 첫 라인의 상태 코드를 확인 후 302라면 Location 값을 읽어서 서버에 재요청을 보내게 된다. 재 요청을 보내면 클라이언트의 요청은 회원가입 처리를 위한 &quot;/user/create&quot; 으로의 요청이 아니라 &quot;/index.html&quot; 으로의 요청으로 변경된다. 이 상태에서 URL 을 확인해보면 &quot;/user/create&quot; 가 아닌 &quot;/index.html&quot; 로 변경된 것을 확인할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;302-를-적용한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#302-%EB%A5%BC-%EC%A0%81%EC%9A%A9%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;302 를 적용한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;302 를 적용한다.&lt;/h3&gt;
&lt;p&gt;구현 코드는 아래와 같다. 가장 눈여겨 볼 점은 &lt;code class=&quot;language-text&quot;&gt;response302Header()&lt;/code&gt; 라는 메소드를 생성해줬으며, 이를 호출한다는 점이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tokens&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/user/create&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; body &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;br&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; contentLength&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... (이전 구현 코드와 동일)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;response302Header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dos&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/index.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;response302header&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#response302header&quot; aria-label=&quot;response302header permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;response302Header&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;response302Header()&lt;/code&gt; 의 구현 코드는 아래와 같다. 앞서 말한 302 상태 코드를 적절히 리턴하고 있으며, 302 상태 코드값이 확인 되었을때 리다이렉션 될 URL 값(즉, &quot;/index.html&quot;) 을 &lt;code class=&quot;language-text&quot;&gt;Location&lt;/code&gt; 에 할당해줬다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;response302Header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DataOutputStream&lt;/span&gt; dos&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	dos&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;HTTP/1.1 302 Found \r\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        dos&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Location: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\r\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        dos&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\r\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;요구사항5-로그인한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD5-%EB%A1%9C%EA%B7%B8%EC%9D%B8%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;요구사항5 로그인한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;요구사항5. 로그인한다.&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&quot;로그인&quot; 메뉴를 클릭하면 &quot;/user/login.html&quot; 로 이동해 로그인할 수 있다. 로그인이 성공하면 &quot;/index.html&quot; 로 이동하고, 로그인에 실패하면 &quot;/user/login_failed.html&quot; 로 이동해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;앞에서 회원가입한 사용자로 로그인될 수 이썽야한다. 로그인이 성공하면 로구인 상태를 유지할 수 있어야한다. 로그인이 성공할 경우 요청 헤더의 Cookie 값이 logined=true, 실패하면 logined=false 로 전달되어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;쿠키cookie-의-등장배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BF%A0%ED%82%A4cookie-%EC%9D%98-%EB%93%B1%EC%9E%A5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;쿠키cookie 의 등장배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쿠키(Cookie) 의 등장배경&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;HTTP 는 무상태 프로토콜이다.&lt;/strong&gt; 요청을 보내고 응답을 받으면 클라이언트와 서버간의 연결을 바로 끊는다. 연결을 끊기 때문에 각 요청 사이에 상태를 공유할 수가 없다.&lt;/p&gt;
&lt;p&gt;무상태 프로토콜이므로, 서버는 클라이언트가 누구인지 식별할 수가 없다. 서버가 클라이언트를 식별할 수 없기 떄문에 앞에서 클라이언트가 한 행위를 기억할 수 없다. 가령 로그인을 완료한다고 한들, 매 요청마다 다시 로그인을 시도하지 않을 것이다.&lt;/p&gt;
&lt;h3 id=&quot;http-의-쿠기-지원-플로우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-%EC%9D%98-%EC%BF%A0%EA%B8%B0-%EC%A7%80%EC%9B%90-%ED%94%8C%EB%A1%9C%EC%9A%B0&quot; aria-label=&quot;http 의 쿠기 지원 플로우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP 의 쿠기 지원 플로우&lt;/h3&gt;
&lt;p&gt;이를 위해 등장한 것이 &lt;code class=&quot;language-text&quot;&gt;쿠키(Cookie)&lt;/code&gt; 이다. HTTP 가 쿠키를 지원하는 방법은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; : 먼저 서버에서 로그인 요청을 받으면 로그인 성공/실패 여부에 따라 응답 헤더에 &lt;code class=&quot;language-text&quot;&gt;Set-Cookie&lt;/code&gt; 로 결과값을 저장할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; : 클라이언트는 응답 헤더에 &lt;code class=&quot;language-text&quot;&gt;Set-Cookie&lt;/code&gt; 가 존재할 경우 &lt;code class=&quot;language-text&quot;&gt;Set-Cookie&lt;/code&gt; 의 값을 읽어 서버에 보내는 요청 헤더의 Cookie 헤더 값으로 다시 전송한다. &lt;strong&gt;즉, 각 HTTP 요청간에 데이터를 공유할 방법이 없기 떄문에 헤더를 통해 공유할 데이터를 매번 다시 전송하는 방식으로 데이터를 공유한다.&lt;/strong&gt; 이때 공유할 데이터로 쿠키를 헤더에 넣을 수 있는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;쿠키cookie-를-적용한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BF%A0%ED%82%A4cookie-%EB%A5%BC-%EC%A0%81%EC%9A%A9%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;쿠키cookie 를 적용한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쿠키(Cookie) 를 적용한다.&lt;/h3&gt;
&lt;p&gt;구현 코드는 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/user/create&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... (기존 로직과 동일)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;DataBase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;response302Header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dos&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/index.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/user/login&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; body &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;br&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; contentLength&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpRequestUtils&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseQueryString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataBase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findUserById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;userId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;token function&quot;&gt;responseResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/user/login_failed.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;password&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;token function&quot;&gt;response302LoginSuccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dos&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;token function&quot;&gt;responseResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/user/login_failed.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;로그인 기능을 구현하기 위해선, 우선 회원가입 기능 및 데이터베이스에 유저 정보를 저장하는 기능이 원활히 구현되어야 할 것이다. 이를 위해 위의 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 처럼 DB 에 유저 데이터를 저장하는 플로우를 구현했다.&lt;/p&gt;
&lt;h3 id=&quot;response302loginsuccess&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#response302loginsuccess&quot; aria-label=&quot;response302loginsuccess permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;response302LoginSuccess&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;response302LoginSuccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DataOutputStream&lt;/span&gt; dos&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	dos&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;HTTP/1.1 302 Found \r\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        dos&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Set-Cookie: logined-true \r\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        dos&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\r\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;로그인이 성공하면 응답 헤더에 &lt;code class=&quot;language-text&quot;&gt;Set-Cookie&lt;/code&gt; 헤더의 값으로 &lt;code class=&quot;language-text&quot;&gt;logined=true&lt;/code&gt; 를 전달했다. 위와 같이 구현을 완료한 후 서버를 재시작하고 회원가입, 로그인 순으로 테스트를 진행한다.&lt;/p&gt;
&lt;h3 id=&quot;responseresource&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#responseresource&quot; aria-label=&quot;responseresource permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;responseResource&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;responseResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;OutputStream&lt;/span&gt; out&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;DataOutputStream&lt;/span&gt; dos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataOutputStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; body &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readAllBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./webapp&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;response200Header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dos&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;responseBody&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dos&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;로그인이 실패할 경우 호출되는 메소드다.&lt;/p&gt;
&lt;p&gt;이와 같이 모든 요청에 로그인 성공 유무에 대한 정보가 전달된다. &lt;code class=&quot;language-text&quot;&gt;response302LoginSuccess()&lt;/code&gt; 를 호출하여 서버는 응답 헤더에 &lt;code class=&quot;language-text&quot;&gt;Set-Cookie&lt;/code&gt; 에 대해 로그인 성공 여부를 전달한다. 이러한 &lt;code class=&quot;language-text&quot;&gt;Set-Cookie&lt;/code&gt; 필드가 포함된 HTTP 응답을 전달받은 클라이언트는, 이후 서버에게 요청을 보낼때마다 &lt;code class=&quot;language-text&quot;&gt;Set-Cookie&lt;/code&gt; 가 포함된 헤더를 매번 전송하게 된다. 그러면 서버는 클라이언트의 Cookie 요청 헤더를 확인하여 logined 값이 true 인지 여부를 판단하여 현재 로그인 상태 유무를 확인할 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이렇게 기본적인 HTTP 웹 서버의 구축을 마쳤다. 하지만 아직 기능 추가, 리팩토링, 테스트를 거쳐할 부분이 많이 남아있다. 다음에는 현재 구현된 HTTP 웹서버 코드를 리팩토링 해보겠다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Legacy Code 를 개선하기 위한 고민&lt;/li&gt;
&lt;li&gt;LoggerFactory, ServerSocket, BufferedReader&lt;/li&gt;
&lt;li&gt;Session&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL 8.0 레플리케이션의 내부 동작 원리와 아키텍처 구성 방식]]></title><description><![CDATA[💡 현재 포스트는 이전 Velog 블로그에서 작성한 글을 기반으로 새롭게 다시 작성한 글 입니다. 고가용성과 확장성을 위한 데이터베이스 레플리케이션(DB Replication) 에서 데이터베이스 레플리케이션의 필요성, 그리고 MySQL…]]></description><link>https://haon.site/database/replication-architecture/</link><guid isPermaLink="false">https://haon.site/database/replication-architecture/</guid><pubDate>Fri, 18 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 이전 &lt;a href=&quot;https://velog.io/@msung99/posts&quot;&gt;Velog 블로그&lt;/a&gt;에서 작성한 글을 기반으로 새롭게 다시 작성한 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://haon.blog/database/replication-theory/&quot;&gt;고가용성과 확장성을 위한 데이터베이스 레플리케이션(DB Replication)&lt;/a&gt; 에서 데이터베이스 레플리케이션의 필요성, 그리고 MySQL 기준 레플리케이션 동작 방식에 대해 간단히 다루어보았다. 이번에는 MySQL 레플리케이션의 동작 방식에 대해 더 깊게 학습해보고자 한다.&lt;/p&gt;
&lt;h2 id=&quot;mysql-레플리케이션-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mysql-%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;mysql 레플리케이션 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MySQL 레플리케이션 아키텍처&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/160a06021d6e015f5d051f6d66cce9f6/86a1e/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.44171779141104%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACGklEQVR42pWTTW/aQBCG898q9dYfwD8hkSqVVkFqLjQS6a25JFVVVWp7iioBScklSBQCJSnQxBhsY+NvFuMvAn67uxAoUi8dyfaOZ/eZ8bzjHawsIRoSS4Sh69A0DaIoIggC/MsIIej1epAkCZZlLc8nCX/usNuCOsTSEToa+gMJnU4HgiBgPB7D87w1eDKZ8EtVVTQaDXS7XYxGI/g0zpJsgIsEpqHjZ6MGWVF4gAGbzSYM00QYRfxdEIaQZBmlYpEn0yi4XC5jOBwipDFWJQfO53NEcYw3h4colUr8cOXqCplMBj4xMHX6GNxdIyYD6PItXr7ah6LIaN3cYDed5tVFNOkWMI4jvD44QKFQ4MAftTqev9jHw1RGOKpgZlGgUQGRyzg5OYWpq1D6HZy+/4CHWcTbsgU0LQJitJHY35edDwVE2jlkjaz7Y7oR3N8f4WrXEGpfMZc+YeGcQxXrcFxv00OqEe7uRdzWy0iGX6BoDhL9AsNuCWffinBtk++yLBdEa8LTW/CtLpIZgSd8RuypVJiYc9YV+r6/LCwMkEqlcHl5sfLDLZWZZbNZ5I/e0jNTPHn6DI1mC0txFxvg42aTqprL5VCtVrnv+8E62WT16Uf5PI6P38G2LeztptFu/6KMxQbIFo/zxsaBwZnPQJ43oQqGy7EJfEynHh9m27ah6yO+1unP4Dj23z38P2OJGIQlNwxjBXQ48A+r7i+nlpjoigAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/160a06021d6e015f5d051f6d66cce9f6/a6d36/image.png&quot;
        srcset=&quot;/static/160a06021d6e015f5d051f6d66cce9f6/222b7/image.png 163w,
/static/160a06021d6e015f5d051f6d66cce9f6/ff46a/image.png 325w,
/static/160a06021d6e015f5d051f6d66cce9f6/a6d36/image.png 650w,
/static/160a06021d6e015f5d051f6d66cce9f6/e548f/image.png 975w,
/static/160a06021d6e015f5d051f6d66cce9f6/86a1e/image.png 1296w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;지난 포스팅에서 다루었듯이, 데이터베이스 레플리케이션 과정에 크게 3가지 종류의 쓰레드가 참여한다. 이 과정을 다시 요약해보자.&lt;/p&gt;
&lt;p&gt;우선 소스 서버에서 이벤트가 발생했다면 해당 이벤트는 소스 서버내의 바이너리 로그 파일에 기록된다. 기록된 이벤트를 조회하기 위해 레플리카 서버의 &lt;strong&gt;레플리케이션 I/O 쓰레드&lt;/strong&gt;가 로그 정보를 요청하고, 요청을 받은 소스 서버의 &lt;strong&gt;바이너리 로그 덤프 쓰레드&lt;/strong&gt;는 이벤트를 레플리카 서버에 전송한다. 이후 &lt;strong&gt;레플리케이션 I/O 쓰레드&lt;/strong&gt;는 해당 이벤트는 디스크 내의 릴레이 로그 파일에 저장한다. 아직까지는 이벤트가 레플리카 서버에는 반영되지 않는 상태인데, 이를 레플리카 서버에서도 반영하기 위해 &lt;strong&gt;레플리케이션 SQL 쓰레드&lt;/strong&gt;가 변경내용을 데이터 파일에 저장한다.&lt;/p&gt;
&lt;h3 id=&quot;레플리케이션-동작-과정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EB%8F%99%EC%9E%91-%EA%B3%BC%EC%A0%95&quot; aria-label=&quot;레플리케이션 동작 과정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레플리케이션 동작 과정&lt;/h3&gt;
&lt;p&gt;이러한 레플리케이션 동작 과정을 더 상세히 풀어쓰자면 아래와 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; &lt;strong&gt;레플리케이션 시작&lt;/strong&gt; : 레플리카 서버에서 &lt;code class=&quot;language-text&quot;&gt;START REPLICA&lt;/code&gt; 명령을 실행하면, 레플리카 서버에서 레플리케이션 I/O 쓰레드를 생성한다. (이 쓰레드는 &lt;code class=&quot;language-text&quot;&gt;STOP REPLICA&lt;/code&gt; 명령어를 실행하면 종료된다.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; &lt;strong&gt;바이너리 로그 읽기 및 전송&lt;/strong&gt; : 소스 서버에서 발생한 이벤트는 **바이너리 로그(Binary Log)**라는 로그 파일에 기록된다. 앞서 레플리카 서버에 생성된 레플리케이션 I/O 쓰레드는 이벤트가 발생할 때 마다 소스 서버의 바이너리 로그 덤프 쓰레드에게 바이너리 로그(이벤트) 정보를 요청한다. 이떄 소스 서버와 레플리카 서버를 연결할 떄 사용하는 정보는 후술할 &lt;strong&gt;커넥션 메타데이터(Connection Meta Data)&lt;/strong&gt; 에 저장되어있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;요청을 받은 바이너리 로그 덤프 쓰레드는 바이너리 로그 파일에서 이벤트를 읽어오는데, 이때 바이너리 로그 파일을 일시적으로 잠근다. 그리고 이벤트를 모두 읽고 난 뒤 잠금을 해제한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; &lt;strong&gt;릴레이 로그 생성 및 바이너리 로그 기록하기&lt;/strong&gt; : 레플리케이션 I/O 쓰레드는 읽어온 바이너리 로그(이벤트) 정보를 레플리카 서버내에 릴레이 로그 파일에다 쓴다. 그리고 레플리케이션 SQL 쓰레드는 레플리케이션 I/O 쓰레드가 생성한 릴레이 로그에 기록된 이벤트를 읽고 실행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이때, &lt;strong&gt;레플리카 서버의 I/O 쓰레드와 SQL 쓰레드는 서로 독립적으로 동작&lt;/strong&gt;한다. 레플리케이션 SQL 쓰레드의 연산이 지연되어 데이터틀 느리게 반영한다고 해서, 레플리케이션 I/O 쓰레드가 소스 서버로부터 데이터를 읽어오는데는 영향을 주지 않는다.&lt;/p&gt;
&lt;p&gt;또한 &lt;strong&gt;레플리카 서버가 소스 서버의 변경 내역을 반영하는 것은 소스 서버와 독립적으로 동작&lt;/strong&gt;한다. 따라서 레플리카 서버에 문제가 발생하더라도 소스 서버는 영향을 받지 않는다. 다만, 소스 서버에 문제가 생기면 그 즉시 레플리카 서버에 애러가 발생하고 레플리케이션이 중단된다. 그런데 이때도 복제 과정만이 중단된 것이기 떄문에 레플리카 서버에서 쿼리는 정상적으로 발생한다.&lt;/p&gt;
&lt;p&gt;하지만 &lt;strong&gt;복제가 중단되는 그 시점부터 소스 서버와 서로 다른 쿼리가 실행되어 다른 데이터를 가지고 있을 수 있기 떄문에 데이터 정합성 문제가 발생&lt;/strong&gt;할 수 있다는 점을 유의해야한다. (요약하자면, 레플리카 서버는 고장나도 소스 서버에 영향을 끼치지 않지만, 반대로 소스 서버가 고장나면 레플리카 서버에게 영향을 끼쳐서 레플리케이션이 중단된다.)&lt;/p&gt;
&lt;h3 id=&quot;레플리카-서버의-레플리케이션-관련-메타-데이터&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%94%8C%EB%A6%AC%EC%B9%B4-%EC%84%9C%EB%B2%84%EC%9D%98-%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EA%B4%80%EB%A0%A8-%EB%A9%94%ED%83%80-%EB%8D%B0%EC%9D%B4%ED%84%B0&quot; aria-label=&quot;레플리카 서버의 레플리케이션 관련 메타 데이터 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레플리카 서버의 레플리케이션 관련 메타 데이터&lt;/h3&gt;
&lt;p&gt;복제 과정이 정상 동작하기 위해, 레플리카 서버는 &lt;strong&gt;릴레이 로그, 커넥션 메타데이터, 어플라이어 메타데이터&lt;/strong&gt; 이 3가지 데이터를 관리한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;릴레이 로그(Relay Log)&lt;/strong&gt; : 소스 서버의 바이너리 로그로부터 읽어온 이벤트(트랜잭션) 정보가 저장된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;커넥션 메타데이터 (Connection)&lt;/strong&gt; : 레플리카 서버의 I/O 쓰레드가 소스 서버에 연결할 때 사용하는 &lt;strong&gt;데이터베이스 계정 정보&lt;/strong&gt;(username, user_password, port 등) 와 &lt;strong&gt;현재 읽고 있는 소스 서버의 바이너리 파일명(File)과 파일 내 위치값(Position)&lt;/strong&gt; 등의 데이터가 저장된다. 이 정보는 MySQL 의 &lt;code class=&quot;language-text&quot;&gt;slave_master_info&lt;/code&gt; 테이블에 저장된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;어플라이어 메타데이터 (Applier)&lt;/strong&gt; : 레플리카 서버의 SQL 쓰레드는 어플라이어라는 역할을 수행한다. &lt;strong&gt;어플라이어는 릴레이 로그의 이벤트를 레플리카에 실제로 반영하는 역할&lt;/strong&gt;을 수행한다. 그리고 어플라이어 메타 데이터는 어플라이어가 최근에 적용한 이벤트에 대해, 해당 이벤트가 저장되어 있는 릴레이 로그 파일명과 파일 내 위치 등의 정보를 담고 있다. SQL 쓰레드는 이 정보들을 바탕으로 레플리카 서버에 나머지 이후 이벤트들을 적용한다. 이 데이터는 &lt;code class=&quot;language-text&quot;&gt;slave_relay_log_info&lt;/code&gt; 에 저장되어 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;레플리케이션-복제-타입&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EB%B3%B5%EC%A0%9C-%ED%83%80%EC%9E%85&quot; aria-label=&quot;레플리케이션 복제 타입 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레플리케이션 복제 타입&lt;/h2&gt;
&lt;p&gt;레플리카 서버는 바이너리 로그에 기록된 이벤트 중 어떤 이벤트를 실제로 반영해야할지 어떻게 판단할까? 바이너리 로그에 기록된 이벤트를 식별하는 방식에 따라 크게 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 바이너리 로그 파일 위치 기반 복제 방식 과 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 글로벌 트랜잭션 ID(GTID) 기반 복제 방식으로 나뉜다. 후술하겠지만, 요즘에는 GTID 기반 복제 방식을 택하고 있다.&lt;/p&gt;
&lt;h3 id=&quot;바이너리-로그-파일-위치-기반-복제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%94%EC%9D%B4%EB%84%88%EB%A6%AC-%EB%A1%9C%EA%B7%B8-%ED%8C%8C%EC%9D%BC-%EC%9C%84%EC%B9%98-%EA%B8%B0%EB%B0%98-%EB%B3%B5%EC%A0%9C&quot; aria-label=&quot;바이너리 로그 파일 위치 기반 복제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;바이너리 로그 파일 위치 기반 복제&lt;/h3&gt;
&lt;p&gt;바이너리 로그 파일 위치 기반 복제 방식은 레플리케이션 기법이 MySQL 에 처음 도입되었을 떄 초기 시점부터 제공된 방식이다. 이 방식은 &lt;strong&gt;바이너리 로그 파일명과 파일 내에서의 위치(offset 또는 position)를 통해 이벤트를 식별&lt;/strong&gt;한다. 위치 값이란 실제 파일의 바이트 수를 뜻한다. 참고로 복제를 하려면 바이너리 로그가 활성화되어 있어야 한다.&lt;/p&gt;
&lt;p&gt;레플리카는 자신이 어떤 바이너리 로그 파일의 어떤 위치(Position) 까지 복제했는지 정보를 관리한다. 그 덕분에 &lt;strong&gt;레플리카는 레플리케이션을 일시 중단하고 다시 재개할 수 있다.&lt;/strong&gt; 복제를 중단한 로그 파일의 특정 위치에서부터 복제를 재개하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f3f12e84fea41258d7173fec57325399/ddc6c/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17.177914110429448%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA3ElEQVR42h2Nu06DYACF2ZmYQCFhAZuiA5SrGFpJJXjpnxCdjJaXaXQ02keoCzqZkNiZtzF9ia+/PcnJWb5zjjIMA1dVhRCCsiyJoogwDEmSBMuy+N1u+dfHek2cnXNzt8CfhJKLD6zneTiOg2EYtG2L0vc9SRzj+z51XRPIHI1OsG0bTdP46rrD4NvLinTsIi4LsrMx1vERpmmi6zqGtKqqNE2D8vPd8XRb8SjdimvqWcFEPp/KZ9d1+dxs+NvteH9dsSgyHqqS+/mM6UVOmqbkec60kJ0g4Hm5ZA9bdXOcgg15lgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/f3f12e84fea41258d7173fec57325399/a6d36/image-1.png&quot;
        srcset=&quot;/static/f3f12e84fea41258d7173fec57325399/222b7/image-1.png 163w,
/static/f3f12e84fea41258d7173fec57325399/ff46a/image-1.png 325w,
/static/f3f12e84fea41258d7173fec57325399/a6d36/image-1.png 650w,
/static/f3f12e84fea41258d7173fec57325399/e548f/image-1.png 975w,
/static/f3f12e84fea41258d7173fec57325399/3c492/image-1.png 1300w,
/static/f3f12e84fea41258d7173fec57325399/ddc6c/image-1.png 1534w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;바이너리 로그 파일 위치 기반 복제에서 또 중요한 점은 &lt;code class=&quot;language-text&quot;&gt;server_id&lt;/code&gt; 값이다. 바이너리 로그에는 각 이벤트별로 이벤트가 발생한 서버를 식별하기 위해 &lt;code class=&quot;language-text&quot;&gt;server_id&lt;/code&gt; 값을 저장한다. 디폴트 값은 1인데, 만약 여러 서버가 중복된 &lt;code class=&quot;language-text&quot;&gt;server_id&lt;/code&gt; 값을 가지게 되면 문제가 발생한다. 즉, 레플리케이션 과정에 참여하는 모든 서버는 중복되지 않는 유일한 &lt;code class=&quot;language-text&quot;&gt;server_id&lt;/code&gt; 값을 가져야하는데, 왜 그럴까?&lt;/p&gt;
&lt;p&gt;레플리카 서버의 I/O 쓰레드는 소스 서버의 바이너리 로그 덤프 쓰레드로부터 바이너리 로그를 읽어오는 방식으로 동작한다. 만약 소스 서버와 레플리카 서버가 별도의 설정이 없다면 모두 &lt;code class=&quot;language-text&quot;&gt;server_id&lt;/code&gt; 값이 1일 것이다. 그런데, 이벤트를 읽어보니 &lt;code class=&quot;language-text&quot;&gt;server_id&lt;/code&gt; 값이 1이다. 이렇게 된다면 레플리카는 현재 읽어들인 이벤트를 자기 자신으로부터 발생한 이벤트라고 간주하고 반영하지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;바이너리 로그에는 항상 소스 서버의 모든 이벤트가 반드시 기록되는 것이 보장되지 않는다.&lt;/strong&gt; 바이너리 로그가 비활성화 된 기간이 있을 수도 있고, 용량 확보를 위해 일정 기간이 지난 바이너리 로그를 제거했을 수도 있다. 따라서 레플리카 서버를 구성하기 전에 &lt;strong&gt;mysqldump 와 같은 툴을 이용해서 소스 서버의 데이터를 우선 레플리카 서버에 적재한뒤에 레플리케이션을 구성해야한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;gtid-글로벌-트랜잭션-id-기반-복제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#gtid-%EA%B8%80%EB%A1%9C%EB%B2%8C-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-id-%EA%B8%B0%EB%B0%98-%EB%B3%B5%EC%A0%9C&quot; aria-label=&quot;gtid 글로벌 트랜잭션 id 기반 복제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GTID (글로벌 트랜잭션 ID) 기반 복제&lt;/h3&gt;
&lt;p&gt;MySQL 5.5 버전까지는 바이너리 로그 파일 위치 기반 복제만 가능했다. 하지만 이후 현재까지 MySQL 버전에선 GTID 방식을 지원하고 있다. 바이너리 로그 파일 위치 기반 복제 방식은 읽어들일 이벤트 식별 과정이 소스 서버에서만 유효하다는 단점이 존재한다.&lt;/p&gt;
&lt;p&gt;이 떄문에 만약 소스 서버에 문제가 생겨서 다른 레플리카 서버게 소스 서버로 승격된 경우, 복제 과정에 참여하는 다른 서버들 모두가 복제를 시작할 위치를 다시 찾아야해서 &lt;strong&gt;FailOver(장애 복구)에 시간이 오래 걸린다. (복제 토폴로지 변경이 어려워진다.)&lt;/strong&gt; 즉, 소스 서버에서 발생한 이벤트에 대해 동일한 이벤트가 레플리카 서버에서 동일한 파일명과 위치값으로 항상 저장된다는 보장이 없다. 같은 이벤트임에도 불구하고 다른 바이너리 로그 파일명과 위치값을 가질 수 있다. 절대적으로 특정 이벤트를 식별할 수 있는 식별자가 없다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e94cf82ec57ef0b8926f34f2a1fe43db/912fc/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.963190184049076%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABx0lEQVR42o2TW2sTURRG8weLf0LwoS/ik+CLbyKCUEWkOi1JSawxU7UmXlKmF2mshQba1OZFTZpLY9OQyVxym0xnspwzmsGmDPWDzZ45Gxb7fHufCFdoPB77eTgcIr9e5dlCAmkhTqVSvVCfKDI5dBzHD9d1L8HGroP4Ku7J/Mheo7h+k3a7HQ4M60rItm06nQ62ZZNKv+Rx7A5Plu7xs1Ty6/82EADL5TKx+ArS4is+b+UCkGEY6LrOaDTyz9Jruzxa/oqU2qFWOwnvsNVqsbY8ixKbIb+dxOxZHkjzoYGHlkXx8Iit7CYbWYVjr4lpWACs1mosRm/xZmWWpDyPZZ1f9tHL01aEAusnDR4mNph7e4D8IUfX0Lxr2heAAlAofKNw9J29/AGqqoZfWUxsW/lCRv7Efn6fwaCPaZq+f2LyQueOSyoZ5emD68zP3fY8rIcDNa2DbuiomspZ68wDDvyiGIaYcNc0/P+dTYnDjzPsvr9BtVr5C3SvXptp77q9HoNel0RG4W48zf0XGUrHf4Bu2GKHabJnljVkXckhPX/HUnTVeymVoD4JwYn879MTnqpqm1+NOqenDX8owg6RRTSbTfr9Pr8BGLmFWQuGN0MAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e94cf82ec57ef0b8926f34f2a1fe43db/a6d36/image-3.png&quot;
        srcset=&quot;/static/e94cf82ec57ef0b8926f34f2a1fe43db/222b7/image-3.png 163w,
/static/e94cf82ec57ef0b8926f34f2a1fe43db/ff46a/image-3.png 325w,
/static/e94cf82ec57ef0b8926f34f2a1fe43db/a6d36/image-3.png 650w,
/static/e94cf82ec57ef0b8926f34f2a1fe43db/e548f/image-3.png 975w,
/static/e94cf82ec57ef0b8926f34f2a1fe43db/3c492/image-3.png 1300w,
/static/e94cf82ec57ef0b8926f34f2a1fe43db/912fc/image-3.png 1934w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;만약 위와 같이 Source 서버 1대, Replica 서버 2대를 배치한 상황을 가정해보자. 현재까지 소스 서버의 바이너리 로그 위치는 &lt;code class=&quot;language-text&quot;&gt;binary-log.000002:510&lt;/code&gt; 이고, 레플리카 서버 A는 실시간으로 이벤트를 잘 동기화하였다. 하지만 레플리카 서버 B가 네트워크 지연 문제로 인해 &lt;code class=&quot;language-text&quot;&gt;binary-log.000002:430&lt;/code&gt; 까지만 동기화된 상태이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/522d7aee41e1d9cd1627544c9696ef59/a4f81/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.920245398773%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABtUlEQVR42p2S3U/TUBjG929xqX+Od0aNF8YPREHEqIl3xoSQ4ViQbLODEXX4NRxhsNJtmGVCvGB0ZJ/S0m5l7fqzp64YkmmiT/P09JzznqfP+7QhPFQqFRRlh2KhgLIjYxiGWMZ1XUZB1Ofz2xSLRWQ5T7V6cFYfErd4IsnUg0dMTU7y5PEzWq3WHwXFmiRJPJx5yvT0DON375NZ3/D3BoMBIfGwK6fJvbrIt9QYmfgljjVzpGAw105OySavUVkZQ45dQNlMDveHgu/SCaLzV3i/Ok44fAu1VvMLut0ulmWdMYhCVY+IRO6RTt0htnid5dTCeYfxt5+5/Pw1txc/cPNFgppao9ft+a13Oh2f7XbbHwWO1EMm5pa5EV3j6uwKL6X0ecGC8pXw3BsWIh+JLa2h6/pfWzaME1alDLHoOkuRT8hbpd8fBd3CPTim/71Jf6+OXfVcHOq4P3q44nJ/UbxdQDgtl8vs7++RzX4hl9uktFvyIxEI4XgHLAdOvQN9QTG3wR6M/GVs20bTNM+lORwNvyPHcYaC/wjhttFo+PkKt81mk3q9jmma/ycYiIoIAgaxCMGf5Rzit8GZUEsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/522d7aee41e1d9cd1627544c9696ef59/a6d36/image-5.png&quot;
        srcset=&quot;/static/522d7aee41e1d9cd1627544c9696ef59/222b7/image-5.png 163w,
/static/522d7aee41e1d9cd1627544c9696ef59/ff46a/image-5.png 325w,
/static/522d7aee41e1d9cd1627544c9696ef59/a6d36/image-5.png 650w,
/static/522d7aee41e1d9cd1627544c9696ef59/e548f/image-5.png 975w,
/static/522d7aee41e1d9cd1627544c9696ef59/3c492/image-5.png 1300w,
/static/522d7aee41e1d9cd1627544c9696ef59/a4f81/image-5.png 1508w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이 상황에서 하필 소스 서버에 장애가 발생했다고 해보자. 신속하게 FailOver 를 위해 레플리카 서버 A 를 소스 서버로 승격시킨다. 그런데 바이너리 로그 파일 위치 기반 복제 방식에서는 같은 이벤트라고 한들, 서로 식별 값이 다르다고 했다. 레플리카 서버 B는 &lt;code class=&quot;language-text&quot;&gt;binary-log.0000002:430&lt;/code&gt; 까지만 동기화 되어있다는 정보를 가지고 있지만, 이는 새로운 소스 서버에는 유효하지 않다. 따라서 이 레플리카는 어디서 부터 바이너리 로그를 읽어와야 할지 알 수 없어서, 즉각 동기화가 불가능한 상태가 된다. 이러한 점에서 바이너리 로그 위치 기반 복제 방식은 FailOver 기능이 취약하다. 이로보아, &lt;strong&gt;우리는 어떤 서버에 있던 상관없이 동일한 이벤트를 동일한 식별자로 식별하는 방법이 필요함을 느낄 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이 문제를 해결하기 위해 GTID(Global Transaction Identifier) 기반 복제 방식이 등장했다. &lt;strong&gt;레플리케이션에 참여하는 모든 서버에서 동일한 고유 식별 ID 값을 가지는 방식이다.&lt;/strong&gt; 모든 서버의 ID 값은 모두 동일하기 떄문에, 동일한 이벤트에 대해서 동일한 글로벌 트랜잭션 ID 값만 읽어오면 된다. 즉, 각 서버간에 바이너리 로그 파일명 및 위치 값은 각기 다를지라도 GTID 값이 동일하여 같은 이벤트를 식별 가능한 방식이다. GTID 기반 복제 방식은 GTID 값을 통해 이벤트를 식별할 수 있다. 따라서 새로운 소스 서버로부터 레플리카 서버가 즉시 동기화를 시작할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;복제-토폴로지-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B3%B5%EC%A0%9C-%ED%86%A0%ED%8F%B4%EB%A1%9C%EC%A7%80-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;복제 토폴로지 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;복제 토폴로지 방식&lt;/h2&gt;
&lt;p&gt;소스 서버와 레플리카 서버를 어떻게 구성할 수 있을지 아키텍처 관점에서도 생각해보자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;싱글 레플리카(Single Replica)&lt;/strong&gt; : 단순하게 소스 서버 1대와 레플리카 서버 1대를 배치하는 싱글 레플리카 구성 방식이 있다. 레플리카 서버는 예비 서버 및 데이터 백업용으로 활용한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;멀티 레플리카(Multi Replica)&lt;/strong&gt; : 레플리카 서버를 여러대 배치한 방식인 멀티 레플리카 방식도 존재한다. 즉, 소스 서버 1대와 레플리카 여러대로 배치한다. 만약 레플리카를 2대로 구성한다면 구성하기 나름이지만, 한 서버는 부하 분산을 위한 용도로 사용하고, 다른 서버는 백업용을 위한 서버로 사용할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;체인 복제(Chain Replication)&lt;/strong&gt; : 소스서버에 연결된 레플리카 서버가 많은 경우, 소스 서버에 복제로 인한 부하가 커질 수 있다. 이런 경우 다른 레플리카 서버를 소스 서버로 활용하여 부하를 분산시키도록 구성할 수도 있다. 이 방식은 MySQL 서버를 버전업하거나 중간 장비들을 교채하는 시기에도 유용하게 사용될 수 있을 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;듀얼 소스 복제(Dual Source Replication)&lt;/strong&gt; : 레플리케이션에 참여하는 두 서버 모두 읽기, 쓰기 연산이 가능한 형태이다. 두 서버 모두 동일한 형태의 데이터를 가지고있고, 트랜잭션 충돌이 일어날 경우 롤백된다. 롤백될 경우 레플리케이션이 중단되기 떄문에 잘 사용되지 않는 구성 방식이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;멀티 소스 복제(Multi Source Replication)&lt;/strong&gt; : 레플리카 서버 1대에 소스 서버 여러대가 연결된 형태이다. 소스 서버에 흩어져있는 데이터들은 한 곳에 보아서 데이터를 분석할 때 사용할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;모행-팀은-어떠한-복제-아키텍처를-구성하였는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A8%ED%96%89-%ED%8C%80%EC%9D%80-%EC%96%B4%EB%96%A0%ED%95%9C-%EB%B3%B5%EC%A0%9C-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EB%A5%BC-%EA%B5%AC%EC%84%B1%ED%95%98%EC%98%80%EB%8A%94%EA%B0%80&quot; aria-label=&quot;모행 팀은 어떠한 복제 아키텍처를 구성하였는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;모행 팀은 어떠한 복제 아키텍처를 구성하였는가?&lt;/h3&gt;
&lt;p&gt;우리 모행 팀은 레플리케이션 아키텍처 방식 중 &lt;strong&gt;멀티 레플리카(Multi Replica)&lt;/strong&gt; 방식을 택했다. 우리가 레플리케이션을 도입한 가장 큰 이유는 부하 분산을 통한 쿼리 성능 개선이다. 데이터베이스를 단일 서버로 배치하면 병목 현상이 발생하여 시스템 전체 성능 저하가 발생할 수 있다는 문제점이 존재하기에, 데이터베이스를 다중화하여 부하 분산을 하는 것이다. 다중화를 통해 사용자 경험 개선을 기대해볼 수 있다.&lt;/p&gt;
&lt;p&gt;이 떄문에 멀티 레플리카 방식이 가장 적합하다고 판단했다. 레플리카 서버를 2대를 배치하여, 읽기 쿼리에 대한 성능 개선을 기대하는 방식을 생각했다. 우리 서비스는 몰론, 대부분의 웹 서비스는 쓰기 연산에 비해 조회 연산이 매우 높게 발생한다. 따라서 조회 연산을 담당하는 레플리카 서버를 2대로 다중화하여 부하를 분산하여 성능을 개선하는 방식을 택하였다.&lt;/p&gt;
&lt;p&gt;싱글 레플리카 방식으로 구성하는 것을 생각해보았지만, 싱글 레플리카는 우리 목적과 다소 다르다고 생각했다. 싱글 레플리카는 레플리카 서버를 FailOver 와 백업용으로 사용하는 구상 방식이다. 하지만 우리는 부하 분산을 통해 (특히) 조회 쿼리 연산의 성능을 개선하는 것이 주 목적이었기 떄문에, 멀티 레플리카 방식이 더 적합하다고 판단했다.&lt;/p&gt;
&lt;p&gt;게다가 레플리카 서버가 여러대라면, 레플리카 서버가 1대 있는 방식에 비해 FailOver 에 유연하게 대응할 수 있을 것이다. 싱글 레플리카 방식일 때, 읽기 쿼리를 부하 분산을 위해 레플리카 서버에 요청한다면, 레플리카 서버가 문제가 발생했을 때에 대한 대비책이 없다. 반면 여러대의 Replica 서버(최소 2개 이상)를 구성하게 되면 한 대의 Replica 서버가 장애가 나면 다른 Replica 서버를 사용하면 된다. 따라서 싱글 레플리카 복제 구성일 때는 대비가 없다는 것이다. (몰론, 이러한 논리로 따지자면 소스 서버 또한 다중화해야할 수 있다고 생각한다. 하지만 아직 소스 서버 1대만으로도 충분히 부하를 견딜 수 있기에 장애가 발생할 가능성이 낮을 것으로 판단했다. 쓰기 연산 특성상 읽기 연산에 비해 요청 비율이 매우 낮기 떄문에, 아직까지는 소스 서버가 1대로 충분히 견딜 수 있는 상황이다.)&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;아직 레플리케이션을 완전히 내것으로 체화한 느낌이 들지는 않는다. 깊게 공부하고 이해하려고 해도 너무 어려운 것 같다 🥲 이번 포스팅에서는 지나치게 욕심내어 완벽하게 모든 내용을 이해하려는데 힘쓰지 않았다. 다음 포스트에서 차근차근 데이터베이스 레플리케이션에 대해 점진적으로 자세히 다루어볼 생각이다.&lt;/p&gt;
&lt;h2 id=&quot;더-학습해야-할-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EC%95%BC-%ED%95%A0-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해야 할 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해야 할 키워드&lt;/h2&gt;
&lt;ul&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;JPA 영속성 컨텍스트 Life Cycle&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Real MySQL 8.0 - 백은빈, 이성욱&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=NPVJQz_YF2A&amp;#x26;t=211s&quot;&gt;https://www.youtube.com/watch?v=NPVJQz_YF2A&amp;#x26;t=211s&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/mysql-8.0-replication-architecture-and-replication-type/&quot;&gt;https://hudi.blog/mysql-8.0-replication-architecture-and-replication-type/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=95bnLnIxyWI&quot;&gt;https://www.youtube.com/watch?v=95bnLnIxyWI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL 에서 B+ Tree 기반 인덱스로 데이터를 스캔하는 방식 (인덱스 레인지 스캔)]]></title><description><![CDATA[인덱스 레인지 스캔 인덱스 레인지 스캔이란 검색해야 할 인덱스의 범위가 결정됐을 떄 사용허는 방식이다. 과정을 먼저 정리하자면 아래와 같이 수행된다.  인덱스 탐색 (Index Seek…]]></description><link>https://haon.site/database/index-scan-type/</link><guid isPermaLink="false">https://haon.site/database/index-scan-type/</guid><pubDate>Tue, 15 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;인덱스-레인지-스캔&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EB%A0%88%EC%9D%B8%EC%A7%80-%EC%8A%A4%EC%BA%94&quot; aria-label=&quot;인덱스 레인지 스캔 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스 레인지 스캔&lt;/h2&gt;
&lt;p&gt;인덱스 레인지 스캔이란 검색해야 할 인덱스의 범위가 결정됐을 떄 사용허는 방식이다. 과정을 먼저 정리하자면 아래와 같이 수행된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 인덱스 탐색 (Index Seek) : 조건을 만족하는 값이 저장된 위치를 찾는다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 인덱스 스캔 (Index Scan) : &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에서 찾은 위치부터 필요한 만큼 인덱스를 쭉 읽는다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 레코드 읽어오기 : &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 에서 읽어온 인덱스가 키와 레코드 주소를 이용해 레코드가 저장된 페이지를 가져오고, 최종 레코드를 읽어온다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;리프 노드를 제외한 모든 계층의 노드는 인덱스 Key 값과 자식 노드의 주소값을 저장하고 있다. 반면 리프 노드는 인덱스 Key 값과 실제 레코드 데이터에 대한 주소값을 저장하고 있다(가리키고 있다). 인덱스 레인지 스캔은 루프 노드부터 시작하여 리프 노드로 쭉 내려가서 범위 탐색의 시작점을 찾는다. 이후 정렬된 인덱스 데이터를 범위 탐색의 끝점까지 순차대로 따라가며 원하는 데이터들만 쭉 읽는다(스캔한다).&lt;/p&gt;
&lt;p&gt;인덱스 B+ Tree 구조는 항상 정렬된 상태를 유지하기 떄문에 리프 노드의 탐색 시작점을 빠르게 찾을 수 있다. 또한 리프 노드끼리 서로 링크드 리스트로 연결되어 있기 떄문에, 인덱스 범위 탐색이 가능하다. (이 또한 B+ Tree 구조에서 리프 노드끼리도 서로 정렬된 상태를 구성하기 떄문에, 빠르게 범위 탐색이 가능하다.)&lt;/p&gt;
&lt;h3 id=&quot;인덱스를-통해-랜덤-io-를-최소화하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%ED%86%B5%ED%95%B4-%EB%9E%9C%EB%8D%A4-io-%EB%A5%BC-%EC%B5%9C%EC%86%8C%ED%99%94%ED%95%98%EA%B8%B0&quot; aria-label=&quot;인덱스를 통해 랜덤 io 를 최소화하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스를 통해 랜덤 I/O 를 최소화하기&lt;/h3&gt;
&lt;p&gt;리프 노드에서 쭉 링크드리스트를 따라가며 검색 조건에 일치하는 인덱스 데이터를 찾으면, 그에 매핑되는 실제 레코드를 데이터 파일에서 읽어오는 과정이 필요하다. 이떄, 레코드를 읽어오는 과정에서 디스크 랜덤 I/O가 발생한다. 따라서 인덱스를 통해 데이터 레코드를 읽어오는 과정은 비용이 많이 발생하는 작업이다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://haon.blog/database/db-index/storage-and-random-sequantial-io/&quot;&gt;쿼리 튜닝을 위한 HDD 와 SSD 의 순차 I/O 와 랜덤 I/O&lt;/a&gt; 에서 설명했듯이, 랜덤 I/O 의 발생횟수를 줄여야만 쿼리 속도가 높아진다. 또한 랜덤 I/O 에 비해 순차 I/O 가 연산 속도가 더 빠르다고 설명했었다. 인덱스 레인지 스캔 방식은 실제 레코드를 읽어올 때 랜덤 I/O 가 발생하는 반면, 테이블 풀 스캔 방식은 순차 I/O 가 발생한다. 따라서 읽어야 할 실제 데이터 레코드 수가 20~25%를 넘는다면, 테이블의 레코드를 직접 읽는 테이블 풀 스캔 방식이 더 효율적이다.&lt;/p&gt;
&lt;h3 id=&quot;커버링-인덱스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EB%B2%84%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4&quot; aria-label=&quot;커버링 인덱스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커버링 인덱스&lt;/h3&gt;
&lt;p&gt;쿼리내에 수행되는 모든 컬럼을 인덱스가 모두 가지고 있는 인덱스를 커버링 인덱스라고 한다. 이 경우 인덱스가 쿼리에서 필요로 하는 모든 데이터를 이미 가지고 있기 떄문에, 실제 레코드에 접근하여 데이터를 읽어오는 랜덤 I/O 가 수행되지 않는다. 따라서 성능이 매우 빨라진다.&lt;/p&gt;
&lt;h3 id=&quot;인덱스를-적용-후-실행계획으로-확인하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%EC%A0%81%EC%9A%A9-%ED%9B%84-%EC%8B%A4%ED%96%89%EA%B3%84%ED%9A%8D%EC%9C%BC%EB%A1%9C-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0&quot; aria-label=&quot;인덱스를 적용 후 실행계획으로 확인하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스를 적용 후 실행계획으로 확인하기&lt;/h3&gt;
&lt;p&gt;MySQL 의 실행계획(Execution Plan) 을 통해 실제로 쿼리가 실행될 떄 인덱스 레인지 스캔이 발생하는지 확인해보자. 실행계획은 MySQL 의 옵티마이저가 쿼리를 최적화하여 수행한 작업 절차를 뜻한다. 우리는 &lt;code class=&quot;language-text&quot;&gt;EXPLAIN&lt;/code&gt; 을 사용하여 특정 쿼리에 대한 옵티마이저의 실행 계획을 확인할 수 있다. 아래와 같이 trip 테이블에 대해 인덱스를 추가해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; trip
&lt;span class=&quot;token keyword&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;index&lt;/span&gt; trip_index &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;WHERE&lt;/code&gt; 조건절에 &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt; 필드에 대한 인덱스 스캔이 발생하도록 쿼리문을 작성해보자. 그리고 &lt;code class=&quot;language-text&quot;&gt;EXPLAIN&lt;/code&gt; 을 통해 이 쿼리의 실행계획을 조회해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;EXPLAIN&lt;/span&gt; 
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; trip
&lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;between&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;a&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;b&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러면 신기하게도 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt; 이 &lt;code class=&quot;language-text&quot;&gt;range&lt;/code&gt; 로 조회되는 것을 확인할 수 있다. &lt;code class=&quot;language-text&quot;&gt;range&lt;/code&gt; 라는 것은 인덱스 레인지 스캔이 발생함을 뜻한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4fdfa3435dfa7065c243b6db48dfc059/24def/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 8.588957055214724%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAYAAABYBvyLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAc0lEQVR42iWLWRaDIBRDXYkD2DJUGQQErN3/slJ4fuSc5CYZrAs4Qka9foipIMSC3Xic+Yt0Vhh7NJaR603ZugjnI+2dT+j/7kt9/sM4MbzlB3qzmJeVNM0corGFvdB7voom2fjTMS6g9E6+b5Q2kGqj7x+mUTzQoiA56QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/4fdfa3435dfa7065c243b6db48dfc059/a6d36/image.png&quot;
        srcset=&quot;/static/4fdfa3435dfa7065c243b6db48dfc059/222b7/image.png 163w,
/static/4fdfa3435dfa7065c243b6db48dfc059/ff46a/image.png 325w,
/static/4fdfa3435dfa7065c243b6db48dfc059/a6d36/image.png 650w,
/static/4fdfa3435dfa7065c243b6db48dfc059/e548f/image.png 975w,
/static/4fdfa3435dfa7065c243b6db48dfc059/3c492/image.png 1300w,
/static/4fdfa3435dfa7065c243b6db48dfc059/24def/image.png 1812w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;인덱스-풀-스캔&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4-%ED%92%80-%EC%8A%A4%EC%BA%94&quot; aria-label=&quot;인덱스 풀 스캔 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스 풀 스캔&lt;/h2&gt;
&lt;p&gt;인덱스 풀 스캔은 인덱스 레인지 스캔처럼 범위 탐색을 수행하지만, 이 방식에선 &lt;strong&gt;전체 인덱스를 모두 탐색&lt;/strong&gt;한다. &lt;strong&gt;쿼리 조건절이 인덱스의 첫 컬럼이 아닌 경우에 인덱스 풀 스캔 방식이 적용&lt;/strong&gt;된다. 예를들어 인덱스를 &lt;code class=&quot;language-text&quot;&gt;(name, place_name, coorindate_x)&lt;/code&gt; 으로 설정해줬는데, 조건절에서 &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt; 이 아니라 &lt;code class=&quot;language-text&quot;&gt;place_name&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;coorindate_x&lt;/code&gt; 가 사용되는 경우이다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;커버링 인덱스인 경우에만 이 방식이 사용&lt;/strong&gt;된다. 즉, 쿼리가 인덱스에 명시된 컬럼만으로 조건을 처리할 수 있는 경우에만 사용되는 스캔 방식이다. 만약 반대로 인덱스에 있는 컬럼만으로 처리할 수 없는 경우에 인덱스 풀 스캔이 사용된다면 랜덤 I/O 가 발생한다. 이는 비효율적이기 떄문에 절대 사용되지 않는다.&lt;/p&gt;
&lt;p&gt;인덱스의 크기는 테이블 크기보다 작기 떄문에 테이블 풀 스캔 보다는 효율적이다. 인덱스 풀 스캔이 테이블 풀 스캔보다는 적은 디스크 I/O 가 사용되기 때문이다.&lt;/p&gt;
&lt;h3 id=&quot;실행계획으로-확인하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%ED%96%89%EA%B3%84%ED%9A%8D%EC%9C%BC%EB%A1%9C-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0&quot; aria-label=&quot;실행계획으로 확인하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실행계획으로 확인하기&lt;/h3&gt;
&lt;p&gt;name, place_name, coordinate_x 이 3가지 컬럼에 대해 인덱스를 걸어준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; trip
&lt;span class=&quot;token keyword&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; trip_idx2 &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; place_name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; coordinate_x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 아래처럼 인덱스의 첫번째 컬럼인 name 가 아닌, 그 외의 컬럼 place_name 컬럼을 조건문에 넣어준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;EXPLAIN&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; place_name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; coordinate_x
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; trip
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; place_name &lt;span class=&quot;token operator&quot;&gt;between&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;a&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;b&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;실행계획으로 확인해보면 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt; 이 &lt;code class=&quot;language-text&quot;&gt;index&lt;/code&gt; 로 조회된다. &lt;code class=&quot;language-text&quot;&gt;index&lt;/code&gt; 는 인덱스 풀 스캔을 뜻한다. 즉, 인덱스의 모든 범위를 탐색한 것이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4dc072395cfc2d89ce170aded6c97189/f98ee/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 12.883435582822086%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAl0lEQVR42h2N2RaDIBBD/ZC6INpdoICCS48v/f9fSsk85JDMzQyV+0Tk7YswZ/iYEJdVxHycPzi/wLgAW3rUZD2MDTKjp5yfkfIhO9XjZbCkrQx28LgphffkkNcD1kU8C4+lGMpn5Mw+JNmh52zbz9JZxVdN20MPN3RqwKXuRG2nofpRPDlZ3SjJfHt9hVKjeDI93qVH/geKr1tgJmtb6QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/4dc072395cfc2d89ce170aded6c97189/a6d36/image-1.png&quot;
        srcset=&quot;/static/4dc072395cfc2d89ce170aded6c97189/222b7/image-1.png 163w,
/static/4dc072395cfc2d89ce170aded6c97189/ff46a/image-1.png 325w,
/static/4dc072395cfc2d89ce170aded6c97189/a6d36/image-1.png 650w,
/static/4dc072395cfc2d89ce170aded6c97189/e548f/image-1.png 975w,
/static/4dc072395cfc2d89ce170aded6c97189/3c492/image-1.png 1300w,
/static/4dc072395cfc2d89ce170aded6c97189/f98ee/image-1.png 1808w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;한편 커버링 인덱스가 아닌 경우로 쿼리를 실행시켜보자. 즉, 쿼리에서 인덱스를 구성하고 있지 않는 컬럼까지 포함시켜서 실행시켜보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;EXPLAIN&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; trip
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; place_name &lt;span class=&quot;token operator&quot;&gt;BETWEEN&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;a&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;b&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그 결과는 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt; 이 &lt;code class=&quot;language-text&quot;&gt;ALL&lt;/code&gt; 로 조회되고 있다. &lt;code class=&quot;language-text&quot;&gt;ALL&lt;/code&gt; 은 테이블 풀 스캔을 뜻한다. 즉, 커버링 인덱스가 아닌 상황에서는 인덱스 풀 스캔보다 테이블 풀 스캔을 하는 것이 더 효율적이라고 옵티마이저가 판단한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/aa32522e1ad83fa572a63463306ecd42/e515d/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 22.085889570552148%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAoklEQVR42p2NWQ7CMAxEc5DSpItAdMvaJnRBQtz/TENsQQ/Ax5PGM2NbbMcLhDYBYXkgrQdciKznuMLPCUvcsD/fsD5C2wDjZtiMNh6Tdjyb7JMW927kA/0wsbHn484vTD9o9mLaeZm6PiR+Qvkwmozl59o4dP0EUTdXFIXEpaygqhZNe2NdXNTpETzLCjLrUtZnLlVz5uQL9S0QVCB+8z98ANlpdqlOwhd+AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/aa32522e1ad83fa572a63463306ecd42/a6d36/image-2.png&quot;
        srcset=&quot;/static/aa32522e1ad83fa572a63463306ecd42/222b7/image-2.png 163w,
/static/aa32522e1ad83fa572a63463306ecd42/ff46a/image-2.png 325w,
/static/aa32522e1ad83fa572a63463306ecd42/a6d36/image-2.png 650w,
/static/aa32522e1ad83fa572a63463306ecd42/e548f/image-2.png 975w,
/static/aa32522e1ad83fa572a63463306ecd42/3c492/image-2.png 1300w,
/static/aa32522e1ad83fa572a63463306ecd42/e515d/image-2.png 1430w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;루스-인덱스-스캔&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A3%A8%EC%8A%A4-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EC%8A%A4%EC%BA%94&quot; aria-label=&quot;루스 인덱스 스캔 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;루스 인덱스 스캔&lt;/h2&gt;
&lt;p&gt;인덱스 레인지 스캔과는 비슷하게 동작하지만, 중간중간에 필요없는 인덱스 Key 값을 건너뛰고(Skip) 다음으로 넘어가는 형태로 처리한다. 일반적으로 &lt;code class=&quot;language-text&quot;&gt;GROUP BY&lt;/code&gt; 또는 집합 함수 가운대 &lt;code class=&quot;language-text&quot;&gt;MAX()&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;MIN()&lt;/code&gt; 함수에 대해 최적화하는 경우에 사용한다. 실행계획을 통해 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; trip
&lt;span class=&quot;token keyword&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; trip_idx &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; coordinate_x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 인덱스를 생성했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;EXPLAIN&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MIN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;coordinate_x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; trip
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;BETWEEN&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;a&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;e&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 쿼리를 살펴보자. &lt;code class=&quot;language-text&quot;&gt;trip&lt;/code&gt; 테이블은 &lt;code class=&quot;language-text&quot;&gt;(name, coordindate_x)&lt;/code&gt; 두 컬럼 조합으로 인덱스가 정렬된 상태이다. &lt;code class=&quot;language-text&quot;&gt;GROUP BY&lt;/code&gt; 를 통해 &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt; 컬럼을 기준으로 그룹화하고, &lt;code class=&quot;language-text&quot;&gt;MIN&lt;/code&gt; 집계 함수를 사용하여 그룹별로 가장 작은 &lt;code class=&quot;language-text&quot;&gt;coordindate_x&lt;/code&gt; 를 가져온다. 인덱스는 항상 정렬된 상태를 유지하므로, &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt; 그룹별로 첫 번째 레코드의 &lt;code class=&quot;language-text&quot;&gt;coordinate_x&lt;/code&gt; 값만 읽으면 된다. 옵티마이저는 인덱스에서 &lt;code class=&quot;language-text&quot;&gt;WHERE&lt;/code&gt; 조건을 만족하는 범위 전체를 모두 스캔할 필요가 없다는 것을 알고있기 때문에, 조건에 만족하지 않는 레코드는 무시하고 다음 레코드로 이동한다.&lt;/p&gt;
&lt;p&gt;인덱스 레인지 스캔 중 그룹에서 가장 작은 &lt;code class=&quot;language-text&quot;&gt;coordinate_x&lt;/code&gt; 를 찾으면, 해당 그룹에서는 더 이상 인덱스 스캔을 할 필요가 없어진다. 이때 필요없는 인덱스를 건너뛰게되고, 이를 루스 인덱스 스캔이라고 한다.&lt;/p&gt;
&lt;h2 id=&quot;인덱스-스킵-스캔&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EC%8A%A4%ED%82%B5-%EC%8A%A4%EC%BA%94&quot; aria-label=&quot;인덱스 스킵 스캔 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스 스킵 스캔&lt;/h2&gt;
&lt;p&gt;인덱스는 컬럼 순서를 기준으로 정렬된다. 따라서 인덱스를 생성시 컬럼의 순서는 꽤 중요하다. 아래처럼 인덱스를 생성했다고 가정해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;alter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;table&lt;/span&gt; member
&lt;span class=&quot;token keyword&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;index&lt;/span&gt; member_idx &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gender_type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nick_name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 아래 쿼리를 실행한다고 해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;EXPLAIN&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; gender_type &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; member
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; nick_name &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;g&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 쿼리는 인덱스를 탈 수 있을까? 앞서 인덱스 풀 스캔에서 학습한바에 따르면, 인덱스의 첫번째 컬럼이 조건절에 존재하지 않으므로 인덱스를 타지 않을 것 같다. 따라서 이 경우 &lt;code class=&quot;language-text&quot;&gt;coordindate_x&lt;/code&gt; 로 시작하는 새로운 인덱스를 생성해야한다.&lt;/p&gt;
&lt;p&gt;하지만 MySQL 8.0 부터는 인덱스 스킵 스캔이라는 최적화 기능이 도입되었다. 한번 위 쿼리에 대한 실행 계획을 살펴보면 &lt;code class=&quot;language-text&quot;&gt;Extra&lt;/code&gt; 에 &lt;code class=&quot;language-text&quot;&gt;Using index for skip scan&lt;/code&gt; 이 포함될 것이다. 이는 무슨 뜻일까? MySQL 8.0은 쿼리를 아래처럼 2개의 쿼리로 최적화하여 실행해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; gender_type &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; member &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; gender_type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;MEN&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; nick_name &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;g&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; gender_type &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; member &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; gender_type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;WOMEN&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; nick_name &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;g&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;우리가 직접 추가하지 않는 &lt;code class=&quot;language-text&quot;&gt;gender_type&lt;/code&gt; 컬럼이 조건절에 추가되었다. 인덱스에 대한 첫번쨰 컬럼과 그 컬럼이 가질 수 있는 모든 값 &lt;code class=&quot;language-text&quot;&gt;MEN&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;WOMEN&lt;/code&gt; 에 대해 옵티마이저가 임의로 조건절에 추가한 것이다. 이렇게되면 조건절이 인덱스에 첫번쨰 컬럼을 포함하여 쿼리가 인덱스를 탈 수 있다. 다만, 인덱스 스킵 스캔은 아래 2가지 제약 조건을 충족하였을 때 실행된다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;WHERE 조건절에 조건이 없는 인덱스의 선행 컬럼의 유니크한 값의 개수가 적어야 함.&lt;/li&gt;
&lt;li&gt;쿼리가 인덱스에 존재하는 컬럼만으로 처리 가능해야 함(커버링 인덱스).&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Real MySQL 8.0 (8.3.4) - 백은빈, 이성욱&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/mysql-index-scan/&quot;&gt;https://hudi.blog/mysql-index-scan/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jojoldu.tistory.com/476&quot;&gt;https://jojoldu.tistory.com/476&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[쿼리 튜닝을 위한 HDD 와 SSD 의 순차 I/O 와 랜덤 I/O]]></title><description><![CDATA[컴퓨터는 CPU 나 메모리처럼 전지적 특성을 띤 장치의 성능이 매우 빠른 속도로 발전했다. 하지만, 디스크와 같은 기계식 장치의 성능은 상당치 제한적으로 발전해왔다. 비록 최근에는 대부분 자기 디스크 원판에 의존하는 HDD 보다 SSD…]]></description><link>https://haon.site/database/db-index/storage-and-random-sequantial-io/</link><guid isPermaLink="false">https://haon.site/database/db-index/storage-and-random-sequantial-io/</guid><pubDate>Mon, 14 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;컴퓨터는 CPU 나 메모리처럼 전지적 특성을 띤 장치의 성능이 매우 빠른 속도로 발전했다. 하지만, 디스크와 같은 기계식 장치의 성능은 상당치 제한적으로 발전해왔다. 비록 최근에는 대부분 자기 디스크 원판에 의존하는 HDD 보다 SSD 드라이브를 채택하고 있지만, 여전히 데이터 저장 매체는 컴퓨터에서 가장 느린 부분이라는 것은 변함없는 사실이다. 즉, 데이터베이스 성능 튜닝은 디스크 I/O 를 어떻게 줄이는냐가 관건이다.&lt;/p&gt;
&lt;h2 id=&quot;hdd-와-ssd&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hdd-%EC%99%80-ssd&quot; aria-label=&quot;hdd 와 ssd permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HDD 와 SSD&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a4a525bf44fa20e134bf1ce47c9a8b23/6b26f/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 69.93865030674846%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB90lEQVR42pWTS2/TUBCF+5+7YMu2EqKiC0Rhg4QqIdjwC0ChUETFompKhUKa2Lm2k5jm2vflV+IcZm6K1FBShZFGXnj0zZmZc3dwE8vlEkVZI4hDxNMIrqgoS/81trg3tXEoqJZj5w+wbVtoW6Lb+4kPJ+8xSVMMhiFCEftG96XjdMU6kBVmymI0TvHxyzFypX1nVrAd8JZChuEGOJ3luAh6mJPi+XzhgTz2fwFX0JZUWUyuJXYfP8DD53s4u7zAVX9AjfT2wKZpIKII1hjk2iI3BR0lxqOXezh4c0gqgaqeo24W2wJrCCGgtfbjzXKDhkalJSBVGZQ2dJwA4Uhs3OfGkbWxkLmFpQIrU+TnHdhJACkzWodBWTU+GcKqOT2wqL3F1o7ibUPATDm4aoHvp8d4d/QKSZxg0QLGlBgOBBTtmY90Pct8eg9uso3S1nfjsblQWTJ3WXnY595bnCcdSJ3D0ugiSnA1CMi7zivdCORulhSwUuNWltHa4bT/GvudXZyJryhsgx+9PmJS3+1e+uYlNb5rbNqfYVUEYWhOIPamoss7t0REryfNJAbhGIfPXuDgyVN8OvkGSc15XWtADkuXUtqtxvWvhNPSriTi8RjpL+l3aOiJRkmKIIxQ0riSVsS1d4D/ClY+TacIgiHEaAT27d//b8dv1Igt9QgZ1wcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/a4a525bf44fa20e134bf1ce47c9a8b23/a6d36/image.png&quot;
        srcset=&quot;/static/a4a525bf44fa20e134bf1ce47c9a8b23/222b7/image.png 163w,
/static/a4a525bf44fa20e134bf1ce47c9a8b23/ff46a/image.png 325w,
/static/a4a525bf44fa20e134bf1ce47c9a8b23/a6d36/image.png 650w,
/static/a4a525bf44fa20e134bf1ce47c9a8b23/e548f/image.png 975w,
/static/a4a525bf44fa20e134bf1ce47c9a8b23/3c492/image.png 1300w,
/static/a4a525bf44fa20e134bf1ce47c9a8b23/6b26f/image.png 1658w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;HDD 는 특정 위치에 디스크 헤더를 위치시켜 플래터(원판)을 회전시켜서 읽거나 쓰는 원리로, 매우 성능이 느리다. 이 단점을 보완하고자 SSD가 등장하였다. SSD는 디스크 플레터를 사용하는 방식을 버리고, 플래시 메모리를 사용함으로써 더 빠른 성능을 보인다. SSD가 HDD 보다 더 빠른 이유를 깊게 이해하려면, 랜덤 I/O 와 순차 I/O 에 대해 알아야한다.&lt;/p&gt;
&lt;h2 id=&quot;랜덤-io-와-순차-io&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9E%9C%EB%8D%A4-io-%EC%99%80-%EC%88%9C%EC%B0%A8-io&quot; aria-label=&quot;랜덤 io 와 순차 io permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;랜덤 I/O 와 순차 I/O&lt;/h2&gt;
&lt;h3 id=&quot;hdd-의-io&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hdd-%EC%9D%98-io&quot; aria-label=&quot;hdd 의 io permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HDD 의 I/O&lt;/h3&gt;
&lt;p&gt;HDD 관점에서 랜덤, 순차 I/O 동작 방식을 먼저 학습해보자. 랜덤 I/O 와 순차 I/O 모두 HDD의 플래더(원판)을 돌려서 읽어야 할 데이터가 저장된 위치로 디스크 헤더(Disk Arm)을 이동시킨 다음 데이터를 읽는 것이다. 그렇다면 무엇이 차이점일까?&lt;/p&gt;
&lt;p&gt;차이점은 바로 데이터의 물리적 위치에 따라 데이터 스캔 방식이 다르다는 점이다. 랜덤 I/O는 읽어야하는 &lt;strong&gt;데이터가 물리적으로 불연속적으로 위치&lt;/strong&gt;해있기 떄문에 디스크 헤더를 이동시킨 다음 데이터를 읽는 것을 뜻한다. 이때 디스크 헤드를 이동시키는 시간을 Seek Time 이라고 한다. 반면 순차 I/O 는 읽어야하는 &lt;strong&gt;데이터가 연속적으로 위치&lt;/strong&gt;해 있어서, 헤더를 이곳저곳 이동시킬 필요없이 순차대로 쭉 읽기만 하는 경우를 뜻한다.&lt;/p&gt;
&lt;p&gt;디스크 헤더를 이동시켜야하는 횟수가 많아질수록 성능 저하가 심해진다. 이 떄문에 디스크 헤더를 여러번 이동시켜야하는 랜덤 I/O 가 순차 I/O 에 비해 성능이 꽤 느리다. 랜덤 I/O 가 자주 발생하는 상황에선 Seek Time이 길어지며, 많은 데이터를 찾고 저장하는데 걸리는 연산 비용이 커진다.&lt;/p&gt;
&lt;h3 id=&quot;ssd-의-io&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ssd-%EC%9D%98-io&quot; aria-label=&quot;ssd 의 io permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SSD 의 I/O&lt;/h3&gt;
&lt;p&gt;앞서 설명했듯이, SSD는 HDD와 달리 디스크 원판(플래터)를 사용하지 않고 플래시 메모리를 대신 사용한다. 이 떄문에 일반적으로 SSD가 HDD에 비해 매우 빠른 성능을 보이고, 대부분의 DBMS에서 HDD 대신 SSD를 채택하고 있다.&lt;/p&gt;
&lt;p&gt;그렇다면 SSD는 디스크 원판이 없기 떄문에 랜덤 I/O와 순차 I/O 의 연산 속도의 차이가 없을까? 기대와 달리 SSD 또한 랜덤 I/O 가 순차 I/O 에 비해 성능이, 즉 처리율(throughput)이 꽤 느리다. SSD 는 물리적으로 NAND 플래시 메모리에 저장된 데이터를 직접 쓰고 읽는 것이 아니라, 내부적으로 논리주소를 매핑하는 매핑 테이블을 사용한다. 매핑 테이블을 업데이트하려면 NAND 플래시 메모리의 물리적인 메모리를 찾아갸아 하므로, 랜덤 I/O의 경우 이 과정에 추가적으로 발생하기 때문에 성능이 느리다.&lt;/p&gt;
&lt;h2 id=&quot;쿼리-튜닝의-핵심은-랜덤-io-를-최소화하는-것&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BF%BC%EB%A6%AC-%ED%8A%9C%EB%8B%9D%EC%9D%98-%ED%95%B5%EC%8B%AC%EC%9D%80-%EB%9E%9C%EB%8D%A4-io-%EB%A5%BC-%EC%B5%9C%EC%86%8C%ED%99%94%ED%95%98%EB%8A%94-%EA%B2%83&quot; aria-label=&quot;쿼리 튜닝의 핵심은 랜덤 io 를 최소화하는 것 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쿼리 튜닝의 핵심은 랜덤 I/O 를 최소화하는 것&lt;/h2&gt;
&lt;p&gt;HDD의 성능 개선을 위해 SSD가 등장하였지만, 결국 SSD 또한 마찬가지로 랜덤 I/O 의 성능이 느리다. (몰론 HDD 의 랜덤 I/O 에 비해 SSD 의 랜덤 I/O 가 성능이 매우 빠르긴하다.) 그렇다면 랜덤 I/O 의 발생횟수를 최소화하는 방식으로 쿼리 성능 개선을 꾀하고 싶지만, 아쉽게도 사실상 이는 불가능에 가깝다.&lt;/p&gt;
&lt;h3 id=&quot;대부분의-dbms-서버는-랜덤-io-작업이-많이-발생한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8C%80%EB%B6%80%EB%B6%84%EC%9D%98-dbms-%EC%84%9C%EB%B2%84%EB%8A%94-%EB%9E%9C%EB%8D%A4-io-%EC%9E%91%EC%97%85%EC%9D%B4-%EB%A7%8E%EC%9D%B4-%EB%B0%9C%EC%83%9D%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;대부분의 dbms 서버는 랜덤 io 작업이 많이 발생한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;대부분의 DBMS 서버는 랜덤 I/O 작업이 많이 발생한다&lt;/h3&gt;
&lt;p&gt;대부분의 DBMS 서버에선 순차 I/O 보다 랜덤 I/O 의 비율이 훨씬 높다. 우리의 바람사항은 데이터베이스 연산 수행시 순차 I/O 를 중심으로 연산이 발생하고, 가끔씩 꼭 필요한 경우에만 드물게 랜덤 I/O 가 발생하면 좋겠다는 것이지만, 그렇지 않다.&lt;/p&gt;
&lt;p&gt;데이터베이스내의 대부분의 작업은 작은 데이터를 빈번하게 읽고 쓰는 작업이다. 그리고 읽기는 원하는 데이터의 물리적 위치는 항상 디스크내에서 자주 변하게 된다. 또한 우리가 연속된 데이터를 저장한다고 해도, 빈번하게 읽고 쓰기 때문에 비어있는 디스크 공간이 연속되지 않아 흩여저서 저장될 수도 있다. 결국 랜덤 I/O 는 데이터베이스에서 매우 빈번하게 발생한다.&lt;/p&gt;
&lt;p&gt;MySQL 8.0 InnoDB 엔진에선 쓰기 작업에서 이러한 단점을 극복하기 위해 바로바로 커밋을 디스크에 반영하는 것이 아니라, 버퍼에 저장해놓고 한 번에 디스크에 저장하는 버퍼 기능을 제공한다고는 한다.&lt;/p&gt;
&lt;h3 id=&quot;랜덤-io-연산-횟수를-최소화할-것&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9E%9C%EB%8D%A4-io-%EC%97%B0%EC%82%B0-%ED%9A%9F%EC%88%98%EB%A5%BC-%EC%B5%9C%EC%86%8C%ED%99%94%ED%95%A0-%EA%B2%83&quot; aria-label=&quot;랜덤 io 연산 횟수를 최소화할 것 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;랜덤 I/O 연산 횟수를 최소화할 것&lt;/h3&gt;
&lt;p&gt;랜덤 I/O 를 순차 I/O 로 바꿔서 성능 개선을 유도하고 싶지만, 이 방식 또한 데이터베이스 연산의 특성상 거의 불가능에 가깝다고 한다. 결국, &lt;strong&gt;쿼리 튜닝을 하기 위해선 랜덤 I/O 연산 횟수 자체를 줄여야한다. 즉, 필요한 데이터만 읽도록하여 쿼리를 개선하는 것이 중요하다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;인덱스 레인지 스캔은 데이터를 읽기위해 주로 랜덤 I/O를 이용하고, 테이블 풀 스캔 방식은 순차 I/O 를 사용한다고 한다. 이 떄문에 큰 테이블의 레코드 대부분을 읽는 작업에서는 인덱스를 사용하지 않고 테이블 풀 스캔을 사용하도록 MySQL 옵티마이저가 유도하는 경우도 있다고 하는데, 보통 전체 테이블 레코드의 20~25% 를 넘어서면 인덱스를 타는 것이 아니라 테이블 전체를 모두 읽어오는 풀 테이블 스캔을 유도한다.&lt;/p&gt;
&lt;h3 id=&quot;랜덤-io-를-최소화하기-위한-데이터베이스-인덱스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9E%9C%EB%8D%A4-io-%EB%A5%BC-%EC%B5%9C%EC%86%8C%ED%99%94%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%9D%B8%EB%8D%B1%EC%8A%A4&quot; aria-label=&quot;랜덤 io 를 최소화하기 위한 데이터베이스 인덱스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;랜덤 I/O 를 최소화하기 위한 데이터베이스 인덱스&lt;/h3&gt;
&lt;p&gt;어떻게 하여 랜덤 I/O 횟수를 줄일 수 있을까? ? 일반적으로 랜덤 I/O 를 최소화하여 쿼리 성능을 높이기 위해서 사용하는 인덱스라는 개념을 사용한다. DBMS에서 DB 테이블의 모든 데이터를 검색해서 원하는 결과를 가져오려면 시간이 오래 걸리기 때문에 컬럼의 값과 해당 레코드가 저장된 주소를 키&amp;#x26;값의 쌍으로 인덱스를 만들어 두고 원하는 데이터를 인덱스를 통해서 접근하여 조회 성능을 빠르게 해주게 된다.&lt;/p&gt;
&lt;h2 id=&quot;더-학습해야-할-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EC%95%BC-%ED%95%A0-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해야 할 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해야 할 키워드&lt;/h2&gt;
&lt;ul&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;InnoDB 버퍼&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Real MySQL 8.0 - 백은빈, 이성욱&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@ddangle/%EC%88%9C%EC%B0%A8Sequential-IO%EC%99%80-%EB%9E%9C%EB%8D%A4Random-IO&quot;&gt;https://velog.io/@ddangle/순차Sequential-IO와-랜덤Random-IO&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@tco0427/%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EC%9D%B4%EB%A1%A0-%EB%B0%8F-%EC%8B%A4%EC%A0%84%ED%8E%B8-%EC%BF%BC%EB%A6%AC-%EA%B0%9C%EC%84%A0%EA%B8%B0&quot;&gt;https://velog.io/@tco0427/인덱스-이론-및-실전편-쿼리-개선기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[트랜잭션의 전파(propagation) 속성과 추상화 인터페이스]]></title><description><![CDATA[현재 소개하는 트랜잭션 기능은 SpringBoot…]]></description><link>https://haon.site/haon/database/propagation/</link><guid isPermaLink="false">https://haon.site/haon/database/propagation/</guid><pubDate>Sun, 13 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;현재 소개하는 트랜잭션 기능은 SpringBoot 를 기준으로 진행함을 알려드립니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;여러 명령을 하나의 트랜잭션으로 보장받고 싶은 경우 개발자가 직접 트랜잭션의 경계설정을 통해 트랜잭션을 명시하는 일이 필요합니다.
저희가 사용하는 스프링 프레임워크를 통해 트랜잭션의 경계설정을 데이터베이스에 전달할 수 있는데, 그렇다면 이러한 트랜잭션을 어떻게 지원하고 있을까요?&lt;/p&gt;
&lt;h2 id=&quot;트랜잭션-추상화-인터페이스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%B6%94%EC%83%81%ED%99%94-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4&quot; aria-label=&quot;트랜잭션 추상화 인터페이스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 추상화 인터페이스&lt;/h2&gt;
&lt;p&gt;스프링은 트랜잭션 추상화 인터페이스인 PlatformTransactionManager 를 제공하여 다양한 DataSource 에 알맞게 트랜잭션을 관리할 수 있게 지원하고 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;transaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PlatformTransactionManager&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionManager&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

   &lt;span class=&quot;token class-name&quot;&gt;TransactionStatus&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getTransaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nullable&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionDefinition&lt;/span&gt; definition&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TransactionStatus&lt;/span&gt; status&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rollback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TransactionStatus&lt;/span&gt; status&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;트랜잭션-경계설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B2%BD%EA%B3%84%EC%84%A4%EC%A0%95&quot; aria-label=&quot;트랜잭션 경계설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 경계설정&lt;/h3&gt;
&lt;p&gt;getTransaction 메소드를 통해 파라미터로 전달되는 TransactionDefinition 에 따라 트랜잭션을 시작합니다. 트랜잭션을 문제없이 마치면 commit 메소드를, 또는 문제가 발생하면 rollback 메소드를 호출합니다.
이렇게 getTransaction 부터 commit 또는 rollback 을 호출하는 부분까지가 트랜잭션의 경계설정입니다. 쉽게말해, &lt;strong&gt;하나의 트랜잭션이 시작되면 commit() 또는 rollback() 이 호출될 때 까지의 부분들이 하나의 트랜잭션으로 묶입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;platformtransactionmanager-구현채&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#platformtransactionmanager-%EA%B5%AC%ED%98%84%EC%B1%84&quot; aria-label=&quot;platformtransactionmanager 구현채 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PlatformTransactionManager 구현채&lt;/h3&gt;
&lt;p&gt;그렇다면 위 PlatformTransactionManager 인터페이스를 구현하는 클래스, 즉 스프링에서 제공하는 트랜잭션 매니저 구현체에는 무엇이 있을까요?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DataSourceTransactionManager&lt;/strong&gt; : JDBC 에서 사용되는 트랜잭션 매니저&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JpaTransactionManager&lt;/strong&gt; : JPA 에서 사용되는 매니저&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JtaTransactionManager&lt;/strong&gt; : 글로벌 트랜잭션에서 사용되는 매니저&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 두 트랜잭션 매니저는 하나의 데이터베이스를 사용하거나 각각의 데이터를 독립적으로 사용하는 &lt;strong&gt;로컬 트랜잭션&lt;/strong&gt;의 경우에 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;반면 다중 데이터베이스를 구축하는 경우라면 글로벌 트랜잭션에 사용되는 JtaTransactionManager 를 사용할 수 있습니다. 여러개의 DB에 대한 작업을 하나의 트랜잭션으로 묶을 수 있고, 다른 서버에 분산된 것도 하나로 묶을 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;transactiondefintion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#transactiondefintion&quot; aria-label=&quot;transactiondefintion permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TransactionDefintion&lt;/h3&gt;
&lt;p&gt;앞서 보았던 PlatformTransactionManager 안에 있는 TransactionDefintion 인터페이스는 트랜잭션의 동작방식에 영향을 줄 수 있는 4가지 속성을 정의하고 있습니다. 그 4가지 속성들은 트랜잭션을 세부적으로 이용할 수 있게 도와주며, @Transactional 어노테이션도 공통적으로 적용할 수 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;트랜잭션 전파 (Transaction Propagation)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;격리수준 (Transaction Isolation)&lt;/li&gt;
&lt;li&gt;제한시간 (Transaction Timeout)&lt;/li&gt;
&lt;li&gt;읽기전용 (Transaction ReadOnly)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;사실 조금 더 있긴하지만, 저희는 이 4가지를 중점으로 다루어볼 예정입니다.&lt;/p&gt;
&lt;h3 id=&quot;선언적-트랜잭션&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%A0%EC%96%B8%EC%A0%81-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98&quot; aria-label=&quot;선언적 트랜잭션 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;선언적 트랜잭션&lt;/h3&gt;
&lt;p&gt;또, 위에서 언급한 3가지 트랜잭션 매니저외에도 다른 DataSource 가 들어올때도 사용할 수 있는 다양한 트랜잭션 매너지 구현체들이 존재합니다. 하지만, 이렇게 직접적으로 코드에 구현하는 방식 외에도 &lt;strong&gt;스프링은 AOP 를 이용한 선언적 트랜잭션을 제공&lt;/strong&gt;하고 있습니다.&lt;/p&gt;
&lt;p&gt;선언전 트랜잭션은 크게 2가지 방법이 있는데, 그 중 한가지가 바로 저희가 가장 많이 사용하는 @Transactional 어노테이션을 기반으로 트랜잭션을 설정하는 방법입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;선언적 트랜잭션, 즉 @Transactional 에서 제공하는 여러 옵션(속성)을 뜯어보며 트랜잭션 전파와 격리수준을 어떻게 설정할지 알아보자!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;transactional-원본&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#transactional-%EC%9B%90%EB%B3%B8&quot; aria-label=&quot;transactional 원본 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@Transactional 원본&lt;/h2&gt;
&lt;p&gt;아래는 트랜잭션 어노테이션 @Transactional 에 대한 실제 구현 코드 내용입니다.
value(), transactionManager() 메소드를 보면 알수있듯이, 트랜잭션 매니저를 속성으로 지정 가능합니다. Bean 으로 등록되어있는 특수한 트랜잭션 매니저를 사용하고 싶은 경우에 지정하고 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;또, 아래의 여러 메소드들로 트랜잭션에 대한 세부적인 옵션을 지정할 수 있습니다. 저희는 이 메소드중에 propagation, isolation 을 자세히 뜯어보며 전파와 격리수준을 어떻게 지정해줄지 알아볼겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ElementType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TYPE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ElementType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;METHOD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Retention&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RetentionPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RUNTIME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Inherited&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Documented&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Transactional&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token annotation punctuation&quot;&gt;@AliasFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;transactionManager&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@AliasFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;transactionManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Propagation&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;propagation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Propagation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;REQUIRED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Isolation&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isolation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Isolation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DEFAULT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionDefinition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TIMEOUT_DEFAULT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;timeoutString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readOnly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Throwable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rollbackFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rollbackForClassName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Throwable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;noRollbackFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;noRollbackForClassName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;트랜잭션-전파란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%A0%84%ED%8C%8C%EB%9E%80&quot; aria-label=&quot;트랜잭션 전파란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 전파란?&lt;/h2&gt;
&lt;p&gt;트랜잭션 전파 속성(Propgation) 이란 &lt;strong&gt;이미 진행중인 트랜잭션에 대해 추가적인 트랜잭션 진행을 어떻게할지 결정하는 것&lt;/strong&gt;입니다. 예를들어 어떤 작업 A에 대한 트랜잭션이 진행중이고 작업 B가 시작될 때 어떻게 처리할까에 대한 부분입니다.
전파 속성에 따라 기존의 트랜잭션에 참여할 수도 있고, 별도의 트랜잭션으로 진행할 수도있고, 에러를 발생시키는 등 여러 선택을 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;@Transactional 은 해당 메서드를 하나의 트랜잭션 안에서 진행할 수 있도록 만들어주는 역할을 합니다. 이때 트랜잭션 내부에서 트랜잭션을 또 호출한다면 스프링에서는 어떻게 처리하고 있을까요?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; price&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;buyItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;buyItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;새로운 트랜잭션이 생성될 수도 있고, 이미 트랜잭션이 있다면 부모 트랜잭션에 합류할 수도 있을 것입니다. &lt;strong&gt;진행되고 있는 트랜잭션에서 다른 트랜잭션이 호출될 때 어떻게 처리할지 정하는 것을 &apos;트랜잭션의 전파 설정&apos;이라고 부릅니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;전파-propagation-의-속성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%ED%8C%8C-propagation-%EC%9D%98-%EC%86%8D%EC%84%B1&quot; aria-label=&quot;전파 propagation 의 속성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전파 (propagation) 의 속성&lt;/h2&gt;
&lt;p&gt;지금부터 다양한 트랜잭션의 전파 속성에 대해 알아봅시다.&lt;/p&gt;
&lt;h3 id=&quot;required--a의-트랜잭션에-참여&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#required--a%EC%9D%98-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%97%90-%EC%B0%B8%EC%97%AC&quot; aria-label=&quot;required  a의 트랜잭션에 참여 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;REQUIRED : A의 트랜잭션에 참여&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;트랜잭션 A에 B가 참여하는 방식입니다. 트랜잭션 B의 코드는 새로운 트랜잭션을 만들지 않고 트랜잭션 A에 작업중인 내용에 참여하는 방식입니다.&lt;/strong&gt;
이 경우 트랜잭션 B가 마무리되고 나서, A의 남은 작업 내용을 처리할 때 예외가 발생하면 A와 B의 작업 내용이 모두 롤백됩니다. 왜냐하면 A와 B의 트랜잭션이 하나로 묶여있기 떄문이죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;propagation&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Propagation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;REQUIRED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ... (child 메소드에 대한 트랜잭션 처리후 남은 작업 내용들)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;requires_new--독립적인-트랜잭션-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#requires_new--%EB%8F%85%EB%A6%BD%EC%A0%81%EC%9D%B8-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;requires_new  독립적인 트랜잭션 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;REQUIRES_NEW : 독립적인 트랜잭션 생성&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;반대로 트랜잭션 B를 A와 무관하게 만들 수 있습니다.&lt;/strong&gt; 이 경우 B의 트랜잭션 경계를 빠져나오는 순간 B의 트랜잭션은 독자적으로 커밋 또는 롤백되고, 이것은 A에 어떤 영향도 주지 않습니다. 즉, 아래처럼 메소드 B에 대한 작업 수행 및 트랜잭션 커밋을 완료한 후 작업 (2) 를 수행하다가 예외가 발생해도 앞서 작업한 B에 대한 내용은 롤백되지 않습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;propagation&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Propagation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;REQUIRES_NEW&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 작업 (1)&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 작업 (2)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;supports&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#supports&quot; aria-label=&quot;supports permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SUPPORTS&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;부모 트랜잭션이 존재하면 부모 트랜잭션에 합류하여 트랙잭션이 적용되며 동작하며
(마치 REQUIRED처럼 동작), 없을 경우 트랜잭션이 적용되지 않은 상태로 그냥 동작합니다.&lt;/strong&gt; 아래의 경우 메소드 B에 대한 부모 메소드인 A는 트랜잭션이 적용되어 있는 상태이므로, 트랜잭션이 적용된 상태로 동작합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;propagation&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Propagation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SUPPORTS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;mandatory&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mandatory&quot; aria-label=&quot;mandatory permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MANDATORY&lt;/h3&gt;
&lt;p&gt;이 설정이 적용된 메소드 B는 부모 메소드인 A가 트랜잭션이 적용된 상태라면 트랜잭션이 적용된 상태로 동작하고 (REQUIRED 처럼 동작), 적용 안된 상태라면 예외가 발생합니다.
아래의 경우 메소드 A가 트랜잭션이 적용된 상태이므로 예외가 발생하는 일 없이 트랜잭션으로 정상 동작합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;propagation&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Propagation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MANDATORY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;not_supported--트랜잭션-없이-동작&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#not_supported--%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%97%86%EC%9D%B4-%EB%8F%99%EC%9E%91&quot; aria-label=&quot;not_supported  트랜잭션 없이 동작 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;NOT_SUPPORTED : 트랜잭션 없이 동작&lt;/h3&gt;
&lt;p&gt;메소드 B의 작업에 대해 트랜잭션을 걸지 않을 수 있습니다. (특정 메소드에 대한 트랜잭션 미지원 기능) 만약 메소드 B에 작업이 단순 데이터 조회라면 굳이 트랜잭션이 필요 없겠죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;propagation&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Propagation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NOT_SUPPORTED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;nested--중첩자식-트랜잭션을-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nested--%EC%A4%91%EC%B2%A9%EC%9E%90%EC%8B%9D-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%84-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;nested  중첩자식 트랜잭션을 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;NESTED : 중첩(자식) 트랜잭션을 생성&lt;/h3&gt;
&lt;p&gt;이미 진행중인 트랜잭션에 트랜잭션에 중첩(자식) 트랜잭션을 만드는 것으로, 독립적인 트랜잭션을 만드는 REQUIRES_NEW 와는 다릅니다. NESTED 에 의한 중첩 트랜잭션은 부모 트랜잭션의 영향(커밋과 롤백)을 받지만, 중첩 트랜잭션이 외부에 영향을 주지는 않습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;propagation&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Propagation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NEVER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;never--전-구간에서-트랜잭션-없이-동작&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#never--%EC%A0%84-%EA%B5%AC%EA%B0%84%EC%97%90%EC%84%9C-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%97%86%EC%9D%B4-%EB%8F%99%EC%9E%91&quot; aria-label=&quot;never  전 구간에서 트랜잭션 없이 동작 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;NEVER : 전 구간에서 트랜잭션 없이 동작&lt;/h3&gt;
&lt;p&gt;메소드 B 외에도 기존 트랜잭션이 적용되는 부모 메소드 A 에서도 트랜잭션 없이 동작하도록 합니다. 만약 기존 메소드 A에 트랜잭션이 적용되는 경우 IllegalTransactionStateException 예외가 발생합니다. 아래의 경우 A에 트랜잭션이 적용되어 있으므로 예외가 발생합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;propagation&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Propagation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NEVER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;트랜잭션-격리수준&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80&quot; aria-label=&quot;트랜잭션 격리수준 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 격리수준&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%98-%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80isolation-level-ACID-%EC%84%B1%EC%A7%88%EC%97%90-%EB%8C%80%ED%95%B4&quot;&gt;트랜잭션의 격리수준(Transcation isolation level) 4단계, ACID 성질&lt;/a&gt; 에도 살펴봤듯이, 트랜잭션은 격리수준이라는 개념을 지닙니다. 동시에 여러 트랜잭션이 실행될 때 트랜잭션의 작업 내용을 다른 트랜잭션에게 얼마나 노출시킬 것인지 결정하는 것으로, &lt;strong&gt;기본적으로 데이터베이스에 설정되어 있지만 이 속성을 통해 재설정할 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;격리수준의-속성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80%EC%9D%98-%EC%86%8D%EC%84%B1&quot; aria-label=&quot;격리수준의 속성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;격리수준의 속성&lt;/h3&gt;
&lt;p&gt;가장 낮은 격리수준인 READ_UNCOMMITED 부터 시작해서 READ_UNCOMMITED, READ_COMMITED, REPEATABLE_READ, SERIALIZABLE 등의 설정이 있습니다. 각 격리수준은 &lt;a href=&quot;https://velog.io/@msung99/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%98-%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80isolation-level-ACID-%EC%84%B1%EC%A7%88%EC%97%90-%EB%8C%80%ED%95%B4&quot;&gt;트랜잭션의 격리수준(Transcation isolation level) 4단계, ACID 성질&lt;/a&gt; 에서 살펴본 내용이므로 생략하겠습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isolation&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;isolation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;READ_UNCOMMITED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;orderItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; price&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;데이터베이스에서는 이러한 격리수준에 따라 트랜잭션이 실행되는 동안 각기 다른 lock을 걸고 데이터를 보호하고자 합니다. 격리수준이 높아질수록 더욱 강하게 lock을 걸고 트랜잭션을 마치면 lock을 해제합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;timeout&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#timeout&quot; aria-label=&quot;timeout permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TIMEOUT&lt;/h2&gt;
&lt;p&gt;다음으로 timeout 속성으로 트랜잭션 제한시간을 지정할 수 있습니다. &lt;a href=&quot;https://velog.io/@msung99/JPA-%EC%9D%98-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BD%EC%97%90-%EB%8C%80%ED%95%9C-LockModeType-%EA%B3%B5%EC%9C%A0-%EB%9D%BD%EA%B3%BC-%EB%B0%B0%ED%83%80-%EB%9D%BD&quot;&gt;JPA, MySQL 8.0 에서의 비관적 락 : 공유 락(Shared Lock)과 배타 락(Exclusive Lock) 으로 동시성 이슈 제어하기&lt;/a&gt; 에서도 살펴봤듯이, 데드락(DeadLock) 을 해결하기 위해선 트랜잭션의 생명주기를 지정해줘야합니다.&lt;/p&gt;
&lt;p&gt;초 단위로 제한시간을 지정할 수 있는데, 예시와 같이 어노테이션이 달린 메소드를 수행하는데 10초가 지나면 예외가 발생해 롤백됩니다. 따로 설정하지 않으면 timeout 은 지정되어 있지 않습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timeout &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;orderItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; price&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;readonly&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#readonly&quot; aria-label=&quot;readonly permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ReadOnly&lt;/h2&gt;
&lt;p&gt;마지막으로 readOnly 로 지정하는 읽기전용 트랜잭션입니다. True 로 지정하면 트랜잭션 작업안에서 UPDATE, INSERT, DELETE 작업이 일어나는 것을 방지합니다. readOnly 의 디폴트값은 false로, 모든 작업을 허용합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;readOnly &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;orderItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; price&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5b8b8438-0fd2-466d-a76e-2dbb77368229/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4&gt;출처 : &lt;a href=&quot;https://mangkyu.tistory.com/269&quot;&gt;[Spring] 스프링의 트랜잭션 전파 속성(Transaction propagation) 완벽하게 이해하기&lt;/a&gt;&lt;/h4&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/269&quot;&gt;[Spring] 스프링의 트랜잭션 전파 속성(Transaction propagation) 완벽하게 이해하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@syleemk/-%EB%A9%B4%EC%A0%91-%EB%8C%80%EB%B9%84-%EC%8A%A4%ED%94%84%EB%A7%81-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%A0%84%ED%8C%8C#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%B4-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%9C%EA%B0%80&quot;&gt;[면접 대비] 스프링과 트랜잭션&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kth990303.tistory.com/385&quot;&gt;[Spring] @Transactional의 전파 레벨에 대해 알아보자&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@titu/Spring-JPA-%EB%B6%80%EB%AA%A8-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EB%82%B4%EC%97%90-%EC%9E%90%EC%8B%9D-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%B4-%EC%8B%A4%ED%96%89%EB%90%A0-%EB%95%8C-Transactional&quot;&gt;[Spring JPA] 부모 트랜잭션 내에 자식 트랜잭션이 실행될 때 (@Transactional)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://steady-hello.tistory.com/121&quot;&gt;[Spring] 트랜잭션 전파 알아보기, @Transaction, propagation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[트랜잭션내에 커넥션을 점유하는 외부 API 요청 분리를 통한 TPS 개선기 (feat. JPA OSIV)]]></title><description><![CDATA[💡 현재 포스트는 하모니 팀 기술 블로그 에 게시된 글 입니다. 외부 API 요청이 DB 커넥션을 점유하여 요청이 지연되는 문제상황 성능 개선에 대한 고민이 많은 우리 팀은, 지난 HikariCP 와 데이터베이스 커넥션 풀(DBCP…]]></description><link>https://haon.site/network/transaction-separate-osiv-issue/</link><guid isPermaLink="false">https://haon.site/network/transaction-separate-osiv-issue/</guid><pubDate>Fri, 11 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/transaction-separate-osiv-issue/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;외부-api-요청이-db-커넥션을-점유하여-요청이-지연되는-문제상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%B8%EB%B6%80-api-%EC%9A%94%EC%B2%AD%EC%9D%B4-db-%EC%BB%A4%EB%84%A5%EC%85%98%EC%9D%84-%EC%A0%90%EC%9C%A0%ED%95%98%EC%97%AC-%EC%9A%94%EC%B2%AD%EC%9D%B4-%EC%A7%80%EC%97%B0%EB%90%98%EB%8A%94-%EB%AC%B8%EC%A0%9C%EC%83%81%ED%99%A9&quot; aria-label=&quot;외부 api 요청이 db 커넥션을 점유하여 요청이 지연되는 문제상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;외부 API 요청이 DB 커넥션을 점유하여 요청이 지연되는 문제상황&lt;/h2&gt;
&lt;p&gt;성능 개선에 대한 고민이 많은 우리 팀은, 지난 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/hikaricp-theory/&quot;&gt;HikariCP 와 데이터베이스 커넥션 풀(DBCP) 최적화 고민하기&lt;/a&gt; 에서 최적의 HikariCP 커넥션 풀링 환경을 고민했다. 하지만 이런 노력에도 불구하고, 메인 페이지에서 가장 많은 조회 요청이 발생하는 AI 맞춤 여행지 추천 API 을 위한 성능 개선이 이루어지지 않아 API 응답 속도가 지연되는 문제가 발생하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f43a344205560443b3136d4bbc9b04ef/29007/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.171779141104295%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB9klEQVR42o2R608aQRTF939utLGNTRoDRvswbdTaSm1ckKLVYqUoVVYRENCGCBhiQBDpKg+1vHzB4s6vq9bWmH7oyZzMPTNzbybnSIW4YDeok/VfokYgHxL8CAv21nRywf/npuuQz6MrSK2mRv2kSqV+xNHPItXGMcfVMlq7CQiD+j2Kf9bbyXVymTjSaU2jXKpQKpVR9w8oFIoUi2VarRZXEII/uFvf1/v5LLvpJJKuG1+ObZDw+wj5w6i5POnIKmFFIRxcR91JEVv2EFhS2IonySWiRJVv+L0+dlI7pNf9bChefN5VGo0TY2C7jWfSgf1ZL7aBlyQiEb5Y3mHrM+GyykT9AWYGB7D2mfHNL7A2/xX7816sxttYMIDbJjPRb8Y+NMhptYLUbGnIFhtTpm76Ox/h8awgvxpCftzBa/MLFgxt7TUx0vmA8fd2nJNO7D1PGHjYhXPGjf3tGI6nXfR0dHNo2CYJobPsmmXaMsziR5nUVoI52zizH0YIzc+RjH5nyvIGtzxKPBQgpCwyNTrMkkNmezOKZ9qBc2wE76cJGrUa0rWh6h6qYerFeZ3mxRnZTJqjwwLt1hn1epVMJmXsx1xqF5SKB+T3Mpyf1WhrTXK7WeNMNfoa6PrlzcCbyIylCwTid4LiWv9NVFzz7t2tvu29wi+sdnd6qAzM7wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/f43a344205560443b3136d4bbc9b04ef/a6d36/image.png&quot;
        srcset=&quot;/static/f43a344205560443b3136d4bbc9b04ef/222b7/image.png 163w,
/static/f43a344205560443b3136d4bbc9b04ef/ff46a/image.png 325w,
/static/f43a344205560443b3136d4bbc9b04ef/a6d36/image.png 650w,
/static/f43a344205560443b3136d4bbc9b04ef/e548f/image.png 975w,
/static/f43a344205560443b3136d4bbc9b04ef/3c492/image.png 1300w,
/static/f43a344205560443b3136d4bbc9b04ef/29007/image.png 1600w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;로컬에서 테스트한 결과, 트래픽이 몰리는 상황에서 AI 모델 서빙 서버에 한번 API 요청을 보내고 응답 받기까지 무려 &lt;strong&gt;1,300ms 가 넘는 시간이 소요&lt;/strong&gt;된다. 이는 사용자 경험 측면에서 좋지 않은 상황이다.&lt;/p&gt;
&lt;p&gt;특히, 이 API 를 구성하는 비즈니스 로직에는 외부 서빙 API 를 호출하는 로직뿐 아니라, 데이터베이스내에 저장된 유저 정보를 조회하는 로직까지 함께 포함되어 있다. 따라서 &lt;strong&gt;외부 API 요청을 호출하는 로직 또한 트랜잭션내에서 처리되므로, 외부 API 요청 로직이 불필요하게 데이터베이스 커넥션을 점유하는 문제가 발생하고 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;또한 메인 페이지에서 가장 많은 조회 요청이 발생한다는 점도 주목해야한다. 이 떄문에 다수의 유저가 동시간대에 API 를 호출할 경우 응답 속도가 더 지연되는 &lt;strong&gt;병목 현상이 발생&lt;/strong&gt;한다. 애당초 유저 트래픽이 급증하는 상황에선 데이터베이스내에 병목 현상이 발생할 수 있는 가능성은 충분하기에, 어떤 상황에서 개발하던 간에 병목현상에 대한 대응과 성능 개선이 필요하다.&lt;/p&gt;
&lt;p&gt;이러한 API 요청 로직으로 인해 성능을 어떻게 개선할지 고민이 많았다. 며칠간 고민한 끝에, 우리 팀은 &lt;strong&gt;외부 API 요청시 불필요하게 데이터베이스 커넥션을 점유하는 일이 발생하지 않도록, 외부 API 요청 로직을 트랜잭션에서 분리하여 성능을 개선하는 방법을 고안했다.&lt;/strong&gt; 이 내용을 이번 포스트에서 공유해보고자 한다 😎&lt;/p&gt;
&lt;h2 id=&quot;ai-맞춤-추천-여행지-조회-api-특성-파악하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ai-%EB%A7%9E%EC%B6%A4-%EC%B6%94%EC%B2%9C-%EC%97%AC%ED%96%89%EC%A7%80-%EC%A1%B0%ED%9A%8C-api-%ED%8A%B9%EC%84%B1-%ED%8C%8C%EC%95%85%ED%95%98%EA%B8%B0&quot; aria-label=&quot;ai 맞춤 추천 여행지 조회 api 특성 파악하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AI 맞춤 추천 여행지 조회 API 특성 파악하기&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f43a344205560443b3136d4bbc9b04ef/29007/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.171779141104295%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB9klEQVR42o2R608aQRTF939utLGNTRoDRvswbdTaSm1ckKLVYqUoVVYRENCGCBhiQBDpKg+1vHzB4s6vq9bWmH7oyZzMPTNzbybnSIW4YDeok/VfokYgHxL8CAv21nRywf/npuuQz6MrSK2mRv2kSqV+xNHPItXGMcfVMlq7CQiD+j2Kf9bbyXVymTjSaU2jXKpQKpVR9w8oFIoUi2VarRZXEII/uFvf1/v5LLvpJJKuG1+ObZDw+wj5w6i5POnIKmFFIRxcR91JEVv2EFhS2IonySWiRJVv+L0+dlI7pNf9bChefN5VGo0TY2C7jWfSgf1ZL7aBlyQiEb5Y3mHrM+GyykT9AWYGB7D2mfHNL7A2/xX7816sxttYMIDbJjPRb8Y+NMhptYLUbGnIFhtTpm76Ox/h8awgvxpCftzBa/MLFgxt7TUx0vmA8fd2nJNO7D1PGHjYhXPGjf3tGI6nXfR0dHNo2CYJobPsmmXaMsziR5nUVoI52zizH0YIzc+RjH5nyvIGtzxKPBQgpCwyNTrMkkNmezOKZ9qBc2wE76cJGrUa0rWh6h6qYerFeZ3mxRnZTJqjwwLt1hn1epVMJmXsx1xqF5SKB+T3Mpyf1WhrTXK7WeNMNfoa6PrlzcCbyIylCwTid4LiWv9NVFzz7t2tvu29wi+sdnd6qAzM7wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/f43a344205560443b3136d4bbc9b04ef/a6d36/image.png&quot;
        srcset=&quot;/static/f43a344205560443b3136d4bbc9b04ef/222b7/image.png 163w,
/static/f43a344205560443b3136d4bbc9b04ef/ff46a/image.png 325w,
/static/f43a344205560443b3136d4bbc9b04ef/a6d36/image.png 650w,
/static/f43a344205560443b3136d4bbc9b04ef/e548f/image.png 975w,
/static/f43a344205560443b3136d4bbc9b04ef/3c492/image.png 1300w,
/static/f43a344205560443b3136d4bbc9b04ef/29007/image.png 1600w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;앞서 설명했듯이, 우리 서비스 메인 페이지에선 사용자 정보를 기반으로 AI 맞춤 추천 여행지를 조회할 수 있도록 기능을 제공한다. 이 API 의 특성에 대해 더 자세히 알아보자. AI 맞춤 추천 여행지 조회 API 는 크게 아래와 같은 비즈니스 로직을 거쳐서 요청을 수행한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 데이터베이스에서 가장 최근에 유저가 방문한 상위 10개의 여행지를 가져온다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 방문한 상위 10개 여행지 정보를 외부 AI 서빙 모델에 전송하고, 유저에게 알맞은 여행지 리스트를 추천받는다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 유저가 선호하는 생활정보 카테고리를 데이터베이스에서 조회한다. 그를 기반으로, 앞서 추천받은 여행지들중에 동일한 생활정보 카테고리를 갖는 여행지들로만 필터링하고 클라이언트에게 응답한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;처음 AI 맞춤 여행지 조회 기능을 개발했을 떄는, AI 맞춤 결과를 실제 우리 데이터베이스에 저장한 뒤 변경점에 대해 관리할지, 혹은 매 요청마다 AI 모델 서빙 서버 측에서 여행지 정보를 받아올지 고민이 많았다. 즉, 전자의 방식은 외부 API 요청으로부터 추천받은 데이터를 데이터베이스에 저장하고, 기존에 DB 에 저장된 데이터들 중에 달라진 추천 데이터 일부만 변경하는 방식이다.&lt;/p&gt;
&lt;p&gt;하지만, 이 방식은 DB 에 저장한 정보에 대해 매번 지속적으로 싱크를 맞춰줘야하는 번거로움이 존재했다. 게다가 유저가 최근에 방문한 상위 10개 여행지 정보는 자주 변경되어 그에 대한 추천 결과도 매번 자주 달라질 것으로 에상되어, 캐싱이나 인덱스를 적용한 방식으로도 성능 개선이 불가능했다. 캐싱이나 인덱스 적용은 변경이 적고 조회가 많이 발생하는 상황에 적용하기에 적합하므로, 이 개선 방식은 부적합했다. 따라서 우리 팀은 후자의 방식으로 API 개발을 진행했다.&lt;/p&gt;
&lt;h3 id=&quot;데이터베이스-커넥션이-불필요한-부분&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%BB%A4%EB%84%A5%EC%85%98%EC%9D%B4-%EB%B6%88%ED%95%84%EC%9A%94%ED%95%9C-%EB%B6%80%EB%B6%84&quot; aria-label=&quot;데이터베이스 커넥션이 불필요한 부분 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 커넥션이 불필요한 부분&lt;/h3&gt;
&lt;p&gt;다시 본론으로 돌아가, 위 3가지 비즈니스 로직 단계중 DB 커넥션이 필요하지 않은 부분은 어디일까? 바로 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 번이다. &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 과 달리 외부 API 를 요청하는 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 단계의 경우 추가적인 네트워크 비용만이 발생할 뿐, 데이터베이스를 접근할 필요가 없다. 실제로 작성된 코드 일부를 표현하자면 다음과 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;readOnly &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecommendTripService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FindTripsResponse&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findRecommendTripsByModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; memberId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TripFilterStrategy&lt;/span&gt; tripFilterStrategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tripFilterStrategyProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findTripsByFilterStrategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PREFERRED_LOCATIONS_STRATEGY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; filteredTrips &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tripFilterStrategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreferredLocationsFilterInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;memberId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FindTripsResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tripKeywordRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findByTrips&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filteredTrips&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 서비스 계층에서 &lt;code class=&quot;language-text&quot;&gt;tripFilterStrategy.execute()&lt;/code&gt; 를 호출하고 있다. &lt;code class=&quot;language-text&quot;&gt;execute()&lt;/code&gt; 를 호출함으로써 앞선 AI 맞춤 여행지 조회 로직이 수행된다. 여기서 가장 중요한점은, 여행지 조회 로직이 &lt;code class=&quot;language-text&quot;&gt;@Transactional(readOnly=true)&lt;/code&gt; 에 묶여서 트랜잭션으로 수행된다는 점이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TripPreferredLocationsFilterStrategy&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TripFilterStrategy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreferredLocationsByFilterFinder&lt;/span&gt; preferredLocationsProvider&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ExternalRecommendModelClient&lt;/span&gt; externalRecommendModelClient&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;    

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FilterStandardInfo&lt;/span&gt; filterStandardInfo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findRecommendTripsByModelClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; preferredLocations&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; memberId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// (1) DB 를 조회하여 상위 10개의 여행지 조회&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RecommendTripsByVisitedLogsRequest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LocationPreference&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; locationPreferences &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; preferredLocations&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entrySet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;preferredLocation &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecommendTripsByVisitedLogsRequest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LocationPreference&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;preferredLocation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; preferredLocation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// (2) 외부 API 호출&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecommendTripsByVisitedLogsResponse&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; externalRecommendModelClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;recommendTripsByVisitedLogs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecommendTripsByVisitedLogsRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;locationPreferences&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// (3) DB 를 조회하여 생활정보 필터링&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;filterTripsByLiveinformation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; memberId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 AI 맞춤 여행지 조회 구현체 일부 코드를 살펴보면 위와 같다. 코드에 집중하기보다 해당 코드가 어떤 역할을 수행하는지에만 집중해보자. 주석을 살펴보면 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 번에서 데이터베이스 커넥션을 필요한 로직이 앞선 &lt;code class=&quot;language-text&quot;&gt;@Transactional(readOnly=true)&lt;/code&gt; 로 인해 트랜잭션 내에서 수행되고, 불필요하게 커넥션을 점유한다는 것을 알 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;외부-리소스-조회-api-의-문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%B8%EB%B6%80-%EB%A6%AC%EC%86%8C%EC%8A%A4-%EC%A1%B0%ED%9A%8C-api-%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;외부 리소스 조회 api 의 문제점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;외부 리소스 조회 API 의 문제점&lt;/h3&gt;
&lt;p&gt;이러한 외부 리소스 조회는 추가적인 네트워크 비용이 발생한다. 특히 그 네트워크 지연 속도가 길어질수록 다른 요청에도 영향을 끼칠 수 밖에 없다. &lt;strong&gt;외부 API 요청시 불필요한 커넥션을 잡고있는데, 이로인해 다른 쓰레드에서 데이터베이스 연산을 수횅하기 위해 커넥션을 얻고자 대기하는 시간이 길어지게 된다.&lt;/strong&gt; 결국 트래픽이 몰리면 외부 요청이 트랜잭션내에서 수행됨으로 인해 예상치못하게 데이터베이스내에 커넥션이 모자른 현상이 발생할 수 밖에 없다. HikariCP 와 같은 커넥션 풀에서 제공하는 커넥션 갯수는 한정적이기 떄문이다.&lt;/p&gt;
&lt;h2 id=&quot;트랜잭션-분리를-통한-성능-개선-시도&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EB%B6%84%EB%A6%AC%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0-%EC%8B%9C%EB%8F%84&quot; aria-label=&quot;트랜잭션 분리를 통한 성능 개선 시도 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 분리를 통한 성능 개선 시도&lt;/h2&gt;
&lt;p&gt;이러한 이유로 외부 요청을 트랜잭션 외부로 분리하여 불필요하게 커넥션을 잡고있는 시간을 제거할 필요성을 느낄 수 있다. 위 코드는 아래와 같이 로직을 개선할 수 있었다. 기존에 컨트롤러에서 서비스 계층을 호출했다면, 이젠 &lt;code class=&quot;language-text&quot;&gt;TripPreferredLocationsFilterStrategy&lt;/code&gt; 를 직접 호출한다.&lt;/p&gt;
&lt;p&gt;서비스 계층은 &lt;code class=&quot;language-text&quot;&gt;@Transactional(readOnly=true)&lt;/code&gt; 를 잡고 있기에 모든 비즈니스 로직을 트랜잭션으로 묶어서 수행시킨다. 반면, &lt;code class=&quot;language-text&quot;&gt;TripPreferredLocationsFilterStrategy&lt;/code&gt; 는 트랜잭션 어노테이션 없이 수행되기 때문에, 필요한 일부 비즈니스 로직에 대해서만 트랜잭션을 걸어 연산을 수행시킬 수 있다. 이렇듯 트랜잭션이 필요한, 즉 데이터베이스 연산이 필요한 로직에 대해서만 &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 어노테이션을 사용하여 커넥션을 얻을 수 있도록 개선하였다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/recommend&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecommendTripController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RecommendTripService&lt;/span&gt; recommendTripService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TripFilterStrategyProvider&lt;/span&gt; tripFilterStrategyProvider&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// TripFilterStrategyProvider 는 TripPreferredLocationsFilterStrategy 를 호출하는 컴포넌트&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FindTripsResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findRecommendTripsByModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Authentication&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Accessor&lt;/span&gt; accessor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TripFilterStrategy&lt;/span&gt; tripFilterStrategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tripFilterStrategyProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findTripsByFilterStrategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PREFERRED_LOCATIONS_STRATEGY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FindTripsRespones&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tripFilterStrategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreferredLocationsFilterInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accessor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;tps-를-측정하여-성능-테스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tps-%EB%A5%BC-%EC%B8%A1%EC%A0%95%ED%95%98%EC%97%AC-%EC%84%B1%EB%8A%A5-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;tps 를 측정하여 성능 테스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TPS 를 측정하여 성능 테스트&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/performance-test/&quot;&gt;서버 성능 최적화를 위한 성능 테스트(Performance Test) 환경 구축&lt;/a&gt; 에서 다루었듯이, 우리 팀은 성능 개선의 객관적인 지표를 확인할 수 있도록 &lt;code class=&quot;language-text&quot;&gt;TPS(Transaction Per Seconds)&lt;/code&gt; 를 측정하고 있다. 참고로 HikariCP 설정은 개발용 서버 기준으로 지난 포스트에서 다룬 내용을 아래와 같이 그대로 적용하였다. 각 환경과 상황에따라 커넥션 풀 사이즈를 적절하게 조정하면 될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yml&quot;&gt;&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;spring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;datasource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;hikari&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;maximum-pool-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;connection-timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;트랜잭션에서-외부-api-요청을-분리하지-않은-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%97%90%EC%84%9C-%EC%99%B8%EB%B6%80-api-%EC%9A%94%EC%B2%AD%EC%9D%84-%EB%B6%84%EB%A6%AC%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%80-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;트랜잭션에서 외부 api 요청을 분리하지 않은 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션에서 외부 API 요청을 분리하지 않은 경우&lt;/h3&gt;
&lt;p&gt;Jmeter 툴을 사용하여 TPS 측정 결과를 실제로 확인하였는데, 우선 트랜잭션 밖으로 외부 API 요청을 분리하지 않았을 떄의 결과는 아래와 같이 약 &lt;code class=&quot;language-text&quot;&gt;34.8/sec&lt;/code&gt; 가 측정되었다. 평균 응답속도를 확인해보니 약 &lt;code class=&quot;language-text&quot;&gt;1380ms&lt;/code&gt; 로 느린 속도를 보이고 있다. 이는 사용자에게 큰 불편함을 안겨줄 것이다. 또한 요청 수에 비해 담당할 수 있는 쓰레드 개수와 커넥션 개수가 한정적이다보니, 일부 요청을 처리하지 못하여 약 2.54% 의 애러율도 측정되었다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/09ddc47542f03e80e25fa7f3bfd6282e/c2d9c/image-20.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 19.631901840490798%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAsklEQVR42m1PRxICMQzLv2BrerY34P8fEZaBGwePYkmWHdNZDxcSbIgo4wwXE0IqiHlA7wNi+aBPGT5m9aYyonei5QLroxZniGZeN+znAwwepgXbcamZHE0sBtC8bLtgwrqfmJZNF6zHqXg9X5jXHSYPE7xsrTurobyyansEudCJsemdhjaiM7iTZV4uy8OoWhZ/K3PU+Ctzqxrc6xa1hFRNB/YMJFexhLv/wd9b/V8k9wbC/pFJbiMzSAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/09ddc47542f03e80e25fa7f3bfd6282e/a6d36/image-20.png&quot;
        srcset=&quot;/static/09ddc47542f03e80e25fa7f3bfd6282e/222b7/image-20.png 163w,
/static/09ddc47542f03e80e25fa7f3bfd6282e/ff46a/image-20.png 325w,
/static/09ddc47542f03e80e25fa7f3bfd6282e/a6d36/image-20.png 650w,
/static/09ddc47542f03e80e25fa7f3bfd6282e/e548f/image-20.png 975w,
/static/09ddc47542f03e80e25fa7f3bfd6282e/3c492/image-20.png 1300w,
/static/09ddc47542f03e80e25fa7f3bfd6282e/c2d9c/image-20.png 1326w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a51ae581df9571769562a579f72fb44d/3e096/image-21.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.668711656441715%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB5ElEQVR42mWO6XaqQBCEef83yzlxV1AQlaAsGfZtGEas2zN4E0/8UVZ/1TWNBgtisIuHQsn7AjtfkPhXVEWBB+8xliVGlkzeNBg5n7ysMFY1cY973WBoO0g5whC3AI/tFndrj9E9Ydzu8LAd3Om4PDiQlMvFEnKzhdwfNA9HF5J6w2o9ZTsT0vMhH4DR0SMxm0OsNhDzBfkaYklarDQPpkXzEsOMdjsLw+cMwqSjlKmO5iV1jyeIO/3DJmLg1wDd6QLuuOAXH/zkoQsicsrcMzmJHmhW/bM3dW8hKUKnOmoWdzrYS7Q0aH+VkFPGBzSdQKtEs+4qb3s9a1a7skHXChh1O0Cpevqrqoa8k287zZTXjZhYzXmNhtgo6Kekh0U9+av+Z393b6w8b1DWPYys6pFRIX36r/qf7O/ujZWnNfKKw4izFknZgxUcrOQ0c82v2ffTf1S8dJ5ZwiqkRacOEhQCLBdIcuU9HegR5x2inD6WD2BZr3OW/XbUPtR7ygrqRCWSjA46cYxd6sNhsdYhCbBMXKySE/ZpACeLYJOOGdNsp8QsgklvdsyHm6g8xC0qEAYZjJ17xcGPsP96yotgXUI412/MTRcfGwsfawsz08GnacP2Y70/3lJY56m390K4xPY5xD8cu4jYDnC/GQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/a51ae581df9571769562a579f72fb44d/a6d36/image-21.png&quot;
        srcset=&quot;/static/a51ae581df9571769562a579f72fb44d/222b7/image-21.png 163w,
/static/a51ae581df9571769562a579f72fb44d/ff46a/image-21.png 325w,
/static/a51ae581df9571769562a579f72fb44d/a6d36/image-21.png 650w,
/static/a51ae581df9571769562a579f72fb44d/e548f/image-21.png 975w,
/static/a51ae581df9571769562a579f72fb44d/3c492/image-21.png 1300w,
/static/a51ae581df9571769562a579f72fb44d/3e096/image-21.png 1718w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;우리는 트랜잭션에서 외부 API 요청을 분리했을 떄를 더 개선된 TPS 처리율을 기대해보았다.&lt;/p&gt;
&lt;h3 id=&quot;트랜잭션에서-외부-api-요청을-분리한-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%97%90%EC%84%9C-%EC%99%B8%EB%B6%80-api-%EC%9A%94%EC%B2%AD%EC%9D%84-%EB%B6%84%EB%A6%AC%ED%95%9C-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;트랜잭션에서 외부 api 요청을 분리한 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션에서 외부 API 요청을 분리한 경우&lt;/h3&gt;
&lt;p&gt;하지만 기대와 달리, 트랜잭션을 분리했을 떄와 하지 않았을 떄의 TPS 측정 결과가 거의 동일하게 &lt;code class=&quot;language-text&quot;&gt;35.7/sec&lt;/code&gt; 로 도출된 결과를 확인할 수 있다. 평균 응답속도 또한 &lt;code class=&quot;language-text&quot;&gt;1334ms&lt;/code&gt; 로 별반 차이가 없다. 왜 이렇게 동일한 결과가 도출된 것일까? 🤔&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/07198691aecaa169c048fc08616244cf/844cc/image-23.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 22.699386503067483%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAArElEQVR42pVOWxKCMBDrxYA+6IvyEmFAx/tfY80u6q/6kWnSTbKrnI9k20CpKxQB1sx9zB9ufRAdUj69pac2JJmH1JFxXjRD7cedYkYBxLbfKPej6Ou2SziXAaW98HlZpaCDZ1k3+RumC43zIpnj/iCVEOANtXESjEClwfFvcYHGLMDc2JZiOefs58UGhQ6HlGESX4dXVY0hRq0tvXmlX2i+g3O1ZM68+iX0D55pXLRdh+96GQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/07198691aecaa169c048fc08616244cf/a6d36/image-23.png&quot;
        srcset=&quot;/static/07198691aecaa169c048fc08616244cf/222b7/image-23.png 163w,
/static/07198691aecaa169c048fc08616244cf/ff46a/image-23.png 325w,
/static/07198691aecaa169c048fc08616244cf/a6d36/image-23.png 650w,
/static/07198691aecaa169c048fc08616244cf/e548f/image-23.png 975w,
/static/07198691aecaa169c048fc08616244cf/3c492/image-23.png 1300w,
/static/07198691aecaa169c048fc08616244cf/844cc/image-23.png 1306w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2dcbaf01840663a15588fa0580f1480e/adef7/image-24.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.05521472392638%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB6ElEQVR42n1TW5eaMBjk//+ybs+6rjfQ1UVE7mCAEELC5Uw/ota2D30YZjIZhg9ysNIqQ+0H4L4P6V8h3DO4d4EgDHmOKc0weh6mKDJ6CkNMSYJx5tnLcowzGMOoNSypeuhvF9POxujsMWy2mLY7jJ8rDB9L9O8LDG8/Mfx4w7D4wPC5Rr/eoF+tKbvDQNme7hn2Bwyyg9XQpXU9qNUGamtDUZGy90br9w+oxRKaSjSVaNLq8AVNZYoG0JTTO+eepWIlFawkySFuFdqCQYYxWlZBVhxt1dy5nDWHrIXhVtAAJl/e/dnLCsgogezolV2Xvlst0agRohvQEEQ3vlgOxhePtXisn/smoyYILiEamvD7TAdSd6hFD06oG31nof9a/4vnvtEtcdmimQu/3ABl04Nx/V9+QRsw/vLYzKxFxelQrvENRSVx48qgoGl/c02e4ad+ofjDK+Z7acKSNXQoN4Hk1iIvNUEhZxQgnZUd8koZnvH04kogLeU9S0hpmGzOMYkiq2HFVBYwDr+qEJYCF+JjleJUZzjUEezmCpsHOPIUBx5jWZ/IjylXmv1tfaFSKmb0UBrOOl1yOF6Ile/BCSLsvABOFMK+hDhcYyyOR4PV6Ywl/QA2/VXr6yNL2g5COJQ9BwwnP8MvzuM8OibXRkMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/2dcbaf01840663a15588fa0580f1480e/a6d36/image-24.png&quot;
        srcset=&quot;/static/2dcbaf01840663a15588fa0580f1480e/222b7/image-24.png 163w,
/static/2dcbaf01840663a15588fa0580f1480e/ff46a/image-24.png 325w,
/static/2dcbaf01840663a15588fa0580f1480e/a6d36/image-24.png 650w,
/static/2dcbaf01840663a15588fa0580f1480e/e548f/image-24.png 975w,
/static/2dcbaf01840663a15588fa0580f1480e/3c492/image-24.png 1300w,
/static/2dcbaf01840663a15588fa0580f1480e/adef7/image-24.png 1716w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;-대체-왜-성능-개선이-안되는거야&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-%EB%8C%80%EC%B2%B4-%EC%99%9C-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%EC%9D%B4-%EC%95%88%EB%90%98%EB%8A%94%EA%B1%B0%EC%95%BC&quot; aria-label=&quot; 대체 왜 성능 개선이 안되는거야 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;😤 대체 왜 성능 개선이 안되는거야!?&lt;/h2&gt;
&lt;p&gt;외부 API 요청을 트랜잭션에서 분리했을 떄와 분리하지 않았을 때 차이가 없다는 뜻은, &lt;strong&gt;결국 데이터베이스 커넥션의 생명 주기를 비슷하게 가져가고 있다는 뜻으로 해석할 수 있다.&lt;/strong&gt; 우리 팀이 이렇게 로직 구성을 변경한 이유는, 데이터베이스 커넥션 생명 주기를 짧게 가져가기 위함이다. 커넥션을 잡고있는 짧아야지만 또 다른 쓰레드가 커넥션을 대기시간 없이 획득하고, 빠른 연산 처리후 커넥션을 반납하는 구조로 돌아갈 수 있기 떄문이다. 하지만 이 기대와 달리 스프링 애플리케이션이 구동되지 않고 있었다.&lt;/p&gt;
&lt;h2 id=&quot;원인은-jpa-osiv-open-session-in-view&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%9D%B8%EC%9D%80-jpa-osiv-open-session-in-view&quot; aria-label=&quot;원인은 jpa osiv open session in view permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원인은 JPA OSIV (Open Session In View)&lt;/h2&gt;
&lt;p&gt;왜 커넥션 생명주기가 거의 동일하게 측정되는 것일까? 원인은 생각보다 가까운 곳에 있었다. 바로 JPA 에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;OSIV&lt;/code&gt; 가 활성화되어 있어서 커넥션 생명주기에 문제가 있었던 것이었다. 정말 다행히도, 지난 레플리케이션 DataSource 라우팅 환경을 구축하면서 JPA OSIV 이슈에 관한 트러블슈팅을 만난적이 있었기 떄문에 원인을 생각보다 빨리 찾아낼 수 있었다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 JPA OSIV 와 관련한 자세한 이론과 DB 레플리케이션 트러블슈팅은 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/replication-osiv-issue/&quot;&gt;DB 레플리케이션 환경에서 DataSource 라우팅이 안되는 이슈 해결기 😤 (feat. JPA OSIV)&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/56783c17470878c8d23481c77d586b9b/d7e70/image-11.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.89570552147239%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACA0lEQVR42qWSTU8TURSG5zfqxiiJG/0ZRolx5c64MAgkECRxAZqgBjA0Qi1YShQoOAht6YedTmc6F/o1ne/HmVvaaFyJJ3lyzszcee+97zkKUQD+RUwbgss4J3S5biiRb+M39/D1bMwuvpmPOST0AtriAl3TMBoNmle5Udcko+fRN9FqEUURitOx0RayWHN7mPM5REx5apPUkzk+Ts6yMfmK1UfzpB4vsvJglncPp8k+XUKf3qH6Ik1t6jPV51uo77N4BChhGOJYHdzWEM/q0tcF3WYbdTdDYe0W5dQd6lt3OVufYPnZzZgb7C9PYO3co5m5j/7pNic703hhcuXA49Ko0GqcY+kVTK2E3TGlH9VaEbO2QttYpVxaQFVn+HG6iFb+gFZ9i3rykqOjKQqFGSqVbVw3RAkGPc72NjnYTvEtvS5pFvN0Oh0KxRKWGNDu+uTUQza+ZMiXv9PtBRiXF2wf5Eh/zbFfVKmbP3EdF2XUHc/zsKyWrBNzk2jFRvu+L2vb7nNeLtLrjSYg+mOtEBaDweBKML62qJ1ROz1G1EtEvjMWbMRdNE1T1km2LAvDMIY0h5iGia7rcnNF6gcOx5k1Vt+8Zj+9Fh+3P949adrvBEHw17sRcg7HExn/HEWhzP8TY8F/lRFCSBtEbEHifVInp1SuexLXdWUTHMeR2bZtKfgLzi2AJSlaCrAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/56783c17470878c8d23481c77d586b9b/a6d36/image-11.png&quot;
        srcset=&quot;/static/56783c17470878c8d23481c77d586b9b/222b7/image-11.png 163w,
/static/56783c17470878c8d23481c77d586b9b/ff46a/image-11.png 325w,
/static/56783c17470878c8d23481c77d586b9b/a6d36/image-11.png 650w,
/static/56783c17470878c8d23481c77d586b9b/e548f/image-11.png 975w,
/static/56783c17470878c8d23481c77d586b9b/d7e70/image-11.png 1286w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;지난 포스팅에서 설명했듯이, 스프링은 기본적으로 OSIV 를 활성화시킨다. OSIV 가 제공하는 이점이 많았기 떄문이다. 현재 OSIV 방식은 프레젠테이션 계층에서 &lt;strong&gt;지연 로딩(Lazy Loading)&lt;/strong&gt; 을 사용할 수 있게 해주며, 엔티티를 프레젠테이션에서 변경할 수 없게 한다. 여러가지 이점들로 인해 현재 OSIV 방식은 프레젠테이션 계층부터 레포지토리 계층에 거친 모든 계층에서 영속성 컨텍스트가 계속 유지된다.&lt;/p&gt;
&lt;p&gt;또한 &lt;strong&gt;영속성 컨텍스트는 데이터베이스 연결을 위해 커넥션 풀에서 커넥션을 획득하는데, 획득환 커넥션은 영속성 컨텍스트가 유지되는 시간동안 반납되지 않는다.&lt;/strong&gt; 그런데 OSIV 활성화 상태에선 영속성 컨텍스트가 전 계층에서 살아있다고 했기 때문에, &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 어노테이션을 최초로 마주했을 떄 얻은 커넥션을 절대 반납하지 않고 끝까지 유지한다.&lt;/p&gt;
&lt;h3 id=&quot;argument-resolver-를-사용하는-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#argument-resolver-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%83%81%ED%99%A9&quot; aria-label=&quot;argument resolver 를 사용하는 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Argument Resolver 를 사용하는 상황&lt;/h3&gt;
&lt;p&gt;지난번에 설명했듯이, 우리 팀은 &lt;code class=&quot;language-text&quot;&gt;ArgumentResolver&lt;/code&gt; 를 사용하여 로그인 처리를 하고있다. &lt;code class=&quot;language-text&quot;&gt;AuthService&lt;/code&gt; 내의 &lt;code class=&quot;language-text&quot;&gt;extractMemberId()&lt;/code&gt; 를 호출하게 되어, 서비스 계층내의 &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 을 일찌감치 마주하게 되므로 커넥션을 재빨리 획득하고, 한번 잡은 커넥션을 절대 놓지 않는다. 또 다른 비즈니스 로직을 수행하면서 &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 을 또 다시 마주해도 동일한 하나의 커넥션으로 요청이 수행되는 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthenticationArgumentResolver&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerMethodArgumentResolver&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthService&lt;/span&gt; authService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthenticationBearerExtractor&lt;/span&gt; authenticationBearerExtractor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Accessor&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resolveArgument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MethodParameter&lt;/span&gt; methodParameter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndViewContainer&lt;/span&gt; modelAndViewContainer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NativeWebRequest&lt;/span&gt; nativeWebRequest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebDataBinderFactory&lt;/span&gt; webDataBinderFactory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nativeWebRequest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getNativeRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BadRequestException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;잘못된 HTTP 요청입니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; accessToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; authenticationBearerExtractor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; authService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;extractMemberId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accessToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Accessor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이로 인해 Argument Resolver 에서 얻은 커넥션이 모든 요청을 수행하고 종료되기 전까지 그대로 유지되는 현상이 발생한 것이다.&lt;/p&gt;
&lt;h3 id=&quot;osiv-비활성화-하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#osiv-%EB%B9%84%ED%99%9C%EC%84%B1%ED%99%94-%ED%95%98%EA%B8%B0&quot; aria-label=&quot;osiv 비활성화 하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OSIV 비활성화 하기&lt;/h3&gt;
&lt;p&gt;원인을 찾았으니, &lt;code class=&quot;language-text&quot;&gt;application.yml&lt;/code&gt; 에서 OSIV 를 비활성하였다. OSIV 를 비홠성화 했으므로 앞선 로직은 아래와 같이 트랜잭션 별로 커넥션 생명주기를 가지게 될 것이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a84e2dccc1ed4641076a3eb9291d3784/5b2ff/image-12.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.828220858895705%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACLUlEQVR42nWS204TURSG+0ze+gy+gMZrTbwxIVGDbQgSIgGN4ikkkKiQqKgxGAJIYqI2lELBKO2U0NPQTgt0WjvTTstMDzPzuRmhQQ4r2dmntb+91r+WjwNzXbom1o7jeMvJN5P47/kZGX1AYCBAUS1657Zte/Psp2lGBu4y9niYh4MBwktBfJww9xDeajVZ/hhkqv8lb4emmH0yQ2m3dOx/h4mFKFeH57n2aI7rL36wGFzDV9dL7CkZ7JZFo6ajpBPUNRW9ZtJJzbD3pZfMTB92dBS9nEOraFQqZVrGPq93wlxZHePywlNupKdZ/LWMb1dOEI2EBKREpVhgNfiVckGmXK7gOjJq7gNK8p2IaIOyqpBLSGxLaxTzeaqWTN74hlSYpUGUWCxyfsqGYdBuOyhygczWtpDApmbUuvqZ5j6W1WQvtU00GMIVvnFJOg10DoGmaZLJpIhEVllZCbMpxahVq12/druNKoqUzWXZSmyhllRiG79PA49H+Wx8gpu9fvqGhukJ9IlH5f/uHbvjdYSu694+K2fOAB5V2XaQ378i1N/D0uAdks/vo+/kOdlmB3BN+wdUstmzInS9YXUENHiLP+MXUMYuwvwltJ2EqH4dQ3RDw7Rxdz+DdBsnMQJJP8rm97NTPtKw3bHIJn+yHpoTbVUTRTE87WyRqkgA1yzgauvYehT2JXLp+PlAy7JoNpsUiyrplOyB6vV6Vz9PQ8elaljoYjSaLvH4Jn8Bx3klCvMA/tAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/a84e2dccc1ed4641076a3eb9291d3784/a6d36/image-12.png&quot;
        srcset=&quot;/static/a84e2dccc1ed4641076a3eb9291d3784/222b7/image-12.png 163w,
/static/a84e2dccc1ed4641076a3eb9291d3784/ff46a/image-12.png 325w,
/static/a84e2dccc1ed4641076a3eb9291d3784/a6d36/image-12.png 650w,
/static/a84e2dccc1ed4641076a3eb9291d3784/e548f/image-12.png 975w,
/static/a84e2dccc1ed4641076a3eb9291d3784/3c492/image-12.png 1300w,
/static/a84e2dccc1ed4641076a3eb9291d3784/5b2ff/image-12.png 2190w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yml&quot;&gt;&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;spring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;jpa&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;open-in-view&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;osiv-비활성화-후-실제-성능-개선-확인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#osiv-%EB%B9%84%ED%99%9C%EC%84%B1%ED%99%94-%ED%9B%84-%EC%8B%A4%EC%A0%9C-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0-%ED%99%95%EC%9D%B8&quot; aria-label=&quot;osiv 비활성화 후 실제 성능 개선 확인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OSIV 비활성화 후 실제 성능 개선 확인&lt;/h2&gt;
&lt;p&gt;동일한 성능 테스트 환경에서 외부 API 요청을 트랜잭션에서 분리했을 떄와 안했을 떄의 TPS 처리량을 확인했다. 그 결과, 보듯이 TPS 측정 결과값이 &lt;code class=&quot;language-text&quot;&gt;219.6/sec&lt;/code&gt; 가 도출되었다. 결과적으로 성능 개선 시도전 &lt;code class=&quot;language-text&quot;&gt;34.8/sec&lt;/code&gt; 에서 &lt;code class=&quot;language-text&quot;&gt;219.6/sec&lt;/code&gt; 로 약 6.1배 극명하게 개선된 것을 확인할 수 있다. 또한 평균 응답속도을 확인했을 때 &lt;code class=&quot;language-text&quot;&gt;1,380ms&lt;/code&gt; 에서 &lt;code class=&quot;language-text&quot;&gt;444ms&lt;/code&gt; 로 약 3.1배 개선되었다 😎&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/45b5345fc403444cc66c96979deecbd9/b4904/image-10.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 19.631901840490798%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAfElEQVR42q2NURLCIAxEuZjaqoRAKGXa3v8qa5KOHfz3Y+ftkg0JXAQpC7IsThNxRkysFGX2mdG6xMUzl/pLqe5DqQ1zJLS+4UXsj0YyxgTSA1ZOmsuy+vK6Hd7r+4G3Hqqt+1yU4faYYbpPT3z9mEdeHeOQT54+jJ/8Qx9L2I/4vnNViwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/45b5345fc403444cc66c96979deecbd9/a6d36/image-10.png&quot;
        srcset=&quot;/static/45b5345fc403444cc66c96979deecbd9/222b7/image-10.png 163w,
/static/45b5345fc403444cc66c96979deecbd9/ff46a/image-10.png 325w,
/static/45b5345fc403444cc66c96979deecbd9/a6d36/image-10.png 650w,
/static/45b5345fc403444cc66c96979deecbd9/e548f/image-10.png 975w,
/static/45b5345fc403444cc66c96979deecbd9/3c492/image-10.png 1300w,
/static/45b5345fc403444cc66c96979deecbd9/b4904/image-10.png 1768w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/538bab4a223ba97c712785a3c0398cb4/10d53/image-9.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.282208588957054%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABkUlEQVR42m2O227UMBRF/f9fgxAVakXV6ZRpAcEUfqEqornYsRPfnXZz7ElmkoGHpbXPxUdmT8/PaJoWbcsRY8I4vhZSGgtzPnk8et7N73yI1E9gxjq8ARhf35CImB+OB0dyKj71lk7TPE31SDCpBgTr4YxHcOGIt8QiF7tIu2RPzuT5nENCiCNYpzR8p2BFDyfIXMHJATbTUY/IuXiuqxaOdyU7LuEaccghH6QDRnton2DoB5owLpEPmIWNnWa9pRxOvYzU5T0TYsCgA3oTMRA95dm9CevevDN5Racx0GHWSAtlEuQQoHRc+byn9Hm9QOgyZxUfIPoArvw/Pu+J/rxeIAxEZ8B+1wotLTSdQyMXdO7UW8zaYrvez++5RkufY08vEpWweOHmgJjgC/P/1KtsUdU9aoIOKtQy4I9wqDoyp+PCU/ZkV5xneedY0848L7s0q1qDmo6z6+033H35iZvP37GdfHv/iLuvv3B5vcP7jzd4d/EJHy43uLjaYnO/x2b3A9sH2t3taXdP+RG3RPZfx4WSr9Pcn1oAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/538bab4a223ba97c712785a3c0398cb4/a6d36/image-9.png&quot;
        srcset=&quot;/static/538bab4a223ba97c712785a3c0398cb4/222b7/image-9.png 163w,
/static/538bab4a223ba97c712785a3c0398cb4/ff46a/image-9.png 325w,
/static/538bab4a223ba97c712785a3c0398cb4/a6d36/image-9.png 650w,
/static/538bab4a223ba97c712785a3c0398cb4/e548f/image-9.png 975w,
/static/538bab4a223ba97c712785a3c0398cb4/3c492/image-9.png 1300w,
/static/538bab4a223ba97c712785a3c0398cb4/10d53/image-9.png 1724w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;OSIV 설정은 스프링에서 기본적으로 활성화시키기 떄문에 이에 대해 인지하지 못할 수 있다. 만약 로컬에서 성능 개선 확인없이 무모하게 개발용 서버에 코드를 반영해버렸다면, OSIV 개념의 중요성에 대해 다시금 학습하지 못했을 수도 있다.&lt;/p&gt;
&lt;p&gt;만약 커넥션 반납을 빠르게 해야 한다면 오랜시간 커넥션을 유지하는 OSIV 설정은 바람직하지 않을 수 있다. 이 또한 서비스가 어떤 시점에 커넥션을 필요로 하는지 더 고민해봐야 겠다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[서블릿 컨테이너의 프론트 컨트롤러 등장 배경, 공통 요청 처리방식]]></title><description><![CDATA[서블릿 컨테이너 프론트 컨트롤러를 이해하기에 앞서, 서블릿 컨테이너에 대해 이해할 필요가 있다. 기존 90년대 전통적인 스프링 프레임워크가 존재했을 당시, 단순히 서블릿 컨테이너(ex…]]></description><link>https://haon.site/haon/spring/front-controller/</link><guid isPermaLink="false">https://haon.site/haon/spring/front-controller/</guid><pubDate>Fri, 11 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;서블릿-컨테이너&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B8%94%EB%A6%BF-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88&quot; aria-label=&quot;서블릿 컨테이너 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서블릿 컨테이너&lt;/h2&gt;
&lt;p&gt;프론트 컨트롤러를 이해하기에 앞서, 서블릿 컨테이너에 대해 이해할 필요가 있다. 기존 90년대 전통적인 스프링 프레임워크가 존재했을 당시, 단순히 서블릿 컨테이너(ex. 톰캣) 내부에 서블릿들을 여러개 띄워놓기만 했었다. &lt;strong&gt;특정 서블릿에 대해 URL 매핑 정보를 기입하면 해당 URL 로 들어온 요청을 그 서블릿이 처리하고, 클라이언트에게 응답하는 방식이었다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이런 방식만으로 불편함을 느껴 등장한 것이 스프링 컨테이너다. 서블릿 컨테이너 뒤에 똑같이 컴포넌트(Bean) 을 보유한 형태를 취한다. 클라이언트로부터 들어온 요청을 서블릿 컨테이너의 특정 컴포넌트(서블릿)이 받고, 그 요청을 스프링 컨테이너의 특정 빈에게 넘겨주는 방식이다.&lt;/p&gt;
&lt;h3 id=&quot;containerless&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#containerless&quot; aria-label=&quot;containerless permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Containerless&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Servlet Containerless&lt;/code&gt; 웹 아키텍처란, 서블릿 컨테이너가 없는 아키텍처 구조를 의미하는 것이 아니다. 서블릿 컨테이너를 필요로 하되, 이를 관리하고 신경쓰기 위해 개발자가 할애햐하는 불편함 및 수고를 제거하는 것이 Containerless 이다.&lt;/p&gt;
&lt;p&gt;즉, 현재 스프링부트는 Containerless 가 적용한 구조를 취하고 있다. ** 서블릿 컨테이너가 실존하고 동작하지만, 이 서블릿 컨테이너를 설치하고 설정하는 등의 과정을 생략하고 곧 바로 스프링부트를 통해 서버를 띄울 수 있는 구조다.** 개발자는 서블릿 컨테이너에 대한 지식 없이도 Bean 컴포넌트에 대한 등록 방법만을 알고 스프링부트 애플리케이션 서버를 띄울 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;서블릿-컨테이너-띄우기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B8%94%EB%A6%BF-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%9D%84%EC%9A%B0%EA%B8%B0&quot; aria-label=&quot;서블릿 컨테이너 띄우기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서블릿 컨테이너 띄우기&lt;/h2&gt;
&lt;p&gt;서블릿 컨테이너로 톰캣을 활용하곘다. 스프링부트의 도움없이 순수히 톰캣을 통해 컨테이너를 띄우겠다는 것이다.&lt;/p&gt;
&lt;p&gt;우선 톰켓은 자바로 만들어진 웹서버 프로그램이다. 자바의 라이브러리 차원에서 내장형 톰캣을 제공해주는데, &lt;code class=&quot;language-text&quot;&gt;Tomcat&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;TomcatServletWebServerFactory&lt;/code&gt; 등의 클래스를 활용하여 내장형 톰캣 객체를 생성 및 활용 가능하다. 스프링부트를 처음에 프로젝트를 만들면 이미 내장형 톰캣이 우리 라이브러리에 들어와있고, 지금껏 이를 우리는 편리하게 사용하고 있었던 것이다.&lt;/p&gt;
&lt;h3 id=&quot;mapping&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mapping&quot; aria-label=&quot;mapping permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Mapping&lt;/h3&gt;
&lt;p&gt;서블릿 컨테이너가 웹 클라이언트로부터 요청을 받으면 여러개의 서블릿중에 어떤 서블릿에게 이 요청을 맡기면 될까를 결정하는데, 이 결정하는 작업을 매핑이라고 한다.&lt;/p&gt;
&lt;p&gt;매핑된 서블릿은 요청을 처리하고, 응답을 만들기 위해서 필요한 작업들을 수행후 작업을 종료한다. 그러면 컨테이너가 다시 클라이언트에게 응답을 해준다. 이를 구현한 간단한 코드는 아래와 같다. 이 전체 코드를 구간별로 나누어서 상세히 알아보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt; serverFactory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;WebServer&lt;/span&gt; webServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; serverFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWebServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servletContext &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			servletContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

					resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpHeaders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CONTENT_TYPE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MediaType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TEXT_PLAIN_VALUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWriter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        webServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;tomcatservletwebserverfactory&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tomcatservletwebserverfactory&quot; aria-label=&quot;tomcatservletwebserverfactory permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TomcatServletWebServerFactory&lt;/h3&gt;
&lt;p&gt;우선 톰캣 팩토리 클래스다. &lt;code class=&quot;language-text&quot;&gt;TomcatServletWebServerFactory&lt;/code&gt; 를 활용하여 &lt;code class=&quot;language-text&quot;&gt;Tomcat&lt;/code&gt; 이라는 내장형 톰캣을 생성해줄 수 있다. 쉽게말해, 이 클래스는 스프링부트의 내장형 톰캣을 쉽게 생성할 수 있게 도와주는 도우미 클래스다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt; serverFactory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;webserver&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webserver&quot; aria-label=&quot;webserver permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;WebServer&lt;/h3&gt;
&lt;p&gt;다음은 WebSerber 이다. 이는 톰캣외에 Jetty 와 같은 여러 서블릿 컨테이너를 추상화 해놓은 것이다. 즉, WebServer 란 인터페이스로써 웹서버 타입을 추상화 해놓은 것으로, Tomac 과 Jetty 등의 다양한 웹서버 클래스 구현체를 수용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;WebServer&lt;/span&gt; webServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; serverFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWebServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;servletcontext&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#servletcontext&quot; aria-label=&quot;servletcontext permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ServletContext&lt;/h3&gt;
&lt;p&gt;WebServer 내부에는 파라미터로 &lt;code class=&quot;language-text&quot;&gt;ServletContextInitalizer&lt;/code&gt; 타입의 구현 객체를 할당하면 된다. 이 생성된 객체를 통해 내부에 서블릿을 하나 생성해 볼 것이며, 해당 서블릿에 대해 매핑하는 과정도 거쳐볼 것이다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;ServletContextInitalizer&lt;/code&gt; 는 인터페이스로, 인터페이스이기 떄문에 이를 구현한 클래스를 오브젝트로 할당해도 좋다. 하지만 이 서블릿을 어디선가 또 여러번 이용할 것은 아니므로 익명 클래스로 간단히 객체를 생성해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;WebServer&lt;/span&gt; webServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; serverFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWebServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servletContext &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	servletContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 파라미터1 : 서블릿 이름 / 파라미터2 : 서블릿 정보 or 서블릿 타입의 오브젝트 =&gt; HttpServlet : 인터페이스 타입인 Servlet 의 구현체&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;서블릿을 생성하는 것은 &lt;code class=&quot;language-text&quot;&gt;servletContext.addServlet()&lt;/code&gt; 을 통해 생성한다. 이 메소드는 첫번째 파라미터로 생성할 서블릿의 이름, 두번쨰 파라미터로 &lt;code class=&quot;language-text&quot;&gt;HttpServlet&lt;/code&gt; 객체를 할당받는다. &lt;code class=&quot;language-text&quot;&gt;HttpServlet&lt;/code&gt; 란 인터페이스 타입의 Servlet 의 구현체다. 즉, 우리는 Servlet 인터페이스의 구현체로 HttpServlet 타입의 객체를 생성했고, 이 객체에서 Servlet 의 여러 메소드들 중에 &lt;code class=&quot;language-text&quot;&gt;service&lt;/code&gt; 라는 메소드를 오버라이딩 할 것이다.&lt;/p&gt;
&lt;h3 id=&quot;serivce-오버라이딩&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#serivce-%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%94%A9&quot; aria-label=&quot;serivce 오버라이딩 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Serivce 오버라이딩&lt;/h3&gt;
&lt;p&gt;Servlet 인터페이스의 &lt;code class=&quot;language-text&quot;&gt;service()&lt;/code&gt; 를 오버라이딩 했다. 이 안에서 요청과 응답을 어떻게 수용하고 처리할 것인지를 정의해주면 된다.이는 &lt;code class=&quot;language-text&quot;&gt;HttpServletRequest&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;HttpServletResponse&lt;/code&gt; 인자를 통해 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;HttpServletRequest&lt;/code&gt; 란 요청을 가져오는 오브젝트, &lt;code class=&quot;language-text&quot;&gt;HttpServletResponse&lt;/code&gt; 란 응답을 만들어내는 오브젝트다. &lt;code class=&quot;language-text&quot;&gt;setStatus()&lt;/code&gt; 로 200 코드를 리턴해줌을 지정하고, &lt;code class=&quot;language-text&quot;&gt;setHeader()&lt;/code&gt; 로 HTTP 응답 헤더의 &lt;code class=&quot;language-text&quot;&gt;Content-Type&lt;/code&gt; 이라는 Key 값에 대한 Value 로 &lt;code class=&quot;language-text&quot;&gt;text/plain&lt;/code&gt; 을 지정해줬다. 응답 헤더의 &quot;Content-Type: text-plain&quot; 을 지정해주면 평범한 문자열을 리턴함을 의미하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;getWriter()&lt;/code&gt; 를 통해 응답의 Body 부분을 구상할 수 있다. 이 안에는 &quot;Hello + ~&quot; 가 바디에 실리게 된다. 마지막으로 &lt;code class=&quot;language-text&quot;&gt;addMapping()&lt;/code&gt; 을 통해 현재 생성한 서블릿이 어떤 URL 에 매핑될 것인지를 지정해줬다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;WebServer&lt;/span&gt; webServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; serverFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWebServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servletContext &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	servletContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpHeaders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CONTENT_TYPE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MediaType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TEXT_PLAIN_VALUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 헤더의 이름(name) 과 값(value) 를 할당. 평범한 문자열을 리턴&lt;/span&gt;
		resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWriter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;톰캣-구동&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%86%B0%EC%BA%A3-%EA%B5%AC%EB%8F%99&quot; aria-label=&quot;톰캣 구동 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;톰캣 구동&lt;/h3&gt;
&lt;p&gt;웹서버의 &lt;code class=&quot;language-text&quot;&gt;start()&lt;/code&gt; 를 호출하면 서블릿 컨테이너를 시작(구동) 할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;webServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;프론트-컨트롤러front-controller&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A1%A0%ED%8A%B8-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%ACfront-controller&quot; aria-label=&quot;프론트 컨트롤러front controller permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프론트 컨트롤러(Front Controller)&lt;/h2&gt;
&lt;p&gt;다음으로 이번의 핵심인 프론트 컨트롤러를 구현해 볼 것이다. 프론트 컨트롤러가 무엇인지 알기전에, 기존 서블릿 처리방식에 대해 다시 되짚어보겠다.&lt;/p&gt;
&lt;h3 id=&quot;중복되는-서블릿-코드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A4%91%EB%B3%B5%EB%90%98%EB%8A%94-%EC%84%9C%EB%B8%94%EB%A6%BF-%EC%BD%94%EB%93%9C&quot; aria-label=&quot;중복되는 서블릿 코드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;중복되는 서블릿 코드&lt;/h3&gt;
&lt;p&gt;기존 서블릿은 각 요청마다 직접 다 하나씩 매핑해줘야하는 번거러움이 있었다. 가령 앞서 살펴봤던 &quot;/hello&quot; 는 이를 담당하는 서블릿을 따로 만들어서 처리를 했다.*즉, 각 서블릿들에 대해 다른 URL 을 각각 매핑하는 방식으로 단순하게 구성했다.&lt;/p&gt;
&lt;p&gt;하지만 이 방식은 &quot;중복되는 코드&quot; 로 부터 단점을 보이게 된다. 생성한 여러 서블릿에서 필요로 하는 공통적인 작업이 각 서블릿 코드안에 중복되서 등장할 수 있다. 여기서 &quot;공통적인 작업&quot; 이란, 인증이나 보안, 다국어처리 등과 같이 모든 웹 요청에 대해서 공통적으로 리턴해줘야 하는 작업 내용일 것이다.&lt;/p&gt;
&lt;h3 id=&quot;프론트-컨트롤러-도입&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A1%A0%ED%8A%B8-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC-%EB%8F%84%EC%9E%85&quot; aria-label=&quot;프론트 컨트롤러 도입 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프론트 컨트롤러 도입&lt;/h3&gt;
&lt;p&gt;중복되는 공통 작업 코드를 해결하고자 등장한 것이 프론트 컨트롤러다. 프론트 컨트롤러란, &lt;strong&gt;모든 서블릿에 공통적으로 등장하는 작업 코드를 중앙화시키는 것으로, 중앙화된 가장 앞단에 (프론트 컨트롤러에) 배치하고 이 서블릿이 공통적인 작업을 처리하는 방식&lt;/strong&gt;이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;프론트-컨트롤러-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A1%A0%ED%8A%B8-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;프론트 컨트롤러 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프론트 컨트롤러 구현&lt;/h2&gt;
&lt;p&gt;기존 코드를 응용하여 프론트 컨트롤러를 구현해보자. 전체 코드는 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HellobootApplication&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt; serverFactory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TomcatServletWebServerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt; helloController &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token class-name&quot;&gt;WebServer&lt;/span&gt; webServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; serverFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWebServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servletContext &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			servletContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token comment&quot;&gt;// 인증, 보안, 다국어등의 공통 처리 코드 부분 (생략함)&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestURI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
						&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

						&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; ret &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; helloController&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

						resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
						resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpHeaders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CONTENT_TYPE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MediaType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TEXT_PLAIN_VALUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
						resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWriter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ret&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestURI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
						resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NOT_FOUND&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
						resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NOT_FOUND&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;
		webServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;hellocontroller-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hellocontroller-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;hellocontroller 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HelloController 생성&lt;/h3&gt;
&lt;p&gt;우선 HelloController 라는 컨트롤러를 생성해줬다. 이 컨트롤러는 특정 서블릿으로 들어온 요청에 대한 복집한 비즈니스 로직을 서블릿 코드 내에서 구현하는 대신에, 이 컨트롤러가 대신하여 처리해준다. 즉, 복잡한 비즈니스 로직을 서블릿이 아닌 HelloController 에 분리 시켜놓은 것이다. 복잡한 로직이 서블릿 코드 내에 모두 담기면 코드가 난잡하고 가독성이 저하됨을 감안한 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt; helloController &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;공통-처리-코드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%B5%ED%86%B5-%EC%B2%98%EB%A6%AC-%EC%BD%94%EB%93%9C&quot; aria-label=&quot;공통 처리 코드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;공통 처리 코드&lt;/h3&gt;
&lt;p&gt;여기서는 생략했지만, 인증 보안 다국어지원 등의 여러 서블릿들에 대한 공통 처리 코드를 구현한다. 그 후 매핑 URL 에 알맞는 특정 서블릿을 호출하는 방식이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// 인증, 보안, 다국어등의 공통 처리 코드 부분 (생략함)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestURI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;hello-매핑&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hello-%EB%A7%A4%ED%95%91&quot; aria-label=&quot;hello 매핑 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;/hello 매핑&lt;/h3&gt;
&lt;p&gt;&quot;/hello&quot; 라는 URL 에 매핑된 서블릿에 대해 처리하는 코드다. 요청으로 전달받은 파라미터 name 을 helloController 에게 넘겨준다. 그러면 helloController 는 복잡한 로직을 수행 후 결과값을 리턴하여 ret 변수에 넘겨줄 것이다. 앞서 말했듯이, 복잡한 로직을 HelloController 가 처리하도록 위임한 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestURI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; ret &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; helloController&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpHeaders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CONTENT_TYPE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MediaType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TEXT_PLAIN_VALUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWriter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ret&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;not-found-처리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#not-found-%EC%B2%98%EB%A6%AC&quot; aria-label=&quot;not found 처리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;NOT FOUND 처리&lt;/h3&gt;
&lt;p&gt;&quot;/hello&quot;, &quot;/user&quot; 와 매핑되는 것이 없다면 NOT FOUND 를 리턴해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NOT_FOUND&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;중앙화-처리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A4%91%EC%95%99%ED%99%94-%EC%B2%98%EB%A6%AC&quot; aria-label=&quot;중앙화 처리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;중앙화 처리&lt;/h3&gt;
&lt;p&gt;마지막으로 중앙화된 처리를 위해 모든 요청을 다 받아야한다. 따라서 매핑시 이렇게 별표를 활용하여, 슬래시 &quot;/&quot; 밑으로 들어오는 모든 요청을 이 서블릿이 다 처리하겠다 라며 서블릿 컨테이너가 등록하는 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;다음엔 스프링부트를 활용하여 DI, 스프링 컨테이너에 대해 더욱이 깊게 다루어보겠다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;DispatcherServlet&lt;/li&gt;
&lt;li&gt;Jetty&lt;/li&gt;
&lt;li&gt;PropertySourcesPlaceholderConfigurer&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Jenkins 와 Nginx 를 활용한 Blue/Green 배포 환경 구축하기]]></title><description><![CDATA[Blue/Green 배포 무중단 배포 아키텍처의 다양한 배포전략 (Rolling, Blue&Green, Canary…]]></description><link>https://haon.site/haon/infra/ci-cd/jenkins-nginx-blue-green/</link><guid isPermaLink="false">https://haon.site/haon/infra/ci-cd/jenkins-nginx-blue-green/</guid><pubDate>Thu, 10 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;bluegreen-배포&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bluegreen-%EB%B0%B0%ED%8F%AC&quot; aria-label=&quot;bluegreen 배포 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Blue/Green 배포&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EB%A5%BC-%EC%9C%84%ED%95%9C-%EB%B0%B0%ED%8F%AC%EC%A0%84%EB%9E%B5Rolling-BlueGreen-Canary-%EC%A0%84%EB%9E%B5&quot;&gt;무중단 배포 아키텍처의 다양한 배포전략 (Rolling, Blue&amp;#x26;Green, Canary 배포에 대해)&lt;/a&gt; 에서도 말했듯이, 특정 서비스는 중단되지 않는 상태로 구버전에서 신버전을 사용자에게 계속해서 배포해야합니다. 그를 위해 서버를 최소 2대이상 확보해야 할것이며, Nginx 와 Apache 같은 &lt;code class=&quot;language-text&quot;&gt;Reverse Proxy&lt;/code&gt; 를 배치함으로써 상황에 따라 적절한 요청을 분산시킬 수 있어야합니다. 무엇보다 가장 중요한것은, &lt;strong&gt;모든 클라이언트의 요청이 거절되는것 없이 적절히 처리되어야 할 것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;현업에서는 &lt;code class=&quot;language-text&quot;&gt;AWS ELB&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;CodeDeploy&lt;/code&gt; 와 같은 다양한 Blue/Green 배포를 지원받을 수 있으나, 이들의 도움을 받아서 배포환경을 구축하는 것은 매우 쉽지만, 인프라를 학습하는 입장에서는 뭔지도 모르고 쓰는것은 좋지 못할겁니다. 이번에는 Jenkins 와 Nginx 를 활용해서 저수준에서부터 시작해서 직접 Blue/Green 아키텍처를 구축해보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;bluegreen-아키텍처-구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bluegreen-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;bluegreen 아키텍처 구성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Blue/Green 아키텍처 구성&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/10b582c3-8a54-4ddc-8835-e034d01d2c3c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;신버전이 깃허브에 PR 이 올라가고 main 브랜치게 병합되면, WebHook 을 통해 Build 가 유발되면서 jar 파일을 빌드하게 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Blue 서버의 프로세스를 Health Check 합니다. Blue 서버에서 프로세스가 죽어있다면 Blue 서버에다 신버전을 배포하고 Green 서버에서 실행되고 있는 기존의 구버전 프로세스를 kill 합니다. 반대로 Blue 서버의 프로세스가 실행중이라면 해당 프로세스를 kill 하고 Green 서버에다 신버전 프로세스를 실행시킵니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;신버전 프로세스를 &lt;code class=&quot;language-text&quot;&gt;nohup&lt;/code&gt; 으로 실행시키고, 해당 프로세스가 정상적으로 실행되었는지 Health Check 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;정상 실행되었다고 판단되면, 기존 구버전 프로세스에는 더 이상 트래픽을 보낼 필요가 없어지므로 Nginx 의 트래픽 분산 방향을 신버전 서버에다 분산시킵니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;또 같은 이유로, 기존 구버전 프로세스는 더 이상 필요없는 것이므로 kill 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;사전-셋팅&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%82%AC%EC%A0%84-%EC%85%8B%ED%8C%85&quot; aria-label=&quot;사전 셋팅 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;사전 셋팅&lt;/h2&gt;
&lt;p&gt;본격적인 아키텍처 구축에 앞서서, 사전에 셋팅해줘야할 작업물들이 있습니다. 우선 Blue 와 Green 서버에는 jdk 가 설치되어 있어야하며, 리버스 프록시 서버에 Nginx 를 설치해주어야 합니다.&lt;/p&gt;
&lt;h3 id=&quot;jenkins-환경변수&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jenkins-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98&quot; aria-label=&quot;jenkins 환경변수 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jenkins 환경변수&lt;/h3&gt;
&lt;p&gt;또 추후 살펴보겠지만, 파이프라인 스크립트에서 리눅스 변수를 활용해야할 일이 있습니다. 이를 위해 Jenkins 의 환경변수 설정이 필요합니다. 위처럼 Dashboard &gt; Jenkins 관리 &gt; 시스템 설절 &gt; Global properties 에서 각 인스턴스의 IP 주소값을 할당해줍시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/879c268e-b979-4a94-b299-eaaf840b8451/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ssh-agent-환경-구축&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ssh-agent-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95&quot; aria-label=&quot;ssh agent 환경 구축 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SSH Agent 환경 구축&lt;/h2&gt;
&lt;p&gt;추후 살펴볼 스크립트를 보면 알겠지만, Jenkins 에서 빌드하고 생성한 jar 파일을 Blue 서버 (또는 Green 서버) 로 전송하기 위해 &lt;code class=&quot;language-text&quot;&gt;scp&lt;/code&gt; 를 활용합니다. 이를 위해선 Publish Over SSH 와 같은 기법이 있지만, 현재는 보안상의 이슈로 인해 잘 사용하지 않는 방법이라고 합니다. 저희는 scp 로 jar 파일 전송시 SSH Agent 를 활영할 것인데, 이를 위한 셋팅이 필요합니다.&lt;/p&gt;
&lt;h3 id=&quot;1-ssh-agent-플러그인-설치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-ssh-agent-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-%EC%84%A4%EC%B9%98&quot; aria-label=&quot;1 ssh agent 플러그인 설치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. SSH Agent 플러그인 설치&lt;/h3&gt;
&lt;p&gt;우선 &lt;code class=&quot;language-text&quot;&gt;SSH Agent 플러그인&lt;/code&gt; 설치가 필요합니다. 이 플러그인은 Jenkins 에서 초기에 제안된 기본 플러그인을 설치할때 함께 설치되는 항목이 아니라서, 별도의 설치가 필요합니다.
&quot;DashBoard &gt; Jenkins 관리 &gt; 플러그인 관리&quot; 로 접속해서 SSH Agent 를 검색하시고 직접 다운로드 받아줍시다.&lt;/p&gt;
&lt;h3 id=&quot;2-ssh-인증서rsa-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-ssh-%EC%9D%B8%EC%A6%9D%EC%84%9Crsa-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;2 ssh 인증서rsa 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. SSH 인증서(RSA) 생성&lt;/h3&gt;
&lt;p&gt;다음으로는 Jenkins 서버에서 scp 로 전송시 SSH 인증방식을 활용하는데, 이를 위해 SSH 인증서를 생성해야합니다. SSH 인증서는 Jenkins 서버에서 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;ssh-keygen&lt;/code&gt; 명령어로 RSA 포맷의 인증서를 생성할 수 있게됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ ssh&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;keygen &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;t rsa &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;b &lt;span class=&quot;token number&quot;&gt;4086&lt;/span&gt;

$ ssh&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;keygen &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;t rsa &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;b &lt;span class=&quot;token number&quot;&gt;4086&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Generating&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; rsa key &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;pair&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;
Enter&lt;/span&gt; file in which &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;save&lt;/span&gt; the key &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;root&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ssh&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;id_rsa&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;root&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ssh&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;id_rsa
&lt;span class=&quot;token class-name&quot;&gt;Enter&lt;/span&gt; passphrase &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;empty &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; no passphrase&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Enter&lt;/span&gt; same passphrase again&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Your&lt;/span&gt; identification has been saved in &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;root&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ssh&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;id_rsa
&lt;span class=&quot;token class-name&quot;&gt;Your&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; key has been saved in &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;root&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ssh&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;id_rsa&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pub
&lt;span class=&quot;token class-name&quot;&gt;The&lt;/span&gt; key fingerprint is&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;발급받게되면 공개키(id_rsa.pub) 와 비밀키(id_rsa) 가 생성되는데, 공개키의 값을 원격 서버(Blue 서버와 Green 서버) 에 복사를 해줘야합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ ssh&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;copy&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;root&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ssh&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;id_rsa&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pub root&lt;span class=&quot;token annotation punctuation&quot;&gt;@111.111.111&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 명령어로 복사를 마쳤다면, SSH 접속이 잘 되는지 직접 확인해봅시다. 아래와 같은 명령어로 원격 접속이 가능한지 봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ ssh &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;i id_rsa root&lt;span class=&quot;token annotation punctuation&quot;&gt;@111.111.111&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 과정을 문제없이 마쳤다면, 원격 서버(Blue 또는 Green 서버) 의 터미널로 접속해지게 됩니다. 만약 다시 Jenkins 서버의 터미널로 다시 되돌아오고 싶다면, &lt;code class=&quot;language-text&quot;&gt;exit&lt;/code&gt; 명령어로 되돌아옵시다.&lt;/p&gt;
&lt;h3 id=&quot;3-credentials--jenkins에-ssh-인증-정보-등록&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-credentials--jenkins%EC%97%90-ssh-%EC%9D%B8%EC%A6%9D-%EC%A0%95%EB%B3%B4-%EB%93%B1%EB%A1%9D&quot; aria-label=&quot;3 credentials  jenkins에 ssh 인증 정보 등록 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Credentials : Jenkins에 ssh 인증 정보 등록&lt;/h3&gt;
&lt;p&gt;&quot;Jenkins 관리 &gt; Security &gt; Credentials&quot; 로 들어가서 앞서 발급받은 SSH 인증서를 Jenkins 에 등록해줍시다. 추후 파이프라인 구성시 SSH Agent 방식을 활용할때를 위해 등록해주는 겁니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/bbafea36-1c25-4ca1-a69e-3652f86725c8/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이를위해 &quot;Add Credentials&quot; 를 눌러서 새로운 Credentials 을 등록해줍시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/06e91161-a360-49e9-995a-f26279a86d16/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;각 라인에 들어갈 정보는 다음과 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kind : SSH Username with private key&lt;/li&gt;
&lt;li&gt;ID : 중복되지 않는 인증 ID - 해당 ID 값으로 pipline에서 인증 정보를 사용&lt;/li&gt;
&lt;li&gt;username : 생략&lt;/li&gt;
&lt;li&gt;private key : ssh-keygen 으로 생성한 SSH 키의 private key 값 내용 (ex: id_rsa) 으로 &lt;code class=&quot;language-text&quot;&gt;$ cat id_rsa&lt;/code&gt; 명령어로 출력되는 내용&lt;/li&gt;
&lt;li&gt;passphrase : ssh-keygen으로 인증키 생성시 입력한 password (ssh-keygen 명령어로 키를 생성할때 별도의 특별한 입력이 없었다면 그냥 공백으로 냅두시면 됩니다!)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx&quot; aria-label=&quot;nginx permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx&lt;/h2&gt;
&lt;p&gt;위와같은 모든 과정을 마쳤다면, 이제부터 본격적인 아키텍처 구성을 시작할때입니다. 우선 Nginx 의 리버스리폭시 환경 구축을 진행해봅시다.&lt;/p&gt;
&lt;h3 id=&quot;sites-enabled&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sites-enabled&quot; aria-label=&quot;sites enabled permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;sites-enabled&lt;/h3&gt;
&lt;p&gt;Nginx 를 설치하고 &quot;/etc/nginx/sites-enabled&quot; 로 접속해서 디폴트로 존재하는 파일인 default 를 제거하고, &lt;code class=&quot;language-text&quot;&gt;vim myapp&lt;/code&gt; 명령어로 myapp 이라는 Nginx 설정파일을 하나 만들어줍시다. 위에서 &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; 라는 명령어를 볼 수 있는데, 이는 외부에서 설정파일을 불러올 수 있는 Nginx 의 기능입니다. 또한 &lt;code class=&quot;language-text&quot;&gt;$service_url&lt;/code&gt; 이라는 URL 로 리버스 프록시 요청을 보내는 모습을 볼 수 있습니다. 이 service-url 이라는 변수에는 service-url.inc 이라는 파일로부터 값이 채워지게 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;server &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    listen &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    include &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;d&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;service&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;inc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    location &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        proxy_pass $service_url&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header &lt;span class=&quot;token class-name&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Real&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IP&lt;/span&gt; $remote_addr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header &lt;span class=&quot;token class-name&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Forwarded&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;For&lt;/span&gt; $proxy_add_x_forwarded_for&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header &lt;span class=&quot;token class-name&quot;&gt;Host&lt;/span&gt; $http_host&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;service-urlinc&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#service-urlinc&quot; aria-label=&quot;service urlinc permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;service-url.inc&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;set $service_url http&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;111.111&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;일단 기본으로는 Blue 인스턴스의 IP로 설정해두었으나, Green으로 설정해도 상관없습니다. 젠킨스가 이 파일을 직접 수정하여 리버스 프록시 방향을 바꿔줄 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;jenkins-파이프라인-스크립트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jenkins-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8&quot; aria-label=&quot;jenkins 파이프라인 스크립트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jenkins 파이프라인 스크립트&lt;/h2&gt;
&lt;p&gt;이번 내용의 가장 핵심인 파이프라인 스크립트 전체 내용입니다. 우선 stage 를 크게 3단계로 구분지었습니다. 특정 깃허브 레포지토로부터 clone 받을 수 있는 &lt;code class=&quot;language-text&quot;&gt;GiHub stage&lt;/code&gt;, 클론받은 내용에 기반해 빌드를 실행후 jar 파일을 생성하는 &lt;code class=&quot;language-text&quot;&gt;Build Stage&lt;/code&gt;, 그리고 Blue/Green 배포가 수행되는 &lt;code class=&quot;language-text&quot;&gt;Deployment Stage&lt;/code&gt; 입니다. 이 중에서 Deployment stage 를 더 자세히 뜯어봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;pipeline &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    agent any
    stages &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token char&quot;&gt;&apos;Github&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            steps &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                git branch&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;main&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;https&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;msung99&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CI&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CD&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Jenkins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;git&apos;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token char&quot;&gt;&apos;Build&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            steps &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                sh &lt;span class=&quot;token string&quot;&gt;&quot;./gradlew bootJar&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&apos;&lt;span class=&quot;token class-name&quot;&gt;Deployment&lt;/span&gt;&apos;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            steps &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                sshagent &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;credentials&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&apos;key&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;jenkins&apos;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    sh &apos;&apos;&apos;#&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;bin&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;bash
                        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; curl &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;s &lt;span class=&quot;token string&quot;&gt;&quot;http://${blue_ip}:8080&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;dev&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
                        then
                            deployment_target_ip&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;$green_ip
                        &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
                            deployment_target_ip&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;$blue_ip
                        fi

                        scp &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;o &lt;span class=&quot;token class-name&quot;&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;no &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;/build&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;libs&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;core&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SNAPSHOT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jar root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;deployment_target_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;home&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ubuntu
                        ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;deployment_target_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nohup java -jar /home/ubuntu/core-0.0.1-SNAPSHOT.jar &gt; /dev/null &amp;amp;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;


                        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; retry_count in \$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;seq &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
                          &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; curl &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;s &lt;span class=&quot;token string&quot;&gt;&quot;http://${deployment_target_ip}:8080&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;dev&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
                          then
                              echo &lt;span class=&quot;token string&quot;&gt;&quot;✅ Health Checking 에 성공했습니다!&quot;&lt;/span&gt;
                              &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;
                          fi

                          &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; $retry_count &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;eq &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                          then
                            echo &lt;span class=&quot;token string&quot;&gt;&quot;❌ Health checking 에 실패했습니다.&quot;&lt;/span&gt;
                            exit &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
                          fi

                          echo &lt;span class=&quot;token string&quot;&gt;&quot;🏥 10초후에 다시 Health Checking 이 시도될 예정입니다.&quot;&lt;/span&gt;
                          sleep &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
                        done

                        ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;nginx_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;echo &apos;set \\\$service_url http://${deployment_target_ip}:8080;&apos; &gt; /etc/nginx/conf.d/service-url.inc &amp;amp;&amp;amp; service nginx reload&quot;&lt;/span&gt;
                        echo &lt;span class=&quot;token string&quot;&gt;&quot;Switch the reverse proxy direction of nginx to ${deployment_target_ip} 🔄&quot;&lt;/span&gt;

                        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${deployment_target_ip}&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${blue_ip}&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                        then
                            ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;green_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k 8080/tcp&quot;&lt;/span&gt;
                        &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
                            ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blue_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k 8080/tcp&quot;&lt;/span&gt;
                        fi
                        echo &lt;span class=&quot;token string&quot;&gt;&quot; ✅ 구버전 프로세스를 종료하고, 신버전 프로세스로 교체합니다.&quot;&lt;/span&gt;
                    &apos;&apos;&apos;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;1-ssh-agent&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-ssh-agent&quot; aria-label=&quot;1 ssh agent permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. SSH Agent&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;sshagent &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;credentials&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&apos;key&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;jenkins&apos;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞서 설명했듯이 scp 및 ssh 전송 방식이 이루어지게 되는데, 이를위해 사전에 등록해둔 SSH 인증서를 불러와야합니다. 이것이 없으면 바로 아래에서 살펴볼 과정속에서 &lt;code class=&quot;language-text&quot;&gt;Permission Denied&lt;/code&gt; 권한 오류가 발생하게 되니, 꼭 참고해주세요!&lt;/p&gt;
&lt;h3 id=&quot;2-blue-서버-health-check&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-blue-%EC%84%9C%EB%B2%84-health-check&quot; aria-label=&quot;2 blue 서버 health check permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Blue 서버 Health Check&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; curl &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;s &lt;span class=&quot;token string&quot;&gt;&quot;http://${blue_ip}:8080&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;dev&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
then
   deployment_target_ip&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;$green_ip
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
   deployment_target_ip&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;$blue_ip
fi&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Blue 서버에서 스프링부트 애플리케이션이 실행중인지 &lt;code class=&quot;language-text&quot;&gt;curl&lt;/code&gt; 명령어로 확인하게 했습니다. &lt;code class=&quot;language-text&quot;&gt;-s&lt;/code&gt; 옵션은 curl 이 실행중에 진행상황이나 오류 메시지등을 출력하지 않도록 하는 것으로, curl 의 출력을 최소화하여 화면에 표시되는 내용을 줄입니다.&lt;/p&gt;
&lt;p&gt;또 리눅스의 &lt;code class=&quot;language-text&quot;&gt;/dev/null&lt;/code&gt; 디렉토리상에 출력 및 에러 메시지를 버리도록 설정했습니다. 이 디렉토리는 데이터를 기록하지 않고, 마치 블랙홀처럼 전달된 내용을 무시하도록 합니다. 즉, curl 의 불필요한 잡다한 출력내용들이 쌓아지않고 위 디렉토리에 전달되면서 자연스래 쓰레기통으로 버려지게 되는 셈입니다.&lt;/p&gt;
&lt;p&gt;그리고 Blue 서버가 살아있다면 deployment_target_ip 에 &lt;code class=&quot;language-text&quot;&gt;Green 서버의 IP&lt;/code&gt; 를 할당하고, 반대로 죽어있다면 &lt;code class=&quot;language-text&quot;&gt;Blue 서버의 IP&lt;/code&gt; 를 할당합니다.&lt;/p&gt;
&lt;h3 id=&quot;3-jar파일-전송-및-신버전-프로세스-실행&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-jar%ED%8C%8C%EC%9D%BC-%EC%A0%84%EC%86%A1-%EB%B0%8F-%EC%8B%A0%EB%B2%84%EC%A0%84-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%8B%A4%ED%96%89&quot; aria-label=&quot;3 jar파일 전송 및 신버전 프로세스 실행 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. jar파일 전송 및 신버전 프로세스 실행&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;scp &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;o &lt;span class=&quot;token class-name&quot;&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;no &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;/build&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;libs&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;core&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SNAPSHOT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jar root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;deployment_target_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;home&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ubuntu
ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;deployment_target_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nohup java -jar /home/ubuntu/core-0.0.1-SNAPSHOT.jar &gt; /dev/null &amp;amp;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;다음으로 &lt;code class=&quot;language-text&quot;&gt;scp&lt;/code&gt; 명령어를 통해 앞서 빌드된 jar 파일을 Blue 또는 Green 서버에 전송합니다. 앞서 살펴본 if 조건문에 따라서, &lt;strong&gt;만일 Blue 서버가 죽어있는경우 이 인스턴스에다 신버전을 배포하면 되므로, jar 파일은 Blue 에 전송될겁니다.&lt;/strong&gt;
그 뒤로 &lt;code class=&quot;language-text&quot;&gt;nohup&lt;/code&gt; 을 통해 jar 파일을 프로세스로써 실행시키는 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;4-10초-간격의-health-check&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-10%EC%B4%88-%EA%B0%84%EA%B2%A9%EC%9D%98-health-check&quot; aria-label=&quot;4 10초 간격의 health check permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 10초 간격의 Health Check&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; retry_count in \$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;seq &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; curl &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;s &lt;span class=&quot;token string&quot;&gt;&quot;http://${deployment_target_ip}:8080&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;dev&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
  then
       echo &lt;span class=&quot;token string&quot;&gt;&quot;✅ Health Checking 에 성공했습니다!&quot;&lt;/span&gt;
       &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;
  fi

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; $retry_count &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;eq &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  then
    echo &lt;span class=&quot;token string&quot;&gt;&quot;❌ Health checking 에 실패했습니다.&quot;&lt;/span&gt;
    exit &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
  fi

  echo &lt;span class=&quot;token string&quot;&gt;&quot;🏥 10초후에 다시 Health Checking 이 시도될 예정입니다.&quot;&lt;/span&gt;
  sleep &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
done&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;다음으로 신버전 프로세스가 정상적으로 실행되었는지 10초 주기의 간격을 두고 최대 5번을 헬스체킹합니다. 앞서 &lt;code class=&quot;language-text&quot;&gt;nohup&lt;/code&gt; 을 통해 프로세스가 문제없이 바로 실행되었다면, Health Checking 에 성공했다는 로그를 곧바로 출력받고 헬스체킹을 그만하게 될것입니다.&lt;/p&gt;
&lt;p&gt;그런데 10초의 간격으로 최대 5번이나 헬스체킹을 시도했음에도 불구하고 신버전 프로세스가 정상 실행되지 않는다면, 빌드를 실패했다고 판단하고 파이프라인 스크립트 내용을 중단시키게됩니다.&lt;/p&gt;
&lt;h3 id=&quot;5-nginx-리버스-프록시-방향-변경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-nginx-%EB%A6%AC%EB%B2%84%EC%8A%A4-%ED%94%84%EB%A1%9D%EC%8B%9C-%EB%B0%A9%ED%96%A5-%EB%B3%80%EA%B2%BD&quot; aria-label=&quot;5 nginx 리버스 프록시 방향 변경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. Nginx 리버스 프록시 방향 변경&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;nginx_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;echo &apos;set \\\$service_url http://${deployment_target_ip}:8080;&apos; &gt; /etc/nginx/conf.d/service-url.inc &amp;amp;&amp;amp; service nginx reload&quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&quot;Switch the reverse proxy direction of nginx to ${deployment_target_ip} 🔄&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞선 헬스체킹 과정을 통해 정상적으로 신버전 프로세스가 Green 서버 (또는 Blue 서버) 에 정상 배포되었다는 것이 안전하게 확인되었다면, Nginx 의 트래픽 분산 방향을 Blue 에서 Green 서버로 (또는 그 반대로) 바꾸게됩니다.&lt;/p&gt;
&lt;h3 id=&quot;6-구버전-프로세스-죽이기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-%EA%B5%AC%EB%B2%84%EC%A0%84-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%A3%BD%EC%9D%B4%EA%B8%B0&quot; aria-label=&quot;6 구버전 프로세스 죽이기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. 구버전 프로세스 죽이기&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${deployment_target_ip}&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${blue_ip}&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
then
	 ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;green_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k 8080/tcp&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
	ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blue_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k 8080/tcp&quot;&lt;/span&gt;
fi
echo &lt;span class=&quot;token string&quot;&gt;&quot; ✅ 구버전 프로세스를 종료하고, 신버전 프로세스로 교체합니다.&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이제 신버전 프로세스가 정상 배포되고 리버스 프록시 서버도 정상적으로 트래픽 분산 방향이 바뀌게 되었으므로, 기존에 있던 Blue 서버의 (또는 Green 서버의) 구버전 프로세스를 Kill 하면 됩니다. 이때 &lt;code class=&quot;language-text&quot;&gt;fuser&lt;/code&gt; 를 사용하면 특정 포트를 점유하고 있는 프로세스를 종료할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;실행결과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%ED%96%89%EA%B2%B0%EA%B3%BC&quot; aria-label=&quot;실행결과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실행결과&lt;/h2&gt;
&lt;p&gt;파이프라인을 직접 실행하면, 아래와 같은 결과를 확인할 수 있습니다. 기존에 실행되고 있던 Blue 서버의 구버전 프로세스를 Kill 하고 Green 서버에 신버전으로 교체하는 모습을 직접 확인해볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c01bcfca-4039-4337-bce8-dd6b7eb47b62/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;한번 더 실행해보면, 앞서 Green 서버에 배포되었던 프로세스를 Kill 하고 Blue 서버에 다시 신버전 프로세스를 배포하는 모습을 볼 수 있습니다. 이로써 Blue/Green 배포가 정상적으로 실행된 모습을 확인할 수 있습니다 😎&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4a045fa6-bc3f-4d7c-80d0-70b3b4525187/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이렇게 직접 제 손으로 Blue/Green 배포를 구현해보니 정말 어려웠던 것 같습니다. 중간중간 머리를 쥐어짰던 기억이 정말 생생하네요! ELB, CodeDeploy 와 같은 고수준이며 추상된 기술에 의존하지않고 저수준에서 직접 구현해보니, 이게 진짜 로드밸런싱이자 무중단배포 아키텍처임을 스스로 깨달을 수 있는 좋은 경험이 된 것 같네요! 🙂&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/zero-downtime-deployment-with-jenkins-and-nginx/&quot;&gt;Jenkins와 Nginx로 스프링부트 애플리케이션 무중단 배포하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jenkins.io/doc/book/using/using-credentials/&quot;&gt;Jenkins Documentation : Using credentials &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://junhyunny.github.io/information/jenkins/github/jenkins-github-webhook/&quot;&gt;젠킨스 Credentials 등록&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sihyung92.oopy.io/e5300d92-1a4e-40f4-b927-a93b2bbb17d2&quot;&gt;젠킨스 파이프라인을 이용한 배포 자동화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://royleej9.tistory.com/m/entry/Jenkins-SSH-%EC%82%AC%EC%9A%A9-pipeline-SSH-Agent&quot;&gt;[Jenkins] SSH 사용 - pipeline SSH Agent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://investechnews.com/ssh-permission-denied-publickey-%EC%A0%91%EC%86%8D-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0/&quot;&gt;[ssh] Permission denied (publickey). 접속 오류 해결하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zionh.tistory.com/14&quot;&gt;[linux] ec2 인스턴스 사용자의 ssh 보안 접속방법 두 가지 : 키페어, 비밀번호&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@sileeee/scp%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-%EC%9B%90%EA%B2%A9-%EC%84%9C%EB%B2%84%EC%97%90-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot;&gt;scp 사용하여 원격 서버에 배포하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;#x26;blogId=eyeballss&amp;#x26;logNo=220881562246&quot;&gt;scp 명령어 리눅스 파일 전송&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL 네임드 락으로 분산 환경에서의 동시성 이슈를 해결해보자!]]></title><description><![CDATA[동시성 이슈를 제어하는 방법은 정말 다양하다. 하지만 해당 상황에 적절히 대응하지 못한다면, 성능 저하의 주범이 될 수 있다. 문제발생 가정 수강신청 사이트가 있다고 해봅시다. Course…]]></description><link>https://haon.site/database/named-lock/</link><guid isPermaLink="false">https://haon.site/database/named-lock/</guid><pubDate>Tue, 08 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;동시성 이슈를 제어하는 방법은 정말 다양하다. 하지만 해당 상황에 적절히 대응하지 못한다면, 성능 저하의 주범이 될 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;문제발생-가정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%A0%9C%EB%B0%9C%EC%83%9D-%EA%B0%80%EC%A0%95&quot; aria-label=&quot;문제발생 가정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문제발생 가정&lt;/h3&gt;
&lt;p&gt;수강신청 사이트가 있다고 해봅시다. Course 라는 테이블이 정의되어 있고, 이 테이블에는 현시간대의 수강신청 인원수 count 필드와 기타 수업정보(수업명, 담당교수명) 등의 정보를 포함하고 있다고 해봅시다. Course 테이블에 대한 레코드가 존재할때, 이 레코드에 대해 여러 클라이언트가 수강신청을 시도하면 동시성 문제가 분명히 발생할 것이므로 비관적 락을 적용할 수가 있을겁니다. 그게 &lt;code class=&quot;language-text&quot;&gt;공유 락(Shared Lock)&lt;/code&gt; 이던 &lt;code class=&quot;language-text&quot;&gt;배타 락(Exclusive Lock)&lt;/code&gt; 으로 구현되던 말이죠.&lt;/p&gt;
&lt;p&gt;그러나 비관적 락은 해당 레코드에 락을 걸기 때문에 수강신청이 아닌, 단순히 해당 수업 정보를 조회하고자 하는 유저들은 락이 걸려있어서 계속 대기상태에 빠질 수밖에 없습니다. 결국 락이 필요없는 비즈니스 로직도 대기상태에 빠지게 되며, 의도치 않게 타 로직에 영향을 끼칠 수 있게 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;분산-락distribution-lock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%84%EC%82%B0-%EB%9D%BDdistribution-lock&quot; aria-label=&quot;분산 락distribution lock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;분산 락(Distribution Lock)&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Redis-%EB%B6%84%EC%82%B0-%EB%9D%BD%EC%9D%84-%EA%B5%AC%ED%98%84%ED%95%B4-race-condition-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0&quot;&gt;[Redis] 분산락(Distribution Lock) 을 구현해 다중화 서버에서 발생하는 동시성 문제 제어하기&lt;/a&gt; 에서도 다루었듯이, Redis 차원에서는 Pub/Sub 에 기반한 &lt;code class=&quot;language-text&quot;&gt;Redisson&lt;/code&gt; 를 활용해 분산락을 제공하고 있습니다. 몰론 &lt;code class=&quot;language-text&quot;&gt;스핀 락(Spin Lock)&lt;/code&gt; 방식으로 &lt;code class=&quot;language-text&quot;&gt;Lettuce&lt;/code&gt; 클라이언트를 활용하는 방법도 설명했었지만, 이는 서버의 많은 부하를 안겨주므로 그다지 좋은 방법은 아닙니다.&lt;/p&gt;
&lt;h3 id=&quot;redis-분산락의-한계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-%EB%B6%84%EC%82%B0%EB%9D%BD%EC%9D%98-%ED%95%9C%EA%B3%84&quot; aria-label=&quot;redis 분산락의 한계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis 분산락의 한계&lt;/h3&gt;
&lt;p&gt;이렇듯 Redis 를 활용하여 분산 락을 구현하는 방법은 직관적이고 널리 알려진 방식이기 때문에 사용하기에 무난한 방식입니다. 그러나, 인프라 환경을 구축시 Redis 서버를 따로 구축해야 한다는 특징이 있으며, 이미 Redis 서버가 존재한다고 한들, 락 처리를 위한 목적으로도 사용되어야한다면 추자적인 부하를 안겨줄것이며, &lt;code class=&quot;language-text&quot;&gt;SPOF(단일 장애지점)&lt;/code&gt; 문제가 발생할 가능성도 무시할 수 없습니다.&lt;/p&gt;
&lt;h3 id=&quot;왜-네임드-락named-lock-을-사용하게-되었는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-%EB%84%A4%EC%9E%84%EB%93%9C-%EB%9D%BDnamed-lock-%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B2%8C-%EB%90%98%EC%97%88%EB%8A%94%EA%B0%80&quot; aria-label=&quot;왜 네임드 락named lock 을 사용하게 되었는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 네임드 락(Named Lock) 을 사용하게 되었는가?&lt;/h3&gt;
&lt;p&gt;분산락을 구현하는 방법에는 MySQL 차원에서 제공하는 네임드 락 기법 외에에도 Redis, Zookeeper 분산 락을 제공합니다. 보편적으로 널리 알려진 방식이 Redis 의 분산락 방식이지만, 이 방식은 Redis 서버에 대해 큰 의존성을 만들기에 부적합한 상황에서는 사용하기에 따라롭습니다.&lt;/p&gt;
&lt;p&gt;현재 운영중인 프로젝트에서도 Redis 의 분산락을 활용하는 방식을 고민을 많이했지만, Redis 로 인한 추가적인 인프라 비용이 꽤 부담스러웠기 떄문에 많은 고민을 하다가 네임드 락 기법을 활용하게 되었습니다. 때문에 MySQL 서버 자체를 활용한 네임드 락을 활용한다면, Redis 서버에 대해 큰 의존성을 제거할 수 있게되며, 인프라 비용도 절감해야하는 상황에선 이 방식이 매우 적합합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;분산-락과-db-락의-차이&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%84%EC%82%B0-%EB%9D%BD%EA%B3%BC-db-%EB%9D%BD%EC%9D%98-%EC%B0%A8%EC%9D%B4&quot; aria-label=&quot;분산 락과 db 락의 차이 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;분산 락과 DB 락의 차이&lt;/h2&gt;
&lt;p&gt;이때 햇갈릴 수 있는점은, 분산 락과 일반적으로 DB에서 설명하는 락(&lt;code class=&quot;language-text&quot;&gt;공유락&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;배타락&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;레코드락&lt;/code&gt;)의 개념은 조금 다릅니다. DB 에서 제공하는 락은 레코드나 테이블등의 &lt;code class=&quot;language-text&quot;&gt;공유자원&lt;/code&gt; 자체에 대해 락을 거는 것이지만, 분산락은 &lt;code class=&quot;language-text&quot;&gt;비즈니스 로직, API 등으로 인해 발생하는 임계영역&lt;/code&gt; 에 대해 락을 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;분산락-vs-db-락&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%84%EC%82%B0%EB%9D%BD-vs-db-%EB%9D%BD&quot; aria-label=&quot;분산락 vs db 락 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;분산락 vs DB 락&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/58707231-7fef-4fc5-b208-b336483b3e5c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;수강신청 상황을 가정해보면, Course 라는 테이블에 기반하여 &quot;수학&quot; 이라는 과목에 대한 엔티티가 생성된 상황을 가정해봅시다. 이 엔티티에는 현 수강신청 인원수 count 필드가 있다면 동시성 문제를 제어하기 위해서 낙관적 락, 비관적 락등의 일반적인 DB 락 기법으로 해결 가능합니다.&lt;/p&gt;
&lt;p&gt;여기까지는 문제가 없어보이지만, &quot;Register_Class&quot; 라는 테이블이 있다고 해봅시다. 특정 유저가 &quot;수학&quot; 에 대한 수강신청을 성공한 경우에, 수강신청 상세정보가 &quot;Register_class&quot; 에 기반하여 새로운 엔티티가 생성되어야 합니다. 즉, 수강신청 비즈니스 로직은 &quot;Course&quot; 의 필드중 count 값을 1증가시키며, Register_class 에 대한 엔티티 객체를 하나 생성해야 하는 로직입니다. 이 로직은 일반적인 DB 락 기법만으로는 해결할 수 없으며, 수강신청 로직이라는 &lt;code class=&quot;language-text&quot;&gt;임계영역&lt;/code&gt; 에 대한 동시성 제어로 해결이 가능해집니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;동시성-이슈concurrency-issue-상황-가정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88concurrency-issue-%EC%83%81%ED%99%A9-%EA%B0%80%EC%A0%95&quot; aria-label=&quot;동시성 이슈concurrency issue 상황 가정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동시성 이슈(Concurrency Issue) 상황 가정&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Redis-%EB%B6%84%EC%82%B0-%EB%9D%BD%EC%9D%84-%EA%B5%AC%ED%98%84%ED%95%B4-race-condition-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0&quot;&gt;[Redis] 분산락(Distribution Lock) 을 구현해 다중화 서버에서 발생하는 동시성 문제 제어하기&lt;/a&gt; 이전에 다루었던 수강신청 동시성 문제 상황을 다시 가정해서, 이번에는 MySQL &lt;code class=&quot;language-text&quot;&gt;네임드 락(Named Lock)&lt;/code&gt; 기법을 활용해서 동시성 문제를 해결해봅시다. 지난번과 인프라 환경이 다른점은, Redis 서버를 별도로 띄우지 않았다는 점입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;인프라에 API 서버를 2대 띄웠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;MySQL DB 서버를 1대를 띄웠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;course&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#course&quot; aria-label=&quot;course permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Course&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@AllArgsConstructor&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Setter&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Course&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GeneratedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenerationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; course_name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; limit_count&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 수강신청 최대 인원수&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; current_count&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 현재 수강신청 인원수 (초기값 0)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Course 는 이번 학기에 수강신청 가능한 엔티티입니다. 디플트로 &quot;국어&quot; 라는 수강과목을 미리 데이터베이스에 생성해두었으며, 신청가능 최대 인원수는 50 으로 설정해두었습니다.&lt;/p&gt;
&lt;h3 id=&quot;registerinfo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#registerinfo&quot; aria-label=&quot;registerinfo permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RegisterInfo&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@AllArgsConstructor&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Setter&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterInfo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GeneratedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenerationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; course_name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;수강신청에 성공했을때, 그에대한 로그를 만들기위해 RegisterInfo 라는 엔티티를 정의했습니다. 컬럼으로 어떤 유저가 신청했는지나, 어떤 날짜에 신청했는지등의 상세정보가 들어가야하지만, 이번엔 간단히 수강신청에 성공한 해당 과목명만 저장할 수 있게 해놓았습니다.&lt;/p&gt;
&lt;h3 id=&quot;courserepository-registerinforepository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#courserepository-registerinforepository&quot; aria-label=&quot;courserepository registerinforepository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CourseRepository, RegisterInfoRepository&lt;/h3&gt;
&lt;p&gt;레포지토리는 Spring Data JPA 를 간단히 활용했습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Repository&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JpaRepository&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Course&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@Repository&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterInfoRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JpaRepository&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RegisterInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;courseservice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#courseservice&quot; aria-label=&quot;courseservice permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CourseService&lt;/h3&gt;
&lt;p&gt;수강신청 비즈니스 로직이 담겨있는 CourseService 입니다. 지난번과 다른점은, RegisterInfo 가 추가된다는 점입니다. 이 로직을 실행한다면 별도의 동시성 문제 처리가 없기 때문에 분명히 문제가 발생할겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseRepository&lt;/span&gt; courseRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterInfoRepository&lt;/span&gt; registerInfoRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CourseRepository&lt;/span&gt; courseRepository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterInfoRepository&lt;/span&gt; registerInfoRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;courseRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;registerInfoRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; registerInfoRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timeout &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerCourse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Course&lt;/span&gt; course &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        course&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCurrent_count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;course&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrent_count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;course&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrent_count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; course&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLimit_count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;마감 되었습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 아래에 비즈니스 로직 정의&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RegisterInfo&lt;/span&gt; registerInfo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        registerInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCourse_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;course&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCourse_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        registerInfoRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;registerInfo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        courseRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;course&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;쓰레드-100개-생성-및-동시성-요청&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%93%B0%EB%A0%88%EB%93%9C-100%EA%B0%9C-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9A%94%EC%B2%AD&quot; aria-label=&quot;쓰레드 100개 생성 및 동시성 요청 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쓰레드 100개 생성 및 동시성 요청&lt;/h2&gt;
&lt;p&gt;예상했듯이, 문제가 발생했습니다. Jmeter 를 활용해서 쓰레드 100개를 생성하고 수강신청 요청을 보냈을때 current_count 값은 50 이 되고 RegisterInfo 는 50개가 생성되어야 할겁니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d5c7d91d-3427-4b25-892b-b3c6f11c842a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그러나 current_count 값은 26개가 되었고, 100개의 쓰레드에 대해 모두 요청이 성공해서 RegisterInfo 가 100개가 생성되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/02356e79-c0f5-468e-b169-8a076ce35a34/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;mysql-네임드-락-named-lock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mysql-%EB%84%A4%EC%9E%84%EB%93%9C-%EB%9D%BD-named-lock&quot; aria-label=&quot;mysql 네임드 락 named lock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MySQL 네임드 락 (Named Lock)&lt;/h2&gt;
&lt;p&gt;MySQL 의 &lt;code class=&quot;language-text&quot;&gt;네임드 락(Named Lock)&lt;/code&gt; 은 테이블이나 레코드, 데이터베이스 객체가 아닌 &lt;strong&gt;사용자가 지정한 문자열(String)에 대해 락을 획득하고 반납하는 잠금 기법&lt;/strong&gt;입니다. 한 세션이 락을 획득한다면 다른 세션은 해당 세션이 락을 해제할 이후 획득할 수 있습니다. 개발자가 애플리리케이션 단에서 락에대한 문자열 이름을 지정하여 제어가 가능합니다.&lt;/p&gt;
&lt;p&gt;앞서 말했듯이 네임드 락은 Redis를 사용하기 위한 인프라 구축, 유지보수 비용을 발생하지 않고, MySQL 을 사용해 분산 락을 구현할 수 있습니다. MySQL 에서는 &lt;code class=&quot;language-text&quot;&gt;GET_LOCK()&lt;/code&gt;을 통해 락을 획득할 수 있고,&lt;code class=&quot;language-text&quot;&gt;RELEASE_LOCK()&lt;/code&gt; 으로 해지할 수 있습니다. 또한 Redis 의 분산락과 마찬가지로 일시적인 락의 정보가 DB에 저장되고, 락을 획득,반납하는 과정에서 DB에 불필요한 부하가 있을 수 있긴합니다. 떄문에 락과 비즈니스 로직의 트랜잭션을 분리할 필요가 있습니다.&lt;/p&gt;
&lt;p&gt;가장 중요한 특징은 &lt;strong&gt;한 세션에서 락을 유지하고 있는동안 다른 세션에서는 동일한 문자열 이름에 대한 락을 획득할 수 없게됩니다.&lt;/strong&gt; 반대로 말해서, 다른 문자열 이름의 락에 대해선 락을 획득할 수 있는 것이고, 결국 &lt;strong&gt;각 문자열 이름별로 락이 관리되는 형태&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;h3 id=&quot;named-lock-쿼리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#named-lock-%EC%BF%BC%EB%A6%AC&quot; aria-label=&quot;named lock 쿼리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Named Lock 쿼리&lt;/h3&gt;
&lt;p&gt;아래의 모든 함수들은 정상적으로 락을 획득하거나 해제한 경우에는 1을, 아니면 NULL 이나 0을 리턴합니다. 또한 &lt;code class=&quot;language-text&quot;&gt;GET_LOCK&lt;/code&gt; 의 2번째 인자값을 보면 타임아웃도 설정가능한 것을 볼 수 있는데, 지정한 타임아웃만큼 락을 획득하기 위해 대기하게 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;select &lt;span class=&quot;token function&quot;&gt;GET_LOCK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&apos;my_lock&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// my_lock 이라는 문자열에 대해 락을 획득한다.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 타임아웃은 2초허용 (최대 2초동안 락 획득을 위해 대기)&lt;/span&gt;
select &lt;span class=&quot;token function&quot;&gt;RELEASE_LOCK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&apos;my_lock&apos;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// my_lock 문자열에 대한 락을 해제&lt;/span&gt;
select &lt;span class=&quot;token function&quot;&gt;IS_FREE_LOCK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&apos;my_lock&apos;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 락이 설정되어 있느 상태인지 확인&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;named-lock-초기적용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#named-lock-%EC%B4%88%EA%B8%B0%EC%A0%81%EC%9A%A9&quot; aria-label=&quot;named lock 초기적용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Named Lock 초기적용&lt;/h2&gt;
&lt;h3 id=&quot;courserepository-리팩토링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#courserepository-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81&quot; aria-label=&quot;courserepository 리팩토링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CourseRepository 리팩토링&lt;/h3&gt;
&lt;p&gt;네임드 락을 활용하기 위해, CourseRepository 에 2가지 메소드를 새롭게 정의해봅시다. getLock 과 releaseLock 에 대한 네임드 락 쿼리 메소드를 정의한 모습을 볼 수 있는데, 메소드의 파라미터로 전달받은 key 값을 기반으로 락을 획득 및 해제합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Repository&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JpaRepository&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Course&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;select GET_LOCK(:key, :timeoutSeconds)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nativeQuery &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getLock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; timeoutSeconds&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;select RELEASE_LOCK(:key)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nativeQuery &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;releaseLock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또한 네임드 락은 Spring Data JPA 나 JPQL 에서 지원하지 않기 떄문에, &lt;code class=&quot;language-text&quot;&gt;nativeQuery&lt;/code&gt; 로 직접 작성해줘야합니다. 또한 nativeQuery 는 기본적으로 내부 트랜잭션을 이용하지 않기때문에, 만약 트랜잭션이 필요하다면 &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 어노테이션을 달아야합니다. 하지만 락을 획득하고 해제하는 로직은 영속성을 이용할 이유가 없으며, 이후에 비즈니스 로직과 동일한 트랜잭션을 탈 필요가 없기때문에 여기서는 트랜잭션을 이용하지 않았습니다.&lt;/p&gt;
&lt;h3 id=&quot;registercourse-1차-리팩토링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#registercourse-1%EC%B0%A8-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81&quot; aria-label=&quot;registercourse 1차 리팩토링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RegisterCourse 1차 리팩토링&lt;/h3&gt;
&lt;p&gt;위에서 구현한 네임드 락을 적용했습니다. &lt;code class=&quot;language-text&quot;&gt;getLock&lt;/code&gt; 의 Key 값으로는 &quot;course-lock:&quot; 에다 각 Course 에 대한 pk 값을 기반으로 문자열로 조합했으며, 최대 10초동안 락을 획득하도록 했습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timeout &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerCourse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// 최대 10초동안 락 획득을 시도&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; available &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;  courseRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;course-lock:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;available &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 락을 획득하지 못한경우&lt;/span&gt;
    	&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;락을 획득하지 못했습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 비즈니스 로직&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Course&lt;/span&gt; course &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;course&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrent_count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;  course&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLimit_count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;마감 되었습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    course&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCurrent_count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;course&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrent_count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token class-name&quot;&gt;RegisterInfo&lt;/span&gt; registerInfo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    registerInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCourse_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;course&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCourse_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	registerInfoRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;registerInfo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	courseRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;course&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	courseRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;releaseLock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;course-lock:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 락을 해제&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;네임드-락을-적용했지만-문제는-여전하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%84%A4%EC%9E%84%EB%93%9C-%EB%9D%BD%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%96%88%EC%A7%80%EB%A7%8C-%EB%AC%B8%EC%A0%9C%EB%8A%94-%EC%97%AC%EC%A0%84%ED%95%98%EB%8B%A4&quot; aria-label=&quot;네임드 락을 적용했지만 문제는 여전하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;네임드 락을 적용했지만 문제는 여전하다&lt;/h3&gt;
&lt;p&gt;그런데 다시 100개의 쓰레드를 생성하고 테스트를 돌려보면, RegisterInfo 가 최대 50개만 생성되어야함에도 불구하고 간혹 그 이상으로 많은 데이터가 생성된 결과를 조회할 수 있게되었습니다. &lt;strong&gt;실패한 이유는 분산락의 해제 시점과 @Transactional 을 이용한 트랜잭션 커밋 시점의 불일치 때문입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;facade-패턴-로직으로-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#facade-%ED%8C%A8%ED%84%B4-%EB%A1%9C%EC%A7%81%EC%9C%BC%EB%A1%9C-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;facade 패턴 로직으로 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Facade 패턴 로직으로 구현&lt;/h2&gt;
&lt;h3 id=&quot;왜-네임드-락을-적용해도-문제가-발생하는걸까--repeatable-read&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-%EB%84%A4%EC%9E%84%EB%93%9C-%EB%9D%BD%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%8F%84-%EB%AC%B8%EC%A0%9C%EA%B0%80-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94%EA%B1%B8%EA%B9%8C--repeatable-read&quot; aria-label=&quot;왜 네임드 락을 적용해도 문제가 발생하는걸까  repeatable read permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 네임드 락을 적용해도 문제가 발생하는걸까? : REPEATABLE READ&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/83fa6c89-d914-43ee-81c9-45bfb10c88ff/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 은 별도의 설정값이 없다면 기본적으로 MySQL 의 기본 격리수준인 &lt;code class=&quot;language-text&quot;&gt;REPEATABLE READ&lt;/code&gt; 으로 동작하게됩니다. 또 앞선 코드를 봤다면 알겠지만, 락 획득과 해제는 registerCourse 메소드 내부에서 동작하고 있습니다. 이 때문에 트랜잭션 A 가 락을 해제하고 커밋되기 전에, 이 락을 기다리던 트랜잭션 B 가 락을 바로 획득한 후 current_count 값을 조회를 상황이 발생하기 때문에, 트랜잭션 A 가 커밋되기 이전의 값을 B가 조회하게 되면서 문제가 발생하는 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;해결방법1--트랜잭션을-제거&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%951--%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%84-%EC%A0%9C%EA%B1%B0&quot; aria-label=&quot;해결방법1  트랜잭션을 제거 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;해결방법1 : 트랜잭션을 제거&lt;/h3&gt;
&lt;p&gt;해결방법은 정말 간단합니다. &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 을 제거함으로써 트랜잭션이라는 논리적인 연산 단위 자체가 없어지도록 만드는것입니다. 이렇게 되면 클라이언트 A 입장에서 네임드 락을 헤재했을때 이미 current_count 값은 DB 상에 정상 반영된 상태이므로, 클라이언트 B 가 해제된 락을 즉시 획득하고 current_count 값을 조회하면 정상 반영된 값을 조회할 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;하지만 이 방식은 데이터 일관성의 문제를 쉽게 해칠 수 있으며, 문제가 발생하더라도 롤백이 되지 못하는 구조이기 떄문에 좋지 못한 방식입니다.&lt;/p&gt;
&lt;h3 id=&quot;해결방법2--논리적인-단위로-트랜잭션을-분이하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%952--%EB%85%BC%EB%A6%AC%EC%A0%81%EC%9D%B8-%EB%8B%A8%EC%9C%84%EB%A1%9C-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%84-%EB%B6%84%EC%9D%B4%ED%95%98%EA%B8%B0&quot; aria-label=&quot;해결방법2  논리적인 단위로 트랜잭션을 분이하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;해결방법2 : 논리적인 단위로 트랜잭션을 분이하기&lt;/h3&gt;
&lt;p&gt;수강신청 비즈니스 로직과 락 생성 및 해제 로직을 별도의 트랜잭션으로 따로 분리하는 방법입니다. 락을 획득하고 해제하는 로직이 기존 비즈니스의 로직의 트랙잭션 범위 밖으로 벗어나서, 별도의 새로운 트랜잭션으로 처리되는 방식인것입니다. 아래에서 이어서 자세히 설명하겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;facade-패턴으로-네임드-락에-대한-로직을-별도로-분리하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#facade-%ED%8C%A8%ED%84%B4%EC%9C%BC%EB%A1%9C-%EB%84%A4%EC%9E%84%EB%93%9C-%EB%9D%BD%EC%97%90-%EB%8C%80%ED%95%9C-%EB%A1%9C%EC%A7%81%EC%9D%84-%EB%B3%84%EB%8F%84%EB%A1%9C-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0&quot; aria-label=&quot;facade 패턴으로 네임드 락에 대한 로직을 별도로 분리하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Facade 패턴으로 네임드 락에 대한 로직을 별도로 분리하기&lt;/h3&gt;
&lt;p&gt;이를 해결하기위해 &lt;code class=&quot;language-text&quot;&gt;Facade 패턴&lt;/code&gt; 을 적용해서 수강신청 메소드를 기능별로 캡슐화시키서 로직을 분리해봅시다. 네임드 락에 대한 로직과, 비즈니스 로직을 별도의 메소드로 각각 분리하고, 별도의 트랜잭션으로 처리할 것입니다. 이를위해 트랜잭션 전파 속성인 &lt;code class=&quot;language-text&quot;&gt;REQUIRES_NEW&lt;/code&gt; 로 분리할 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timeout &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerCourse2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; lock_key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;token comment&quot;&gt;// 락 획득을 시도&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; available &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lock_key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
         &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;available &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 락을 획득하지 못한경우&lt;/span&gt;
		 	&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;락을 획득하지 못했습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

     &lt;span class=&quot;token comment&quot;&gt;// current_count 에 대한 커밋이 보장된 상태에서 락을 해제&lt;/span&gt;
	 &lt;span class=&quot;token function&quot;&gt;registerCourseLogic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      courseRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;releaseLock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lock_key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 락을 해제&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;propagation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Propagation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;REQUIRES_NEW&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerCourseLogic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// 비즈니스 로직&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;Course&lt;/span&gt; course &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;course&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrent_count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;  course&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLimit_count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;마감 되었습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    course&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCurrent_count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;course&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrent_count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token class-name&quot;&gt;RegisterInfo&lt;/span&gt; registerInfo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    registerInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCourse_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;course&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCourse_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    registerInfoRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;registerInfo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    courseRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;course&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;보듯이 비즈니스 로직을 따로 분리해서 &lt;code class=&quot;language-text&quot;&gt;REQUIRES_NEW&lt;/code&gt; 로 별도의 트랜잭션으로써 처리되도록 했습니다. 이로써 current_count 컬럼 값이 완벽히 DB 상에 반영되고나서 (커밋되고나서), 안전하게 락을 해제한 후에 대기하고 있었던 다음 쓰레드가 락을 획득하고 current_count 값을 읽어오게 됩니다.&lt;/p&gt;
&lt;p&gt;실제로 쓰레드 100개를 생성하고 다시 생성을 했을떄도 정상적으로 50개의 요청이 성공하고, 나머지 50개의 요청이 거절되는 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;네임드-락을-언제-사용해야할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%84%A4%EC%9E%84%EB%93%9C-%EB%9D%BD%EC%9D%84-%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;네임드 락을 언제 사용해야할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;네임드 락을 언제 사용해야할까?&lt;/h2&gt;
&lt;p&gt;앞서 소소하게 언급했던 주제이지만, 마무리겸 다시 한번 정리해보고자 합니다.&lt;/p&gt;
&lt;h3 id=&quot;분산락-vs-일반적인-db-락&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%84%EC%82%B0%EB%9D%BD-vs-%EC%9D%BC%EB%B0%98%EC%A0%81%EC%9D%B8-db-%EB%9D%BD&quot; aria-label=&quot;분산락 vs 일반적인 db 락 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;분산락 vs 일반적인 DB 락&lt;/h3&gt;
&lt;p&gt;우선 일반적인 락과 분산락은 다른 개념이라고 말했듯이, &lt;strong&gt;어떤 타깃이 동시성 문제의 제어가 될것인지에 따라서 분산락 사용유무가 달라질 것입니다.&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;비즈니스 로직&lt;/code&gt;이 제어의 타깃이라면 분산락이 적합할 것이고, 반대로 엔티티에 대한 제어가 타깃이라면 DB 락으로 해결 가능할것입니다.&lt;/p&gt;
&lt;h3 id=&quot;확장성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%99%95%EC%9E%A5%EC%84%B1&quot; aria-label=&quot;확장성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;확장성&lt;/h3&gt;
&lt;p&gt;확실한건 네임드락을 활용했을때 Redis, Zookeeper 와 같은 별도의 서버 사용이 요구되지 않기 때문에, &lt;code class=&quot;language-text&quot;&gt;비용 절약&lt;/code&gt;에 있어서는 매우 좋습니다. 하지만 락에 대한 정보가 매번 MySQL 에 저장되어야 하기 떄문에 &lt;code class=&quot;language-text&quot;&gt;부하&lt;/code&gt; 가 생길 수 밖에 없고, 실제 DB 에 락으로 인한 커넥션 대기가 발생하기 때문에 성능상 단점이 있습니다.&lt;/p&gt;
&lt;p&gt;또한 DB 확장성 측면에서 좋지 못합니다. 데이터베이스에 대해 &lt;code class=&quot;language-text&quot;&gt;레플리케이션(Replication)&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;파티셔닝(Partitioning)&lt;/code&gt; 등을 진행해서 여러 데이터베이스로 확장될 경우 각 DB 별로 따로 락 정보를 보관하고 있기 때문에, 일관성의 문제가 발생할 것입니다. 이런 면에서는 확실히 Redis 메시지브로커를 활용한 분산락이 더욱이 매력적입니다.&lt;/p&gt;
&lt;p&gt;정리하자면, 추가적인 리소스 비용없이 비용 절약을 위해선 네임드 락을 활용한 방식이 좋겠지만, 확장성 측면에선 제한사항이 생기게되므로 상황에 따라 적절히 분산 락 구현 전략을 선택하는게 좋을겁니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;네임드 락 이외의 데이터베이스 락 기법들&lt;/li&gt;
&lt;li&gt;Facade 전략&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Real MySQL 8.0 (백은빈, 이성욱 지음)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/locking-functions.html&quot;&gt;https://dev.mysql.com/doc/refman/8.0/en/locking-functions.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sudal.site/namedLock/&quot;&gt;https://sudal.site/namedLock/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hyojhand/spring-java-lab/tree/main/spring-concurrency&quot;&gt;https://github.com/hyojhand/spring-java-lab/tree/main/spring-concurrency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@hyojhand/named-lock-distributed-lock-with-redis&quot;&gt;동시성 문제 해결하기2 - 네임드 락(Named Lock) &amp;#x26; Redis를 활용한 분산 락(Distributed lock)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sungsan.oopy.io/5f46d024-dfea-4d10-992b-40cef9275999&quot;&gt;분산락으로 동시성 문제 해결하기(Redis, 네임드 락)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Jenkins 의 파이프라인 기반 배포 자동화 환경 구축 구축하기]]></title><description><![CDATA[학습배경 지난 [CI/CD] Jenkins 기반 다중 Docker 컨테이너 : 애플리케이션 동시 배포 자동화하기 에서는 FreeStyle Project 에 기반한 젠킨스 기반 CI/CD…]]></description><link>https://haon.site/haon/infra/ci-cd/jenkins-pipeline/</link><guid isPermaLink="false">https://haon.site/haon/infra/ci-cd/jenkins-pipeline/</guid><pubDate>Tue, 08 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;지난 &lt;a href=&quot;https://velog.io/@msung99/CICD-Jenkins-%EA%B8%B0%EB%B0%98-%EB%8B%A4%EC%A4%91-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EB%B0%B0%ED%8F%AC-%EC%9E%90%EB%8F%99%ED%99%94&quot;&gt;[CI/CD] Jenkins 기반 다중 Docker 컨테이너 : 애플리케이션 동시 배포 자동화하기&lt;/a&gt; 에서는 FreeStyle Project 에 기반한 젠킨스 기반 CI/CD 자동화 환경을 구축했었습니다. 하지만 더 복잡한 자동화 아키텍처 환경이 요구되는 상황에서는 분명 이 방법은 한계에 부딪힐 것이기에, 더욱이 깊이있는 학습을 진행하고자 이렇게 파이프라인에 기반해서 자동화 환경을 구축하는 방법에 대해 학습을 진행하고자 합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;지난 포스팅을 어느정도 학습했다는 가정하로 진행되니, 이점 참고바랍니다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;아키텍처-구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;아키텍처 구성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;아키텍처 구성&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/606d2cce-06e2-46b9-b9c6-3cd8332f4c1d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;자동화 환경 아키텍처 구성은 복잡하지 않습니다. 이번 학습의 주목적은 &lt;code class=&quot;language-text&quot;&gt;파이프라인&lt;/code&gt;의 개념과 &lt;code class=&quot;language-text&quot;&gt;리눅스 쉘 스크립트&lt;/code&gt; 에 기반해서 애플리케이션을 구버전에서 신버전으로 어떻게 교체하는지를 중점으로 다루는데에 있습니다. 같은 이유로 Blue/Green 과 같은 무중단배포 전략은 이번 주제에서 벗어나는 내용이므로 제외했습니다. 복잡한 내용들은 추후에 다루어보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;인프라-환경-구축&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%ED%94%84%EB%9D%BC-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95&quot; aria-label=&quot;인프라 환경 구축 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인프라 환경 구축&lt;/h2&gt;
&lt;p&gt;자동화 환경을 위한 셋팅은 &lt;a href=&quot;https://velog.io/@msung99/CICD-Jenkins-Docker-%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-SpringBoot-%EB%B0%B0%ED%8F%AC-%EC%9E%90%EB%8F%99%ED%99%94-%EA%B5%AC%EC%B6%95&quot;&gt;[CI/CD] Jenkins 를 이용한 Docker 컨테이너 기반 스프링부트 애플리케이션 배포 자동화&lt;/a&gt; 에서 다룬것과 거의 동일합니다. 거의 동일한 상황에서 차이점만을 언급해보자면 다음과 같습니다.&lt;/p&gt;
&lt;h3 id=&quot;jar-파일-기반-애플리케이션-실행&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jar-%ED%8C%8C%EC%9D%BC-%EA%B8%B0%EB%B0%98-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%8B%A4%ED%96%89&quot; aria-label=&quot;jar 파일 기반 애플리케이션 실행 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;jar 파일 기반 애플리케이션 실행&lt;/h3&gt;
&lt;p&gt;우선 지난번에는 배포 서버(스프링부트 애플리케이션 서버) 가 도커 컨테이너에 기반해서 애플리케이션 프로세스가 운영되는 환경이였습니다. 반대로 이번에는 컨테이너를 사용하지 않고, jar 파일에 기반하여 애플리케이션을 실행하도록 했으며, 이에따라 스프링부트 서버에 별도의 jdk 설치가 필요합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 배포 서버 (스프링부트 서버) 에 사전 셋팅해줘야할 사항&lt;/span&gt;
$ java &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;version
$ apt install &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;jdk &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;y  &lt;span class=&quot;token comment&quot;&gt;// jdk-11 에 기반한다.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;파이프라인-vs-freestyle-project&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-vs-freestyle-project&quot; aria-label=&quot;파이프라인 vs freestyle project permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파이프라인 VS FreeStyle Project&lt;/h3&gt;
&lt;p&gt;앞서 언급했던 내용이겠디만, 이번에는 파이프라인에 기반해서 자동화 환경을 구축했습니다. 또 리눅스 쉘 스크립트를 하나 생성하고, 해당 파일을 읽어와서 배포를 자동화하는 환경을 구축했습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 1. Docker 설치&lt;/span&gt;
$ sudo apt update
$ sudo apt install apt&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;transport&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;https ca&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;certificates curl software&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;properties&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;common
$ curl &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;fsSL https&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;download&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;docker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;linux&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ubuntu&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;gpg &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; sudo apt&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;key add &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;
$ sudo add&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;apt&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;repository &lt;span class=&quot;token string&quot;&gt;&quot;deb [arch=amd64] $ https://download.docker.com/linux/ubuntu bionic stable&quot;&lt;/span&gt;
$ sudo apt update
$ apt&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;cache policy docker&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ce
$ sudo apt install docker&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ce

&lt;span class=&quot;token comment&quot;&gt;// 2. Jenkins Image Pull + Run&lt;/span&gt;
$ docker pull jenkins&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;jenkins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;lts
$ docker run &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;privileged &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;d &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token number&quot;&gt;50000&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;50000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;name jenkins jenkins&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;jenkins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;lts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;ec2-vs-linode-인스턴스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ec2-vs-linode-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4&quot; aria-label=&quot;ec2 vs linode 인스턴스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EC2 vs Linode 인스턴스&lt;/h3&gt;
&lt;p&gt;EC2 에 기반해서 인스턴스를 구축하면 좋겠으나, 아쉽게도 저는 어딘가에서 지원금이나 크래딧을 받고 학습을 진행하는게 아닙니다. 운좋게도 주변 지인에게 Linode 크래딧을 제공받은게 있어서, 이번 인스턴스는 Linode 클라우드로부터 Ubuntu 22.04 LTS 버전의 환경을 구축하게 되었습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;publish-over-ssh&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#publish-over-ssh&quot; aria-label=&quot;publish over ssh permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Publish Over SSH&lt;/h2&gt;
&lt;p&gt;젠킨스 설치를 완료했다면, 파이프라인 구성을 다루기전에 먼저 SSH 연동을 다루고자 합니다. 조금 당황스러울 수 있겠지만, 제 기준에선 SSH 연동이 잘 안되서 애먹었던 기억이 생생하기 때문이에요. &lt;a href=&quot;https://velog.io/@msung99/CICD-Jenkins-Docker-%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-SpringBoot-%EB%B0%B0%ED%8F%AC-%EC%9E%90%EB%8F%99%ED%99%94-%EA%B5%AC%EC%B6%95#dockerfile-%EC%9E%91%EC%84%B1&quot;&gt;[CI/CD] Jenkins 를 이용한 Docker 컨테이너 기반 스프링부트 애플리케이션 배포 자동화&lt;/a&gt; 에서 다루었던 내용처럼 플러그인을 설치해주고, Jenkins 관리 &gt; 시스템 설정 &gt; Publish over SSH 로 접속해줍시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/18f4b7cf-112d-492e-8049-83c867705532/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;일반적으로 사용하는 EC2 인스턴스라면 위에서 Key 란에다 본인이 EC2 인스턴스에 접속할때 필요한 pem 파일의 값을 &lt;code class=&quot;language-text&quot;&gt;cat&lt;/code&gt; 명령어로 출력해서 복붙하는 과정이 필요합니다. 하지만 저는 앞서 말했듯이 EC2 환경이 아니기때문에, Passphrase 란에다 서버 접속시 필요한 root 계정의 비밀번호를 입력해주었습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d7b62cff-93c1-4a06-97b6-38c9e92ff757/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;또 그 아래에는 ssh server 를 추가해야합니다. 각 란을 설명해보자면 다음과 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Name : 임의의 서버이름&lt;/li&gt;
&lt;li&gt;Hostname : 스프링부트 배포 서버 IP 주소. 즉 빌드된 파일을 전송할 서버의 IP 를 기입해주시면 됩니다.&lt;/li&gt;
&lt;li&gt;Username : root 계정 이름. Linode 의 경우 본인이 설정한 루트계정의 이름을 입력해주시면 되고, EC2 의 경우는 별도의 설정이 없었다면 기본값은 ubuntu 입니다.&lt;/li&gt;
&lt;li&gt;Remote Directory : 빌드된 jar 파일이 도착할 베이스 디렉토리를 적어주시면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;다 잘 추가했다면 Test Configuration 버튼을 눌러 확인해줍니다!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;파이프라인-구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;파이프라인 구성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파이프라인 구성&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/01161b63-4796-48e5-b019-d05cebfecec9/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이제부터 본격적인 파이프라인 구성을 시작해봅시다. New Item 을 클릭하여 Pipeline 을 하나 생성해줍시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/306e8c29-ef05-45a3-b380-2be8e42a0031/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;새로운 파이프라인이 생성되었다면, 깃허브로 부터 빌드를 유발받을 수 있도록 &quot;GiHub hook trigger for GITScm polling&quot; 을 체크해줍시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;파이프라인-구성단위-개념&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EA%B5%AC%EC%84%B1%EB%8B%A8%EC%9C%84-%EA%B0%9C%EB%85%90&quot; aria-label=&quot;파이프라인 구성단위 개념 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파이프라인 구성단위 개념&lt;/h2&gt;
&lt;p&gt;당연한 말이지만, 파이프라인 구성을 진행하기 위해선 파이프라인의 각 단위에 대해 개념을 이해하고 작성해야합니다. 그를 위한 설명을 간단히나마 해보자면 다음과 같습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;pipeline &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    agent any

    stages &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token char&quot;&gt;&apos;git&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            steps &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                echo &apos;&lt;span class=&quot;token class-name&quot;&gt;Hello&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;World&lt;/span&gt;&apos;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;pipeline-syntax&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pipeline-syntax&quot; aria-label=&quot;pipeline syntax permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Pipeline Syntax&lt;/h3&gt;
&lt;p&gt;파이프라인의 구성단위는 아니지만, 저희가 스크립트 작성이 맨처음에 막막하다면 저희는 젠킨스에서 지원해주는 pipeline syntax 라는 기능을 사용할 수 있습니다. 이는 추후 계속 설명할 내용이지만 파이프라인의 각 &lt;code class=&quot;language-text&quot;&gt;stage&lt;/code&gt; 에 들어갈 구성물들을 쉽게 스크립트로 자동 구성해줍니다.&lt;/p&gt;
&lt;h3 id=&quot;stages--steps&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stages--steps&quot; aria-label=&quot;stages  steps permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;stages &amp;#x26; steps&lt;/h3&gt;
&lt;p&gt;stage 는 말그대로 번역하면 &quot;단계&quot; 입니다. stage 는 각각의 Job 실행단위를 의미하며, Job 여러개를 통칭해서 stages 라고 부르는것이죠. 그리고 각 stages 단위 안에는 steps 가 들어있습니다. 각 stages 는 Job 내부의 단계를 의미하는 steps를 포함해야합니다. steps에선 실제로 실행할 쉘이나 syntax를 입력해주면 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;agent&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#agent&quot; aria-label=&quot;agent permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;agent&lt;/h3&gt;
&lt;p&gt;agent 는 해당 파이프라인 스크립트를 실행할 executor 를 지정합니다. any 로 지정할경우, 어떤 executor 도 실행할 수 있다는 의미가 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;github-clone-stage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#github-clone-stage&quot; aria-label=&quot;github clone stage permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Github Clone Stage&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6722628a-c987-488f-881d-c48ad352b437/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그리고 지금부터 대망의 파이프라인 구성을 시작해봅시다. 추후 계속 등장하겠지만, 파이프라인 구성이 수동으로 작성하는게 많이 힘들다면 &quot;Pipeline Syntax&quot; 를 클릭해서 편하게 스크립트를 작성할 수 있도록 도움을 받을 수 있으니 이점 참고해서 구성해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5544ac78-1aa0-4ec5-872d-e9811d56b229/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Pipeline Syntax 을 들어가서 스크립트 작성을 위한 소스를 마련해봅시다. 지난 포스팅을 참고하셨다면 어떤 내용들인지 충분히 이해할 수 있을거라는 생각이 듭니다. 이때 Credentials 란에서 새롭게 Credentials 을 하나 add 해주어야 하는데, 저는 이번에 아래처럼 Username with password 로 진행하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7dc56b80-dc77-4d90-9625-5967c3c5f73c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Username 에는 본인의 깃허브 아이디, Password 에는 Personal Access Token 을 하나 발급받고 이곳에 기입해줍시다. 몰론 그냥 평소에 쓰던 깃허브 비밀번호도 좋지만, 보다 보안성을 높이고자 엑세스 토큰을 활용하는게 좋다는 생각입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/f30ec3be-4f3d-4e31-8bdd-66e3bce0afab/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이렇게 다 마치고 Generate Pipeline Script 를 클릭하면 위 조건들로 구성한 스크립트 내용이 결과물로 도출됩니다. 이를 기반으로 1차적인 파이프라인 뼈대를 구성해보자면 아래와 같은 결과물이 나오게됩니다. 참고로 credentialsId 란을 보면 별도의 별칭을 부여해준 것을 볼 수 있는데, 이는 위에서 Add Credentials 를 할때 별칭을 부여가능한 것이니 참고 바랍니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt; pipeline &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        agent any

        stages &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&apos;github clone&apos;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                steps&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
             		git branch&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;main&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        credentialsId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;repo&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;and&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;hook&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;access&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;token&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;credentials&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;https&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;msung99&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CI&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CD&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Jenkins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;git&apos;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;build-stage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#build-stage&quot; aria-label=&quot;build stage permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Build Stage&lt;/h2&gt;
&lt;p&gt;다음으로 빌드 stage 를 구성해봅시다. 큰 고민없이, 아래와 같은 build stage 를 만들 수 있을겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;    pipeline &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        agent any

        stages &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&apos;github clone&apos;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                steps &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    git branch&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;main&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        credentialsId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;repo&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;and&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;hook&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;access&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;token&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;credentials&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;https&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;msung99&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CI&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CD&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Jenkins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;git&apos;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token char&quot;&gt;&apos;build&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                steps&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                     sh&apos;&apos;&apos;
                        echo build start
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;/gradlew clean bootJar
                     &apos;&apos;&apos;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 여기서 &quot;지금 빌드&quot; 버튼을 클릭해서 빌드를 시도했을때 실패하는 경우가 있을 수 있는데, 이는 서브모듈이 존재할때 실패하게됩니다. git plugin snippet은 서브모듈의 init과 update까지 지원해주진 않습니다. 이렇게 생성한 syntax가 원하는대로 동작하지 않으면 다른 step을 찾거나, 공식문서를 통해 원하는 설정이 있는지 확인해봐야 합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt; pipeline &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        agent any

        stages &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&apos;github clone&apos;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                steps&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token function&quot;&gt;checkout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;$&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;GitSCM&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        branches&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;*/main&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        extensions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;$&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;&lt;span class=&quot;token class-name&quot;&gt;SubmoduleOption&lt;/span&gt;&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            disableSubmodules&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            parentCredentials&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            recursiveSubmodules&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            reference&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            trackingSubmodules&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        userRemoteConfigs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;credentialsId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;repo&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;and&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;hook&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;access&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;token&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;credentials&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;https&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;msung99&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CI&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CD&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Jenkins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;git&apos;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token char&quot;&gt;&apos;build&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                steps&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        sh&apos;&apos;&apos;
                            echo build start
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;/gradlew clean bootJar
                        &apos;&apos;&apos;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;서브모듈을 고려한 스크립트는 위와 같습니다. 서브모듈이 private인 경우 서브모듈 레포지토리 이름과 메인 프로젝트에서 서브모듈을 포함한 디렉토리 명이 일치하지 않으면 레포지토리를 못 찾는 버그가 있었습니다. 디렉토리명을 수정하여 해결했습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;publish-on-ssh-stage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#publish-on-ssh-stage&quot; aria-label=&quot;publish on ssh stage permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;publish on ssh stage&lt;/h2&gt;
&lt;p&gt;다음으로 publish on ssh stage 를 작성해봅시다. 이를위해서 다시 pipeline syntax 를 활용할겁니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/fb1aeb9f-52a5-40de-a6aa-5120dfdca9e4/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;각 라인에 대한 설명을 해보자면 다음과 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;source files : 소스파일 (jar 파일) 의 위치입니다. gradle wrapper 를 통해 빌드한 결과물은 build/libs 에 위치하게 되므로, build/libs/*.jar 로 작성했습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;remove prefix : 말그대로 prefix (접두사) 를 제거하는 것입니다. 소스파일(jar 파일)에서 원본파일의 디렉토리를 어디까지 포함할 것인지에 대한 설정입니다. 필요하지 않으므로 디렉토리를 모두 제거합니다. 위와 같이 적은 경우, 만약 /build/libs/core-0.0.1.SNAPSHOT.jar 에 jar 파일이 존재한다면 &quot;/build/libs&quot; 라는 접두사가 제거되고 &quot;core-0.0.1.SNAPSHOT.jar&quot; 라는 것만 접두사 제거 결과물로 도출되는 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Remote directory : 앞서 초반에 빌드된 jar 파일이 도착할 베이스 디렉토리를 적어주었을텐데, 이 베이스 디렉토리에 이어서 배포될 상세 경로를 추가적으로 적어주는 것입니다.&lt;/p&gt;
&lt;p&gt;가령 위와 같이 적었을때, 베이스 리렉토리는 &quot;/home/ubuntu&quot; 이고 추가적으로 지금 적어준 세부 디렉토리는 &quot;/myproject/deploy&quot; 이므로, jar 파일이 배포될(위치하게될) 정확한 디렉토리는 &quot;/home/ubuntu/myproject/deploy&quot; 가 되는 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;당연한 말이지만, 인스턴스에서 /myproject/deploy 라는 디렉토리는 기본적으로 에당초 존재하지 않을겁니다. 따라서 &lt;code class=&quot;language-text&quot;&gt;mkdir&lt;/code&gt; 명령어를 통해 디렉토리를 새롭게 생성해주고, 이 안에 jar 파일이 유입될 수 있게 해줍시다. 또 EC2 를 사용하는 경우 기본적으로 &quot;/home/ubuntu&quot; 디렉토리는 기본적으로 존재해서 문제가 없지만, 저처럼 Linode 를 사용하는 경우 이 디렉토리는 존재하지 않으므로 이 디렉토리도 감안해서 생성해줘야합니다. 즉, &quot;/home/ubuntu/myprojct/deploy&quot; 라는 디렉토리가 생성되어 합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;exec command : 전송을 마치고 실행할 shell문의 디렉토리 및 파일 위치입니다. 바로 아래에서 보겠지만, 저희가 직접 작성한 &quot;init_server.sh&quot; 라는 쉘 스크립트 파일에 기반해서 배포가 실행되는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;파이프라인-최종-스크립트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EC%B5%9C%EC%A2%85-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8&quot; aria-label=&quot;파이프라인 최종 스크립트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파이프라인 최종 스크립트&lt;/h2&gt;
&lt;p&gt;위 과정들을 잘 따라했다면, 아래와 같은 최종적인 파이프라인이 완성됩니다. 참고로 sshPublisher 필드의 경우 verbose옵션이 있는데, 해당 옵션을 true 로 값을 부여하면 트러플 슈팅시 유용합니다. 빌드의 console output에 해당 내용이 상세하게 찍힙니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt; pipeline &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        agent any

        stages &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&apos;github clone&apos;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                steps&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token function&quot;&gt;checkout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;$&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;GitSCM&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        branches&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;*/main&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        extensions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;$&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;&lt;span class=&quot;token class-name&quot;&gt;SubmoduleOption&lt;/span&gt;&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            disableSubmodules&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            parentCredentials&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            recursiveSubmodules&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            reference&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            trackingSubmodules&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        userRemoteConfigs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;credentialsId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;repo&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;and&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;hook&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;access&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;token&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;credentials&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;https&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;msung99&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CI&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CD&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Jenkins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;git&apos;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token char&quot;&gt;&apos;build&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                steps&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        sh&apos;&apos;&apos;
                            echo build start
                            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;/gradlew clean bootJar
                        &apos;&apos;&apos;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&apos;publish on ssh&apos;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                steps&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;sshPublisher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                            publishers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                                    &lt;span class=&quot;token function&quot;&gt;sshPublisherDesc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                                        configName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;msung99&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                        transfers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
                                            &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                                                &lt;span class=&quot;token function&quot;&gt;sshTransfer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                                                    cleanRemote&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                                    excludes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                                    execCommand&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;sh &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;home&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ubuntu&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;myproject&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;deploy&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;init_server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sh&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                                    execTimeout&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;120000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                                    flatten&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                                    makeEmptyDirs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                                    noDefaultExcludes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                                    patternSeparator&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;[, ]+&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                                    remoteDirectory&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;myproject&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;deploy&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                                    remoteDirectorySDF&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                                    removePrefix&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;build&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;libs&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                                    sourceFiles&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;build&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;libs&lt;span class=&quot;token comment&quot;&gt;/*.jar&apos;)],
                                                    usePromotionTimestamp: false,
                                                    useWorkspaceInPromotion: false,
                                                    verbose: true
                                                )
                                            ]
                                        )
                }
            }
        }
    }&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;리눅스-쉘-스크립트-작성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%89%98-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%91%EC%84%B1&quot; aria-label=&quot;리눅스 쉘 스크립트 작성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리눅스 쉘 스크립트 작성&lt;/h2&gt;
&lt;p&gt;앞서 계속 언급했던 쉘 스크립트 파일을 작성해봅시다. 그 내용은 아래와 같이 작성해줬습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;#&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; bin&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;bash

&lt;span class=&quot;token constant&quot;&gt;CURRENT_PID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pgrep &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f core&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SNAPSHOT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jar &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; head &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;n &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;z &lt;span class=&quot;token string&quot;&gt;&quot;$CURRENT_PID&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; then
        echo &lt;span class=&quot;token string&quot;&gt;&quot;구동중인 애플리케이션이 없으므로 종료하지 않습니다.&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
        echo &lt;span class=&quot;token string&quot;&gt;&quot;구동중인 애플리케이션을 종료했습니다. (pid: $CURRENT_PID)&quot;&lt;/span&gt;
        kill &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt; $&lt;span class=&quot;token constant&quot;&gt;CURRENT_PID&lt;/span&gt;
fi

sudo &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;E&lt;/span&gt; nohup java &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;jar &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;home&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ubuntu&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;myproject&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;deploy&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;core&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SNAPSHOT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jar &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;우선 CURRENT_PID 변수에 현재 실행중인 스프링부트 애플리케이션 프로세스의 PID 값을 얻어오게 됩니다. 만약 PID 값이 0 이라면 현재 실행중인 프로세스가 없다는 것이므로 별도의 처리없이 바로 nohup 명령어를 통해 jar 파일을 실행하는 것입니다.&lt;/p&gt;
&lt;p&gt;반대로 PID 값이 0이 아니라면 현재 이미 실행되고 있는 스프링부트 애플리케이션 프로세스가 존재한다는 것이므로, 헤당 구버전 프로세스를 종료시키고, 현재 새롭게 유입된 jar 파일에 기반하여 신버전으로 새롭게 프로세스를 실행시킵니다.&lt;/p&gt;
&lt;h3 id=&quot;jar-파일-경로&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jar-%ED%8C%8C%EC%9D%BC-%EA%B2%BD%EB%A1%9C&quot; aria-label=&quot;jar 파일 경로 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;jar 파일 경로&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c1a4429c-5a8d-40de-8b88-ec3cb14e0acc/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;지금껏 문제없이 따라왔다면 jar 파일은 /home/ubuntu/myprojecyt/deploy 에 위치하게 될것입니다. 또한 init_server.sh 쉘 스크립트 파일도 이 경로에 생성했으니, 이 경로에 위치해있는 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;실행결과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%ED%96%89%EA%B2%B0%EA%B3%BC&quot; aria-label=&quot;실행결과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실행결과&lt;/h2&gt;
&lt;p&gt;Console Output 을 조회해보면, 정상적으로 실행한 경우 아래와 같은 결과를 조회할 수 있게 됩니다!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0b46f5d1-d416-4281-98e1-9f0eaf563341/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;또 기존에 실행중인 프로세스를 종료하고 구버전 -&gt; 신버전으로 교체되는 case 는 아래와 같은 결과를 조회할 수 있게됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/52b414e6-ac34-4b49-9f61-58e0a764ff02/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;webhook&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webhook&quot; aria-label=&quot;webhook permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;WebHook&lt;/h2&gt;
&lt;p&gt;추가적으로 Jenkins 내부에서 직접 &quot;지금 빌드&quot; 버튼을 클릭해서 배포를 자동화하는 것이 아니라, main 브랜치에 PR 내용이 병합된 경우 배포가 자동화되게 하려면 WebHook 연동이 필요할겁니다. 이 내용은 &lt;a href=&quot;https://velog.io/@msung99/CICD-Jenkins-Docker-%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-SpringBoot-%EB%B0%B0%ED%8F%AC-%EC%9E%90%EB%8F%99%ED%99%94-%EA%B5%AC%EC%B6%95#dockerfile-%EC%9E%91%EC%84%B1&quot;&gt;[CI/CD] Jenkins 를 이용한 Docker 컨테이너 기반 스프링부트 애플리케이션 배포 자동화&lt;/a&gt; 에서도 자세히 다룬 내용이므로, 이 내용은 생략하겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;FreeStyle Project 로만 CI/CD 자동화 환경을 구축하다가 직접 파이프라인을 구축해보니 정말 어려웠던 것 같습니다. 다음번에는 Nginx 를 활용해서 Blue/Green 을 직접 구축해보는 경험을 진행해봐야겠다는 아이디어가 떠오르네요! 긴글 읽어주셔서 감사합니다 😁&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://sihyung92.oopy.io/e5300d92-1a4e-40f4-b927-a93b2bbb17d2&quot;&gt;젠킨스 파이프라인을 이용한 배포 자동화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dallog.github.io/continuous-deploy-with-jenkins-1-backend/&quot;&gt;젠킨스를 사용한 달록팀의 지속적 배포 환경 구축기 (1) - 백엔드편&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://leehosu.github.io/jenkins_ssh&quot;&gt;🏗️ Jenkins를 이용한 SSH 통신을 통해 AWS EC2 서버 배포 파이프라인 구성하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://parkhyeokjin.github.io/devops/2020/10/14/JekinsSshConfigure.html&quot;&gt;젠킨스 서버 SSH Key 생성 &amp;#x26; 등록&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[무중단 배포 아키텍처의 다양한 배포전략 (Blue/Green, Rolling, Canary)]]></title><description><![CDATA[애플리케이션의 종료시점 : 서버 1대로 운영할때  서비스를 운영하다보면 새로운 버전을 출시하고 해야할 상황이 발생합니다. 그런데 만약 1대로 운영할때 사용자들이 도중에 중단되는 일 없이 운영이 가능할까요? 현재 서버에는 ver…]]></description><link>https://haon.site/haon/infra/ci-cd/무중단배포/</link><guid isPermaLink="false">https://haon.site/haon/infra/ci-cd/무중단배포/</guid><pubDate>Mon, 07 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;애플리케이션의-종료시점--서버-1대로-운영할때&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%98-%EC%A2%85%EB%A3%8C%EC%8B%9C%EC%A0%90--%EC%84%9C%EB%B2%84-1%EB%8C%80%EB%A1%9C-%EC%9A%B4%EC%98%81%ED%95%A0%EB%95%8C&quot; aria-label=&quot;애플리케이션의 종료시점  서버 1대로 운영할때 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;애플리케이션의 종료시점 : 서버 1대로 운영할때&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/23b1930d-f6bd-4a22-a105-b0e04a51a6b1/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;서비스를 운영하다보면 새로운 버전을 출시하고 해야할 상황이 발생합니다. 그런데 만약 1대로 운영할때 사용자들이 도중에 중단되는 일 없이 운영이 가능할까요?&lt;/p&gt;
&lt;p&gt;현재 서버에는 ver1 버전을 운영하고 있는 상황이고, ver2 버전을 새롭게 배포하고 싶은 상황이라고 가정해봅시다. 앞으로 사용자들은 ver2 를 사용할 수 있도록 배포해야겠죠?&lt;/p&gt;
&lt;h3 id=&quot;서버-1대를-운영시-발생하는-문제점--무중단-배포가-불가능하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B2%84-1%EB%8C%80%EB%A5%BC-%EC%9A%B4%EC%98%81%EC%8B%9C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EB%AC%B8%EC%A0%9C%EC%A0%90--%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC%EA%B0%80-%EB%B6%88%EA%B0%80%EB%8A%A5%ED%95%98%EB%8B%A4&quot; aria-label=&quot;서버 1대를 운영시 발생하는 문제점  무중단 배포가 불가능하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서버 1대를 운영시 발생하는 문제점 : 무중단 배포가 불가능하다.&lt;/h3&gt;
&lt;p&gt;배포를 하려면 우선 새롭게 만든 ver2 버전을 다운로드해야합니다. 문제는 ver1 과 ver2 모두 동일한 포트를 사용하기 때문에 ver2 를 빌드하고 실행하기전에 현재 운영중인 ver1 버전은 잠시 서버를 꺼놓아야합니다.&lt;/p&gt;
&lt;p&gt;결국 서비스 사용자는 ver1 를 종료시킨후, ver2 를 빌드하고 다시 서비스를 배포하기전까지 온전히 이용할 수 없을겁니다. ver2 가 배포 완료된 후에나 다시 정상적인 이용이 가능해지겠죠.&lt;/p&gt;
&lt;p&gt;여기서 문제는 &lt;strong&gt;서버 1대로만 서비스를 운영하면, 기존 버전으로 돌아가던 서버는 잠시 종료되고, 다음 버전을 다시 배포하기 전까지 사용자를 서비스를 이용할 수 없게됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;다운타임(Downtime) : 이렇게 서비스가 잠시 중단되어야 하는 시간대를 다운타임이라고 부릅니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;ver1-서비스를-꼭-중단시켜야-ver2-를-실행시킬-수-있는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ver1-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%A5%BC-%EA%BC%AD-%EC%A4%91%EB%8B%A8%EC%8B%9C%EC%BC%9C%EC%95%BC-ver2-%EB%A5%BC-%EC%8B%A4%ED%96%89%EC%8B%9C%ED%82%AC-%EC%88%98-%EC%9E%88%EB%8A%94%EA%B0%80&quot; aria-label=&quot;ver1 서비스를 꼭 중단시켜야 ver2 를 실행시킬 수 있는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ver1 서비스를 꼭 중단시켜야 ver2 를 실행시킬 수 있는가?&lt;/h3&gt;
&lt;p&gt;동일한 포트를 사용한다고 하면 당연한 말입니다.&lt;/p&gt;
&lt;p&gt;예를들어 Docker 컨테이너를 띄울때, &quot;docker run -p 8080:8080 이미지명&quot; 을 입력할겁니다. 즉 8080 포트를 특정 컨테이너가 사용중일때 다른 컨테이너를 8080포트를 중복으로 사용할 수는 없을겁니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;서버를-2대로-늘린다면--로드밸런싱이-필요한-이유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B2%84%EB%A5%BC-2%EB%8C%80%EB%A1%9C-%EB%8A%98%EB%A6%B0%EB%8B%A4%EB%A9%B4--%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B4%EC%9C%A0&quot; aria-label=&quot;서버를 2대로 늘린다면  로드밸런싱이 필요한 이유 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서버를 2대로 늘린다면? : 로드밸런싱이 필요한 이유&lt;/h2&gt;
&lt;p&gt;그렇다면 서버를 2대 이상으로 늘려서 확장시키면 다운타임(DownTime) 시간대에 대한 문제가 해결 되는걸까요? 정확히 말하자면, 무작정 서버를 늘린다고 해결되는 것은 아닙니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/08046d63-96b6-429c-92f0-b66611dd5d3c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그냥 무작정 서버를 늘릴경우, 다음과 같은 문제점이 발생하빈다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;사용자가 모든 서버의 IP 를 알고있어야지 리소스를 요청 가능합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;만일 사용자가 어떻게 해서라도 서버 IP 들을 알아낸다고 한들, 어떤 서버가 현재 운영중인 버전에 대한 서버의 IP 인지 알 수 없습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;따라서 저희는 로드밸런싱을 진행해서, 클라이언트와 서버 사이에서 중계해줄 Reverse Proxy 서버를 별도로 배치해주어야 합니다. 한 서버에 요청이 들어오면, 그 서버의 뒷단에 있는 여러 서버중에서 적절한 특정 서버에다 요청을 분산해주는 것이죠.&lt;/p&gt;
&lt;p&gt;만일 로드밸런싱와 Proxy 에 대해 아직 잘 모르시는 분들은 제 지난 포스팅을 참고하시면 좋을듯합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@msung99/%ED%94%84%EB%A1%9D%EC%8B%9C-%EC%84%9C%EB%B2%84Forward-Proxy-Reverse-Proxy-%EC%99%80-Load-Balancer-%EB%9E%80&quot;&gt;Proxy Server와 로드밸런싱, 수평확장(Scale Out)과 수직확장(Scale Up)에 대해&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;무중단-배포&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC&quot; aria-label=&quot;무중단 배포 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;무중단 배포&lt;/h2&gt;
&lt;p&gt;무중단 배포는 &lt;strong&gt;서비스가 중단되지 않는 상태로 새로운 버전을 사용자에게 계속해서 배포하는 것&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4476bf92-d893-440e-97e6-f553dd75e713/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;무중단 배포를 진행하려면 서버를 2대 이상 확보해야합니다.&lt;/strong&gt; 그러고 Nginx 와 같은 웹서버를 앞단에 배치시켜서 한 곳에서 모든 클라이언트들의 요청을 받도록 해야합니다. (Reverse Proxy 서버 앞단에 배치)&lt;/p&gt;
&lt;p&gt;그러고 뒷단에 존재하는 여러 서버들에게 상황에 따라 적절히 요청을 분배시키는 방식인 것이죠. (로드밸런싱 진행)&lt;/p&gt;
&lt;p&gt;지금부터 무중단 배포 전략을 사용하기 위한 다양한 배포방식을 알아보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;롤링rolling&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%A4%EB%A7%81rolling&quot; aria-label=&quot;롤링rolling permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;롤링(Rolling)&lt;/h2&gt;
&lt;p&gt;일반적인 배포 방식을 의미하며, 단순하게 서버를 구성하여 배포하는 전략입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;다시말해 구 버전에서 신 버전으로 &lt;strong&gt;점진적으로 서버 하나씩 차근차근 전환하는 배포입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;예를들어 다음처럼 운영중인 ver1 버전의 서버 3대가 있을때, 모두 ver2 버전으로 바꿔야하는 경우입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/879a249f-eae3-4f53-9f9a-6d6cc24eb14c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;점진적으로 서버 하나씩 버전을 바꾼다고 했었죠? 과정을 나열해보면 다음과 가습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;우선 서버1에 클라이언트들이 요청을 못하도록 로드밸런서와 연결을 끊습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d88220d1-9647-4cbc-89d5-236edb56c74b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;서버1의 버전을 ver2 로 업데이트 합니다. 이떄 서버1은 잠시 중단된 상태일텐데, 클라이언트의 요청들은 서버 2와 3에서 수용하고 있는 상황입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7657b76c-594e-4653-9791-d037d3599ac8/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;서버1이 ver2 버전으로 바뀌었다면 다시 로드밸런서와 연결을 맺어줍니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/253f1cc1-acd9-4cdb-b1ef-4a519f6a8f95/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;이 과정을 서버2 와 3에 대해서도 동일하게 순차적으로 진행해줍니다. 이렇게 서버 하나씩 점차적으로 진행나가는 방식이 Rolling 배포전략입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/594158f3-7bdf-4f39-868c-4db98022a768/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;장점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90&quot; aria-label=&quot;장점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;많은 서버 자원을 확보하지 않아도 무중단 배포가 가능합니다.&lt;/li&gt;
&lt;li&gt;점진적으로 새로운 버전이 사용자에게 출시되므로 안정적인 배포가 가능해집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;단점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90&quot; aria-label=&quot;단점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;배포 중 서버 인스턴스의 수가 감소되므로, &lt;strong&gt;다른 서버가 대신하여 감당해야할 트래픽의 양이 늘어납니다.&lt;/strong&gt; 따라서 각각의 서버가 처리해야할 용량을 미리 고려하는 것이 좋습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;구버전과 신버전의 애플리케이션이 동시에 서비스되기 떄문에 &lt;strong&gt;호환성 문제가 발생&lt;/strong&gt;할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;블루-그린blue-green&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%94%EB%A3%A8-%EA%B7%B8%EB%A6%B0blue-green&quot; aria-label=&quot;블루 그린blue green permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;블루 그린(Blue Green)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;현재 운영중인 구 버전 서버를 블루&lt;/strong&gt;, &lt;strong&gt;신 버전 서버를 그린&lt;/strong&gt;이라고 부릅니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;신버전에 대한 새로운 인스턴스들을 생성 및 배포해서, 로드밸런서를 구버전과 신버전 모두와 일제히 연결시킵니다. 그러고 기존 구버전을 종료시키고 로드밸런서가 신버전만을 바라보게 하는 전략입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;다음과 같이 로드밸런서가 구버전 서버1,2,3 과 연결된 상태라고 해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/888c0f23-6684-4af7-8f0c-7e2a205afa3a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;새롭게 신버전에 대한 서버 인스턴스 3개를 배포하고, 로드밸런서가 신버전 서버와도 동시에 연결됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d9974b31-b2cf-4565-b5e5-845b1f74a3c0/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;마지막으로 기존에 연결된 구버전 서버를 종료시킨후, 연결을 끊습니다. 결과적으로 로드밸런서는 신버전 서버들과 연결된 상태가됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/f0e8e348-4743-4a02-ace0-b5b3f3250d5f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;장점-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90-1&quot; aria-label=&quot;장점 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;롤링 배포전략과 달리 한번에 트래픽을 신버전으로 옮기기 떄문에 호환성 문제가 발생하지 않습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;빠른 롤백이 가능하고, 운영환경에 영향을 주지않고 실제 서비스 환경으로 신 버전 테스트가 가능합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;단점-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90-1&quot; aria-label=&quot;단점 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;시스템 자원이 2배로 필요하게되어, 비용이 훨씬 발생합니다. 즉, 실제 운영에 필요한 서버 리소스에 대비해 2배의 리소스를 확보해야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;카나리canary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B9%B4%EB%82%98%EB%A6%ACcanary&quot; aria-label=&quot;카나리canary permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;카나리(Canary)&lt;/h2&gt;
&lt;p&gt;카나리 배포는 위험을 빠르게 감지할 수 있는 배포전략입니다. &lt;strong&gt;지정한 서버 또는 특정 User 에게만 배포해서 서비스를 운영하다가, 버그가 없고 정상적이라고 판단되면 전체에게 배포하는 방식입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;즉, 서버의 트래픽을 일부를 신 버전으로 분산하여 오류 여부를 확인해보는 전략입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d561c87a-2cfe-4ce6-99e1-942b386c2fbb/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이떄 트래픽을 새로운 버전으로 옮기는 기준은 랜덤으로 진행하거나, 또는 팀원들과 정한 규칙(ex. Admin 유저만 신버전으로 배포해보자) 에 따라서 진행하면됩니다.&lt;/p&gt;
&lt;p&gt;또한 이런 배포전략으로 A/B 테스트도 가능합니다.&lt;/p&gt;
&lt;h3 id=&quot;장점-2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90-2&quot; aria-label=&quot;장점 2 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;트래픽 비율을 조정해서 버그 테스트를 안정적으로 진행가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;단점-2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90-2&quot; aria-label=&quot;단점 2 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;롤링 배포전략과 마찬가지로 호환성 문제가 발생할 수 있습니다. 일부는 신버전, 다른 사람들은 신버전을 사용하기 때문이죠.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금까지 무중단 배포의 다양한 전략에 대해서 알아봤습니다. 만약 본인이 진행중인 아키텍쳐에 무중단 배포를 도입한다면, 위와 같은 다양한 배포전략중에서 상황에 맞게 적합한 것을 선택하시면 좋을것 같습니다 🙂&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@znftm97/%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC%EB%A5%BC-%EC%9C%84%ED%95%9C-%ED%99%98%EA%B2%BD-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0#-span-stylecolor0b6e99%EB%AA%A9%ED%91%9Cspan&quot;&gt;무중단 배포란? 무중단 배포를 위한 환경 이해하기&lt;/a&gt;
&lt;a href=&quot;https://www.samsungsds.com/kr/insights/1256264_4627.html&quot;&gt;Samsung SDS : 무중단 배포 아키텍처(Zero Downtime Deployment)- 글로벌 서비스 운영의 필수 요소&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Slf4j MDC(Mapped Diagnostic Context) 로 요청별로 식별 가능한 맥락 남기기]]></title><description><![CDATA[…]]></description><link>https://haon.site/spring/slf4j-mapped-diagnotics-context/</link><guid isPermaLink="false">https://haon.site/spring/slf4j-mapped-diagnotics-context/</guid><pubDate>Sat, 05 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;로그가-뒤섞이는-문제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EA%B7%B8%EA%B0%80-%EB%92%A4%EC%84%9E%EC%9D%B4%EB%8A%94-%EB%AC%B8%EC%A0%9C&quot; aria-label=&quot;로그가 뒤섞이는 문제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로그가 뒤섞이는 문제&lt;/h2&gt;
&lt;p&gt;서비스를 운영하다보면 로그를 통해 모니터링을 하고, 발생한 애러에 대응하기 위해 로그를 추적하고, 기록을 남긴다. 이때 문제점은 멅티 쓰레드 환경에서 동시 요청이 다량 유입되는 경우 로그가 뒤죽박죽 섞이고 쌓인다는 점이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ed772eac7a45e32f154905218981a887/1320e/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.12883435582822%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABc0lEQVR42j2R227bMBBE/TeFG1s3UhfeKUqWHTkxmgZ9bP7/L043fujDwQDEzOxieTjnSFkzq3DdCkMI9JOnTJnFLuQpsboL2STmObCJx5ie6vyTqn5hEH1UR67nI1k4uF+e/SPw9jvw+IzMDynb5W2PT26vnvs9sd8j949vrydvHXGthYbLH8X1S/P61XP9qzic90x+m1neC9tjQW+JoSRKKcxFNhQty4V1Xyniy3si3gLh6vGbI90D+T1y+Vywt5HDcbRY4/DWE72n1oa2Nfgu4LqIbQNeJYKLmMmI1+Ksw0nGGPNUNzlynGmrTgqtFDpHkNulGGhGR6MkqBxWB4yo1xFvAtZKgXeM44hSCqUVWmtBMU6j3LSWQh+IKZGzfMIsUyTYak/qM3EsBNE8rET5pBjj0zcMPbWEm6b5T9d2VFXF4YfSMqlH94MYB15azamWqdWAqkc6UV1PaCUqPqV6GgnXdSMbNdTNNy2dbHw6nfkHq0Hr/9r5MEUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/ed772eac7a45e32f154905218981a887/a6d36/image.png&quot;
        srcset=&quot;/static/ed772eac7a45e32f154905218981a887/222b7/image.png 163w,
/static/ed772eac7a45e32f154905218981a887/ff46a/image.png 325w,
/static/ed772eac7a45e32f154905218981a887/a6d36/image.png 650w,
/static/ed772eac7a45e32f154905218981a887/e548f/image.png 975w,
/static/ed772eac7a45e32f154905218981a887/1320e/image.png 1292w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위 로그는 파악하기 쉬운가? 로그를 확인해보면 1개의 요청에 대한 로그가 순차적으로 나열되는 것이 아니라, 여러 사용자의 요청에 대한 로그가 쌓이고 섞여서 파악하기가 힘들다. &lt;strong&gt;즉, 로그에 맥락이 부재된 상황이라면 문제가 발생했을 떄 원인을 추적하고 진단하기가 힘들다.&lt;/strong&gt; 우리는 각 요청별로 식별 가능한 맥락을 남겨아만 로그를 추적하기 용이할 것이다.&lt;/p&gt;
&lt;h3 id=&quot;맥락context-남기기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%A5%EB%9D%BDcontext-%EB%82%A8%EA%B8%B0%EA%B8%B0&quot; aria-label=&quot;맥락context 남기기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;맥락(Context) 남기기&lt;/h3&gt;
&lt;p&gt;요청별로 식별 가능한 맥락(Context) 의 종류로는 요청을 날린 현재 사용자 고유 정보등 여러가지가 존재하는데, 그 중에 &lt;strong&gt;Correlation ID&lt;/strong&gt; 라는 정보를 맥락으로 남기고자 한다.  &lt;strong&gt;Correlation ID 란 단일 요청에 대해 무작위하게 생성된 고유한 ID 를 뜻한다.&lt;/strong&gt; Correlation ID 에 대한 대표적인 사례가 바로 UUID 가 될 것이다. 우리는 UUID 를 맥락으로 남겨서, 문제가 있는 요청들을 UUID 별로 그룹화할 수 있도록 만들 것이다.&lt;/p&gt;
&lt;h3 id=&quot;쓰레드-로컬threadlocal&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%93%B0%EB%A0%88%EB%93%9C-%EB%A1%9C%EC%BB%ACthreadlocal&quot; aria-label=&quot;쓰레드 로컬threadlocal permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쓰레드 로컬(ThreadLocal)&lt;/h3&gt;
&lt;p&gt;이러한 Correlation ID 는 어떻게 로그와 함께 맥락으로 남길 수 있을까? 가장 무식한 방법으로는 로그를 남겨야할 모든 메소드내에 Correlation ID 를 파라미터로 전달하여 로그를 남기도록 하는 방법이 있을 것이다. 하지만, 이는 모든 메소드에 로깅을 위한 코드가 남발되고 실수를 범할 여지가 크기에 그다지 좋은 방법이 아니다.&lt;/p&gt;
&lt;p&gt;우리는 각 요청별로 고유한 ID 를 저장할 특정 저장소를 활용하는 방법을 떠올릴 수 있다. 이 공간이 바로 쓰레드 로컬이다. &lt;strong&gt;각 요청별로(쓰레드별로) Correlation ID 를 쓰레드 로컬에 저장하고, 로깅할 떄 마다 Correclation ID 를 꺼내어 함께 로깅하면 될 것이다.&lt;/strong&gt;  MDC 는 이 쓰레드 로컬을 사용하여 요청별로 맥락을 관리한다.&lt;/p&gt;
&lt;h2 id=&quot;mdc-mapped-diagnostics-context&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mdc-mapped-diagnostics-context&quot; aria-label=&quot;mdc mapped diagnostics context permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MDC (Mapped Diagnostics Context)&lt;/h2&gt;
&lt;p&gt;Slf4j 를 사용하여 &lt;strong&gt;MDC(Mapped Diagnostics Context)&lt;/strong&gt; 라는 기능을 제공받을 수 있다. MDC 는 현재 실행중인 쓰레드에 특정 클라이언트에 대한 고유한 데이터 (Correlation ID 등) 를 넣고 관리하는 공간이다. MDC 는 각 쓰레듭ㄹ로 맥락을 관리하기 위해, 내부적으로 쓰레드 로컬을 사용하여 구현되어 있다. Map 형태 (key-value 데이터)로 맥락(Context) 를 저장한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;MDC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;key1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;value1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 저장&lt;/span&gt;
&lt;span class=&quot;token constant&quot;&gt;MDC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;key1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 조회&lt;/span&gt;
&lt;span class=&quot;token constant&quot;&gt;MDC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 초기화&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;쓰레드 로컬은 오직 하나의 쓰레드에 의해서 읽고 쓰여질 수 있는 고유한 저장소다. 두 쓰레드가 같은 코드를 실행하고 이 코드가 하나의 쓰레드 로컬 변수를 참조한다고 한들, 서로가 같은 쓰레드 로컬을 참조할 수 없다. 따라서 MDC 쓰레드 로컬을 사용함으로써, 멀티 쓰레드 환경에서 각 쓰레드 환경에서 독립적인 접근 데이터를 관리핧 수 있게된다.&lt;/p&gt;
&lt;h2 id=&quot;mdc-로-로깅하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mdc-%EB%A1%9C-%EB%A1%9C%EA%B9%85%ED%95%98%EA%B8%B0&quot; aria-label=&quot;mdc 로 로깅하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MDC 로 로깅하기&lt;/h2&gt;
&lt;p&gt;MDC 를 사용하여 각 요청별로 고유 ID (Correlation ID) 를 할당하고, 로그를 출력해보자. MDC 를 사용하여 로깅하는 방법에는 &lt;strong&gt;인터셉터(Interceptor), 필터(Filter), AOP&lt;/strong&gt; 등 여러 방법이 존재하지만, 이번 포스팅에선 필터를 사용하여 로깅하는 방법을 학습해보도록 한다. 참고로 MDC 는 Slf4j 구현체 중 Log4j 와 Logback 2가지만 지원하고 있다. 이 중에선 Logback 을 사용하도록 한다.&lt;/p&gt;
&lt;h3 id=&quot;servlet-filter-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#servlet-filter-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;servlet filter 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Servlet Filter 구현&lt;/h3&gt;
&lt;p&gt;구현 방법은 어렵지 않다. &lt;code class=&quot;language-text&quot;&gt;Filter&lt;/code&gt; 를 구현한 MDCFilter 라는 구현체를 만들고 Bean 으로 등록해주면 된다. 이후 &lt;code class=&quot;language-text&quot;&gt;doFilter()&lt;/code&gt; 에서 &lt;code class=&quot;language-text&quot;&gt;MDC.put()&lt;/code&gt; 을 사용하여 랜덤으로 생성된 고유 UUID 를 넣어주면 된다. 이렇게 되면 서블릿이 요청을 받을 때 마다, 요청이 서블릿 &lt;code class=&quot;language-text&quot;&gt;MDCFilter&lt;/code&gt; 를 거치게 되고, 항상 MDC 의 &lt;code class=&quot;language-text&quot;&gt;REQUEST_ID&lt;/code&gt; 라는 Key에 대한 Value 로 UUID 가 저장된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Ordered&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HIGHEST_PRECEDENCE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MdcFilter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;doFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletRequest&lt;/span&gt; servletRequest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                         &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletResponse&lt;/span&gt; servletResponse&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                         &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FilterChain&lt;/span&gt; filterChain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;setMdc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; servletRequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        filterChain&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;doFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servletRequest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; servletResponse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;MDC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setMdc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;MDC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;REQUEST_ID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;randomUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이때 유의할 점은, 요청이 완료되면 항상 &lt;code class=&quot;language-text&quot;&gt;MDC.clear()&lt;/code&gt; 를 통해 쓰레드를 반납해야한다. Spring MVC 는 쓰레드 풀에 쓰레드를 생성하여, 요청이 오면 쓰레드를 할당해 처리하고 반납한다. 그러나 MDC 는 쓰레드 별로 저장되는 쓰레드 로컬을 사용하고, 요청이 완료될 때 &lt;code class=&quot;language-text&quot;&gt;MDC.clear()&lt;/code&gt; 를 하지 않으면 다른 요청이 해당 쓰레드를 재사용할 때 이전에 남긴 UUID 데이터가 쓰레드 로컬에 남아있을 수 있다. 이 떄문에 동일한 UUID 가 남아있을 수 있게되니 주의하자.&lt;/p&gt;
&lt;h3 id=&quot;로그에-correlation-id-함께-출력하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EA%B7%B8%EC%97%90-correlation-id-%ED%95%A8%EA%BB%98-%EC%B6%9C%EB%A0%A5%ED%95%98%EA%B8%B0&quot; aria-label=&quot;로그에 correlation id 함께 출력하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로그에 Correlation ID 함께 출력하기&lt;/h3&gt;
&lt;p&gt;Filter 를 구현했다면 &lt;code class=&quot;language-text&quot;&gt;MDC.get()&lt;/code&gt; 을 사용하여 Correlation ID 와 함께 로그를 출력해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;warn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;[{}] 요청 수행중입니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MDC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;REQUEST_ID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 로그 메시지에 Correlation ID 를 포함시키는 것은 몰론, Logback 설정을 통해 모든 로그에 Correlation ID 가 함께 기록되도록 해보자. &lt;code class=&quot;language-text&quot;&gt;logback-spring.xml&lt;/code&gt; 을 아래처럼 작성해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;appender&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;FILE-WARN&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ch.qos.logback.core.rolling.RollingFileAppender&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;./log/warn/${BY_DATE}.log&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;filter&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ch.qos.logback.classic.filter.LevelFilter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;level&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;WARN&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;level&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;onMatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;ACCEPT&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;onMatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;onMismatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;DENY&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;onMismatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;encoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Pattern&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            %msg%n
            [REQUEST_ID] : [%X{REQUEST_ID:-NO REQUEST ID}]
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Pattern&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;charset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;utf8&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;charset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;encoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;rollingPolicy&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;fileNamePattern&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;./log/backup/warn/%d{yyyy-MM-dd}.%i.log&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;fileNamePattern&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;maxFileSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;100MB&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;maxFileSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;maxHistory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;10&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;maxHistory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;totalSizeCap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;1GB&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;totalSizeCap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;rollingPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;appender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Logback Pattern 설정에서 &lt;code class=&quot;language-text&quot;&gt;%msg&lt;/code&gt; 을 통해 Logger 로 출력할 로그를 출력하고, &lt;code class=&quot;language-text&quot;&gt;%X{MDC_키:-텍스트}&lt;/code&gt; 와 같은 형태로 MDC 에 저장된 값을 꺼내와서 로그로 기록할 수 있다. 이때 텍스트에 MDC 값을 찾을 수 없을 때 대체하여 대신 출력할 내용을 설정할 수 있다. 따라서 위의 경우 &lt;code class=&quot;language-text&quot;&gt;REQUEST_ID&lt;/code&gt; 라는 key 값을 지닌 데이터를 MDC 찾으면 그대로 출력하고, 찾지 못하면 NO REQUEST ID 텍스트를 출력한다.&lt;/p&gt;
&lt;h3 id=&quot;로그-출력-결과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EA%B7%B8-%EC%B6%9C%EB%A0%A5-%EA%B2%B0%EA%B3%BC&quot; aria-label=&quot;로그 출력 결과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로그 출력 결과&lt;/h3&gt;
&lt;p&gt;정상적으로 수행을 마쳤다면, 로그는 아래와 같은 형태로 출력될 것이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/40d35864209704c0951cc5b595e4b175/e28fd/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 2.4539877300613497%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAABCAYAAADeko4lAAAACXBIWXMAAAsTAAALEwEAmpwYAAAARElEQVR42iWKwQ2AMAwD2QPoIxKVGpykwP6zuWl5nO4kexOphPVFU6PHyxvBmr0reEwaVp/pkluB/5aLmr/oH80fKoIDliwdpdra0Y8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/40d35864209704c0951cc5b595e4b175/a6d36/image-4.png&quot;
        srcset=&quot;/static/40d35864209704c0951cc5b595e4b175/222b7/image-4.png 163w,
/static/40d35864209704c0951cc5b595e4b175/ff46a/image-4.png 325w,
/static/40d35864209704c0951cc5b595e4b175/a6d36/image-4.png 650w,
/static/40d35864209704c0951cc5b595e4b175/e548f/image-4.png 975w,
/static/40d35864209704c0951cc5b595e4b175/3c492/image-4.png 1300w,
/static/40d35864209704c0951cc5b595e4b175/e28fd/image-4.png 2650w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;또한 Logback 을 통해 기록된 로그는 아래와 같이 출력될 것이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9f4edda7299c2f1a05e6053af64a94f7/b490f/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 14.11042944785276%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlElEQVR42lWOWQrDMBBDc5CsrtfxxEvihkLvfy7VNl3oxxsYIQkNxV/IdOKgguQOBJMqGdHmqhec/o6dEjgkxFxwPZ6wxBjn9Y9p3jAvAkM77RmnFUJqeI4w1kMbwiYUnGY4xbDSd4ykL00nvXeUdL/CT6lUFjGdcBRgHUPcNEJdutfVLeTf4Y5qMFiH6kkwijAtG16qMVfjlxvZBQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/9f4edda7299c2f1a05e6053af64a94f7/a6d36/image-2.png&quot;
        srcset=&quot;/static/9f4edda7299c2f1a05e6053af64a94f7/222b7/image-2.png 163w,
/static/9f4edda7299c2f1a05e6053af64a94f7/ff46a/image-2.png 325w,
/static/9f4edda7299c2f1a05e6053af64a94f7/a6d36/image-2.png 650w,
/static/9f4edda7299c2f1a05e6053af64a94f7/e548f/image-2.png 975w,
/static/9f4edda7299c2f1a05e6053af64a94f7/b490f/image-2.png 1142w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이로써 로깅 내역에 Prefix 로 Correlation ID 가 함께 출력되어, 로그를 요청별로 그룹화할 수 있게 되었다. MDC 는 Slf4j 에서 지원하는 것이므로, Appender 에 따라 파일, 데이터베이스, 엘라스틱서치 등 다양한 소스와 함께 맥락을 남길 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;비동기-처리시-mdc-전파&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC%EC%8B%9C-mdc-%EC%A0%84%ED%8C%8C&quot; aria-label=&quot;비동기 처리시 mdc 전파 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비동기 처리시 MDC 전파&lt;/h2&gt;
&lt;p&gt;MDC가 전파되지 않는 케이스로 &lt;code class=&quot;language-text&quot;&gt;@Async&lt;/code&gt; 를 사용한 비동기 처리가 존재한다. 아직 우리 모행 서비스에는 비동기 처리 작업이 없기에 당장엔 고려할 필요가 없지만, 이와 관련하여 미리 알아둘 필요가 있다고 생각한다.&lt;/p&gt;
&lt;p&gt;스프링에서 &lt;code class=&quot;language-text&quot;&gt;@Async&lt;/code&gt; 어노테이션을 붙인 메소드는 요청 쓰레드와는 다른 별도의 쓰레드에서 작업이 수행되어 비동기적으로 다른 테스트를 수행할 수 있다. MDC 는 쓰레드 로컬을 사용하기 때문에 &lt;code class=&quot;language-text&quot;&gt;@Async&lt;/code&gt; 가 붙은 메소드에는 MDC 가 전파되지 않는다. 이를위해 비동기 작업을 수행하는 쓰레드에 MDC 를 전파할 방법이 필요하다.&lt;/p&gt;
&lt;h3 id=&quot;taskdecorator&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#taskdecorator&quot; aria-label=&quot;taskdecorator permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TaskDecorator&lt;/h3&gt;
&lt;p&gt;스프링에선 &lt;code class=&quot;language-text&quot;&gt;TaskDecorator&lt;/code&gt; 비동기 작업이 수행되기 이전/이후의 작업을 데코레이트 할 수 있다. 이를 구현하여 비동기 작업이 수행되는 쓰레드에 MDC 를 복제할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MdcTaskDecorator&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TaskDecorator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Runnable&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;decorate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Runnable&lt;/span&gt; runnable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; copyOfContextMap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MDC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCopyOfContextMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token constant&quot;&gt;MDC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setContextMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;copyOfContextMap&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            runnable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞서 구현한 &lt;code class=&quot;language-text&quot;&gt;TaskDecorator&lt;/code&gt; 구현체를 &lt;code class=&quot;language-text&quot;&gt;TaskExecutor&lt;/code&gt; 가 사용하도록 하자. &lt;code class=&quot;language-text&quot;&gt;TaskExecutor&lt;/code&gt; 는 비동기 작업을 관리하고 수행하는 인터페이스이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@EnableAsync&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AsyncConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TaskExecutor&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;taskExecutor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ThreadPoolTaskExecutor&lt;/span&gt; threadPoolTaskExecutor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ThreadPoolTaskExecutor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        threadPoolTaskExecutor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCorePoolSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        threadPoolTaskExecutor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTaskDecorator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MdcTaskDecorator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; threadPoolTaskExecutor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/slf4j-mapped-diagnotics-context/&quot;&gt;https://hudi.blog/slf4j-mapped-diagnotics-context/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev-jwblog.tistory.com/126#article-1--1--mdc(mapped-diagnostic-context)%EB%9E%80&quot;&gt;https://dev-jwblog.tistory.com/126#article-1--1--mdc(mapped-diagnostic-context)%EB%9E%80&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://0soo.tistory.com/246&quot;&gt;https://0soo.tistory.com/246&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/266&quot;&gt;https://mangkyu.tistory.com/266&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[DB 레플리케이션 환경에서 DataSource 라우팅이 안되는 이슈 해결기 😤 (feat. JPA OSIV)]]></title><description><![CDATA[💡 현재 포스트는 하모니 팀 기술 블로그 에 게시된 글 입니다. 데이터베이스 레플리케이션을 통한 쿼리 성능 개선  최근에 MySQL 8.0 레플리케이션과 스프링부트 DataSource 라우팅을 통한 부하 분산 에서 MySQL…]]></description><link>https://haon.site/database/replication-osiv-issue/</link><guid isPermaLink="false">https://haon.site/database/replication-osiv-issue/</guid><pubDate>Fri, 04 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/replication-osiv-issue/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;데이터베이스-레플리케이션을-통한-쿼리-성능-개선&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%84-%ED%86%B5%ED%95%9C-%EC%BF%BC%EB%A6%AC-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0&quot; aria-label=&quot;데이터베이스 레플리케이션을 통한 쿼리 성능 개선 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 레플리케이션을 통한 쿼리 성능 개선&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0ce47ac33b78f3914d733d0380dcb235/0d390/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.73619631901841%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABkUlEQVR42pVTTU/CQBTkV3ow8eYPMF44GhNOxhPGi4IiigFDFEigElDwI1ExIIJCBGqo1RD5aAEppZZ2pKUgAiK+pHnp2523MztvdZgyZFlWM0VRMG/vw2S2w+8PjuzTDYMkSeqDB+u9Wr3+gUTIgKRvDtk7l7YujW/4Gyslt1otNBoNZHMkrLt6OO3zcDjXxzfsAfPPFLxuAvF4vN9IEARUq1WwnU8J+vUNKxYCyzshWA6D2r4hyZJ2QjRzg023ESRJqtJZlkWtVoMoiv1DhZaAZDSFyEUM+Rz1Q4nWsPsjSiJsT0asJhaRYRPgajw+RWFE/l/Rlaw1Pad92Hs0gmmW0GzwHXZVlZ2qQuqqKBQKOHIdw+MN4+r6drLLXbbfTBQjGIZRzWi322qtWCzBbtLDZpzBWcAx2eUeUyUPOsxxHCqVCmRJxgtN49SzgKh/FifEmgaccmwG705hyPM8HlJpGLY8WLIGsHFAjDNlulfSAykjdBmOIuCL4D6WHj82/wlFPstWUC6/g+lkZawGTfkCn7GRYXBHAskAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/0ce47ac33b78f3914d733d0380dcb235/a6d36/image-4.png&quot;
        srcset=&quot;/static/0ce47ac33b78f3914d733d0380dcb235/222b7/image-4.png 163w,
/static/0ce47ac33b78f3914d733d0380dcb235/ff46a/image-4.png 325w,
/static/0ce47ac33b78f3914d733d0380dcb235/a6d36/image-4.png 650w,
/static/0ce47ac33b78f3914d733d0380dcb235/e548f/image-4.png 975w,
/static/0ce47ac33b78f3914d733d0380dcb235/3c492/image-4.png 1300w,
/static/0ce47ac33b78f3914d733d0380dcb235/0d390/image-4.png 1472w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;최근에 &lt;a href=&quot;https://haon.blog/database/replication-mysql-springboot/&quot;&gt;MySQL 8.0 레플리케이션과 스프링부트 DataSource 라우팅을 통한 부하 분산&lt;/a&gt; 에서 MySQL 과 스프링부트 환경으로 레플리케이션 환경을 구축하는 방법에 대해 실습을 다루었다. 레플리케이션을 직접 우리 서비스에 구축하기 전까지는 크게 어려움이 없을 것이라고 생각했다.&lt;/p&gt;
&lt;p&gt;하지만, 막상 우리 모행 서비스에 레플리케이션을 적용할 때 DataSource 라우팅이 되지 않아서 생각보다 많은 트러블슈팅을 만났다. 하나는 &lt;code class=&quot;language-text&quot;&gt;TransactionSynchronizationManager&lt;/code&gt; 동기화 이슈문제이었고, 다른 하나는 &lt;code class=&quot;language-text&quot;&gt;JPA OSIV&lt;/code&gt; 관련 이슈이다. 이번 포스팅에선 후자인 OSIV 관련 DataSource 라우팅에 대해 다루어보고자 한다.&lt;/p&gt;
&lt;h2 id=&quot;문제-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%A0%9C-%EC%83%81%ED%99%A9&quot; aria-label=&quot;문제 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문제 상황&lt;/h2&gt;
&lt;p&gt;위 포스팅을 작성할 때는 간단한 새로운 프로젝트를 별도로 만들어서 학습을 진행했기 떄문에 큰 문제없이 레플리케이션 적용에 성공했다. 문제는 레플리케이션을 모행 서비스에 적용할 때 DataSource 라우팅이 안된다는 점이다. &lt;strong&gt;기대와 달리 트랜잭션 내의 쿼리들이 Source, Replica 서버로 분산되지 않고,&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;readOnly=false&lt;/code&gt; &lt;strong&gt;로 설정된 서비스 메소드 트랜잭션 쿼리가 레플리카(읽기 전용) 서버로 날아갔다.&lt;/strong&gt; DataSource 라우팅이 되지 않는 상황으로, 무엇이 문제였을까?&lt;/p&gt;
&lt;h2 id=&quot;-그렇다고-datasource-라우팅이-모든-메소드에서-안되는건-아니다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-%EA%B7%B8%EB%A0%87%EB%8B%A4%EA%B3%A0-datasource-%EB%9D%BC%EC%9A%B0%ED%8C%85%EC%9D%B4-%EB%AA%A8%EB%93%A0-%EB%A9%94%EC%86%8C%EB%93%9C%EC%97%90%EC%84%9C-%EC%95%88%EB%90%98%EB%8A%94%EA%B1%B4-%EC%95%84%EB%8B%88%EB%8B%A4&quot; aria-label=&quot; 그렇다고 datasource 라우팅이 모든 메소드에서 안되는건 아니다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;🤔 그렇다고 DataSource 라우팅이 모든 메소드에서 안되는건 아니다!?&lt;/h2&gt;
&lt;p&gt;여러 시도를 해보던중, 재밌는 점 하나를 발견했다. 요상하게도 모든 서비스 메소드에서 DataSource 라우팅이 안되는 것은 아니고, 일부 로직에선 라우팅이 정상 수행되고 있다는 점이다. &lt;strong&gt;신기하게도, 로그인이 필요없이 비회원도 이용할 수 있는 API 콜에 대해선 DataSource 타깃이 정상적으로 선택되고 라우팅이 수행되었다.&lt;/strong&gt; 다시말해, 로그인이 필요한 대부분의 로직에선 DataSource 분기 처리가 되지 않고있었다.&lt;/p&gt;
&lt;h3 id=&quot;서비스애플리케이션-계층의-메소드를-사용하는-argument-resolver&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B9%84%EC%8A%A4%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EA%B3%84%EC%B8%B5%EC%9D%98-%EB%A9%94%EC%86%8C%EB%93%9C%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-argument-resolver&quot; aria-label=&quot;서비스애플리케이션 계층의 메소드를 사용하는 argument resolver permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서비스(애플리케이션) 계층의 메소드를 사용하는 Argument Resolver&lt;/h3&gt;
&lt;p&gt;우리 팀은 스프링에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;HandlerMethodArgumentResolver&lt;/code&gt; 를 사용하여 인가(Authorization) 로직을 처리하고 있다. 서비스 계층내의 AuthService 에 선언된 &lt;code class=&quot;language-text&quot;&gt;extractMemberId()&lt;/code&gt; 메소드로 엑세스 토큰으로 부터 멤버의 ID 를 추출하고 있다. 마지막으로 &lt;code class=&quot;language-text&quot;&gt;Accessor&lt;/code&gt; 라는 DTO 를 컨트롤러에게 전달한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthenticationArgumentResolver&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerMethodArgumentResolver&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthService&lt;/span&gt; authService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthenticationBearerExtractor&lt;/span&gt; authenticationBearerExtractor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Accessor&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resolveArgument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MethodParameter&lt;/span&gt; methodParameter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndViewContainer&lt;/span&gt; modelAndViewContainer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NativeWebRequest&lt;/span&gt; nativeWebRequest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebDataBinderFactory&lt;/span&gt; webDataBinderFactory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nativeWebRequest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getNativeRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BadRequestException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;잘못된 HTTP 요청입니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; accessToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; authenticationBearerExtractor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; authService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;extractMemberId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accessToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Accessor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;argument-resolver-는-트랜잭션을-사용하고-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#argument-resolver-%EB%8A%94-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B3%A0-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;argument resolver 는 트랜잭션을 사용하고 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Argument Resolver 는 트랜잭션을 사용하고 있다&lt;/h3&gt;
&lt;p&gt;앞서 설명했듯이 Argument Resolver 는 &lt;code class=&quot;language-text&quot;&gt;AuthService&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;extractMemberId()&lt;/code&gt; 를 사용한다. &lt;code class=&quot;language-text&quot;&gt;extractMemberId()&lt;/code&gt; 는 아래와 같이 멤버 ID 를 추출하고, 회원 존재여부를 검증을 수행한다. 여기서 중요한 점은 &lt;code class=&quot;language-text&quot;&gt;@Transactional(readOnly = true)&lt;/code&gt; 로 선언되었다는 점이다.  &lt;strong&gt;Argument Resolver 의 코드는 서비스 계층에 진입하여 트랜잭션을 사용한다는 점을 알 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;그리고 여러 시도와 실험을 통해 &lt;code class=&quot;language-text&quot;&gt;extractMemberId()&lt;/code&gt; 의 트랜잭션 &lt;code class=&quot;language-text&quot;&gt;readOnly&lt;/code&gt; 설정 값에 따라 DataSource 가 동일하게 다른 트랜잭션에서도 사용되고 있다는 점을 알게 되었다. &lt;code class=&quot;language-text&quot;&gt;extractMemberId()&lt;/code&gt; 를 &lt;code class=&quot;language-text&quot;&gt;readOnly=false&lt;/code&gt; 로 설정했을 땐 DataSource 로 쓰기 전용인 소스(Source) 서버를 선택하게 되고, 반대로 &lt;code class=&quot;language-text&quot;&gt;readOnly=true&lt;/code&gt; 라면 읽기 전용인 레플리카 서버를 선택하게 된다. &lt;strong&gt;즉, 로그인 인증을 요구하는 동일한 API 호출내의 다른 트랜잭션에서도 항상&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;extractMemberId()&lt;/code&gt; &lt;strong&gt;와 동일한 DataSource 로 라우팅 된다는 점을 발견하게 되었다 🤔&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;readOnly &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;extractMemberId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; accessToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; memberId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tokenManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMemberId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accessToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;memberService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;memberId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NoExistMemberException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;존재하지 않는 멤버입니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; memberId&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;-jpa-osiv-로-인해-문제가-발생했다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-jpa-osiv-%EB%A1%9C-%EC%9D%B8%ED%95%B4-%EB%AC%B8%EC%A0%9C%EA%B0%80-%EB%B0%9C%EC%83%9D%ED%96%88%EB%8B%A4&quot; aria-label=&quot; jpa osiv 로 인해 문제가 발생했다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;🎯 JPA OSIV 로 인해 문제가 발생했다&lt;/h2&gt;
&lt;h3 id=&quot;-datasource-라우팅이-안되는-근본적인-원인을-찾기위해&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-datasource-%EB%9D%BC%EC%9A%B0%ED%8C%85%EC%9D%B4-%EC%95%88%EB%90%98%EB%8A%94-%EA%B7%BC%EB%B3%B8%EC%A0%81%EC%9D%B8-%EC%9B%90%EC%9D%B8%EC%9D%84-%EC%B0%BE%EA%B8%B0%EC%9C%84%ED%95%B4&quot; aria-label=&quot; datasource 라우팅이 안되는 근본적인 원인을 찾기위해 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;🔑 DataSource 라우팅이 안되는 근본적인 원인을 찾기위해&lt;/h3&gt;
&lt;p&gt;Argument Resolver 에서 호출되는 &lt;code class=&quot;language-text&quot;&gt;extractMemberId()&lt;/code&gt; 의 트랜잭션 읽기/쓰기 옵션에 따라 DataSource 가 결정된다는 요상한 사실은 발견했으나, 근본적인 문제 해결에 대한 해결책을 찾지 못하여 며칠동안 많은 삽질을 했다. 끝내 문제를 해결하지 못하고 끙끙 앓던 중, 카카오테크 교육자인 퍼실리테이터 맥(mac) 을 통해 도움을 요청했다. 그 결과, &lt;strong&gt;DataSource 라우팅이 되지 않는 근본적인 원인은 JPA OSIV(Open Session In View) 라는 점을 알게 되었다.&lt;/strong&gt; 도대체 JPA OSIV 가 무엇이길래 DataSource 라우팅이 안되도록 괴롭힌것일까? 🤔&lt;/p&gt;
&lt;h3 id=&quot;osiv-open-session-in-view-란-무엇인가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#osiv-open-session-in-view-%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot; aria-label=&quot;osiv open session in view 란 무엇인가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OSIV (Open Session In View) 란 무엇인가?&lt;/h3&gt;
&lt;p&gt;JPA 에서 사용되는 개념인 &lt;strong&gt;OSIV(Open Session In View) 란 영속성 컨텍스트를 프레젠테이션(뷰와 컨트롤러) 레이어까지 열어두는 기능&lt;/strong&gt;이다. 즉, OSIV 가 활성화되면 프레젠테이션 계층부터 레포지토리 계층까지 전 구간에서 영속성 컨텍스트가 제거되지 않고 활성화되어 살아있게 된다. 이를 통해 &lt;strong&gt;영속성 컨텍스트가 한 API 요청 내내 죽지않고 계속 유지된다.&lt;/strong&gt; (영속성 컨텍스트의 생명주기가 더 넓어진 것이다.) 별도의 설정이 없다면 스프링은 OSIV 를 기본적으로 활성화시킨다.&lt;/p&gt;
&lt;p&gt;그렇다면, OSIV 왜 필요할까? 스프링에선 왜 기본값으로 OSIV 를 활성화하는 것일까? 바로 &lt;strong&gt;프레젠테이션 계층에서 지연 로딩(Lazy Loading) 을 사용하기 위함이다.&lt;/strong&gt; OSIV 가 활성화되어 있다면, 서비스 계층에서 컨트롤러 계층으로 직접 엔티티를 반환(노출)하더라도, 프레젠테이션 계층에서 지연 로딩을 통해 엔티티 내부 필드를 정상적으로 읽어올 수 있게 된다. &lt;strong&gt;즉, OSIV 활성화하면 엔티티를 프레젠테이션에서 노출하는 (흔치않은) 일부 상황에서 예외 발생없이 영속성 컨텍스트로 부터 꺼내와서 사용할 수 있게된다.&lt;/strong&gt; 다만 우리 모행 서비스는 서비스 계층에서 엔티티를 DTO 로 반환하기 때문에, 엔티티를 프레젠테이션 계층까지 위험하게 노출시키지 않지 떄문에 OSIV 가 활성화되지 않아도 전혀 무관했다. 이 때문에 OSIV 를 깊게 학습할 일도 없었다.&lt;/p&gt;
&lt;h3 id=&quot;과거-스프링-osiv-의-문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%BC%EA%B1%B0-%EC%8A%A4%ED%94%84%EB%A7%81-osiv-%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;과거 스프링 osiv 의 문제점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;과거 스프링 OSIV 의 문제점&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1631db47d03089f3faee4417050faddd/09b15/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.601226993865026%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACMElEQVR42p2Ty08TURSH5280ceEDEzTGP8GFO6ML48JEXUAQiNEEEYJAxEQpDVIBqbGUZ2untFCdMu9Opy/mwXzemUYjETfe5Ms595xzfzn3JRGFEDQgbPbxLYFBEv+PIXndJoacwiguJOiFOfS9acx6FUM3MDQVXdXQVDXxTV0TVkv8M4h4EARIqm1Qe7ePMpxDGc+jjOX5PrrJ0r0pPtydIH3/FasPp8k8eC1iE8zcGRN2ksOhVZSRLD9GNjge3qDwPIPjNpE000XJPUZdvoy6clNwg/rH61QWr3CUHmB77hJTjy4w9+Si8Aeopa+J3FWU5cGkXsvcwlgZpLB4G6fZQTKMBlp5gfreU44LIyh7QxilZ7SNt7jaPHZ9HuVgFrn4ksL+ELWjcRz1Dc7xLNWDUb4Vh6mUBZUZms0WUsPUqO7k2c5+JbvyiZ0vOcr5HJbdQzM7qHoLtxVwqNfJbGbJFvNYbhvL6ZGTd0l9XmVtO8eBekjLdeMOTU7DgLBji052adbLeO0Gqjh43/fEQfv43gmhqOmP6I87jX7PO502juP0Bb1em3pxk/ezkyzNvMColSgUS6yvr5FOp0mlUlSrVSHs4514eJ6X3GgYhAR+QHQa0Wq1+oJx0rIsGgLbNHAsE108g2qlwtbWViIml0qJYFxn23ZCReTL5XKCLMuURE2sJZ33OKMootfr0e12+1bg+/6ZmrjDWCCOx8R+vE76JXAe8aJka2H4V+5fP+Unne0g13gMjlEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/1631db47d03089f3faee4417050faddd/a6d36/image.png&quot;
        srcset=&quot;/static/1631db47d03089f3faee4417050faddd/222b7/image.png 163w,
/static/1631db47d03089f3faee4417050faddd/ff46a/image.png 325w,
/static/1631db47d03089f3faee4417050faddd/a6d36/image.png 650w,
/static/1631db47d03089f3faee4417050faddd/e548f/image.png 975w,
/static/1631db47d03089f3faee4417050faddd/3c492/image.png 1300w,
/static/1631db47d03089f3faee4417050faddd/09b15/image.png 1704w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;잠시 OSIV 에 대해 더 깊게 이해해보자. 스프링에서 제공하는 과거 OSIV 는 클라이언트 요청이 들어오자마자 그 즉시 &lt;strong&gt;서블릿 필터, 스프링 인터셉터에서 영속성 컨텍스트와 트랜잭션을 생성했다. 그리고 이들을 API 요청 처음부터 끝까지 계속 유지하여 살아있게되는 방식으로 동작했다&lt;/strong&gt;. 유저의 요청 내내 영속성 컨텍스트가 살아있으므로, 엔티티도 영속상태를 끝까지 유지할 수 있게 된다. 즉, 지연 로딩을 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;하지만 이 방식의 과거 OSIV 는 문제점이 존재했다. 영속성 컨텍스트가 프레젠테이션에서 유지되면, 엔티티도 영속상태를 유지한다는 점이 문제가 된다. 이 상황에서 트랜잭션까지 함께 유지되다보니, &lt;strong&gt;엔티티를 프레젠테이션 계층에서 변경할 수 있게 된다.&lt;/strong&gt; 만약 아래처럼 DTO 를 사용하지 않고 컨트롤러에서 응답으로 엔티티를 직접 수정하고 응답해준다면 어떻게 될까?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TripController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TripService&lt;/span&gt; tripService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/trips/{id}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getTrip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@PathVariable&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt; trip &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tripService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findTripById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        trip&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;변경된 여행지 엔티티 이름&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; trip&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 &lt;code class=&quot;language-text&quot;&gt;setter()&lt;/code&gt; 를 컨트롤러에서 호출하면 문제가 발생한다. 과거 OSIV 방식에선 트랜잭션이 컨트롤러에서도 살아있게 된다고 했었다. 이 특징때문에 위 코드에서 JPA 더티 체킹(Dirty Checking) 이 발생하여 트랜잭션이 커밋되고, 수정된 내용이 의도치 않게 실제 DB 에 반영된다. &lt;strong&gt;즉, 과거 OSIV 방식에선 트랜잭션이 프레젠테이션 계층에서도 활성화되어  JPA 더티체킹이 발생함으로인해 엔티티 수정이 가능하다는 문제점이 발생했다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;현재-스프링-osiv-동작-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%98%84%EC%9E%AC-%EC%8A%A4%ED%94%84%EB%A7%81-osiv-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;현재 스프링 osiv 동작 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;현재 스프링 OSIV 동작 방식&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7e1d2255ac4c6e974671e264f4acd651/56caf/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.89570552147239%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB/UlEQVR42qWRzU8TYRCH+0fqyc+b/hsSE+PBEwc0RElI1QNq5FDEGqwCpRbaFDAtLVDKUtrdd79KF1h2u+z7uLulICcTnOTJO5N5M/ObmRSEcH4S4QwJ3eg95qaWIvQJjBKBKEQUCewagb6KjPqcOMfomoohRISWoKsaQlXR1C4iysXEvm1bw4K+66HNlLCm1zDSa9gRB2+W+PE8TXZsivmxd+SefeLr2HsyT9LMP31L4cUs2lSZ9mSRw9erHE78ovathIwLhqHEMx180ccTR/i6w2nXot+1aRSXaXx5gLJwl3buIdvz9/g8fpvZ8Vusf7iPmX+EWHqMlrtDrZAeFpRhgGN1MNUWtq5gaS3cvkiS7c4eVneOvsiw35qm3pik1phGVeboKB+p1SeoVl+ys/uK/f3CcOTQd2lW8qznv1NezLKxskC3XqHvOOzuNun1B5EvKW5t8rNcZGOvytkZiN4Ry7/XWNmoUGlu0dbaxCpSf19I13WklJexYRiXseueoigtTo6di6y8dl3D0InXlxSUQaSi20Kpb2IcbEexl3yyLAsRXThuZJpmgmVaSZwgLoh8TdOGIyd9zj12SotkZ2co5zIQuBciZKIwJgzDa+/IHzGa5NrISMn/Wuqq1pWaf1n8p9frJSsZEa8jVpq6qZLBYIDv+wme5yXEjf4AhTmBraXpdAwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7e1d2255ac4c6e974671e264f4acd651/a6d36/image-1.png&quot;
        srcset=&quot;/static/7e1d2255ac4c6e974671e264f4acd651/222b7/image-1.png 163w,
/static/7e1d2255ac4c6e974671e264f4acd651/ff46a/image-1.png 325w,
/static/7e1d2255ac4c6e974671e264f4acd651/a6d36/image-1.png 650w,
/static/7e1d2255ac4c6e974671e264f4acd651/e548f/image-1.png 975w,
/static/7e1d2255ac4c6e974671e264f4acd651/3c492/image-1.png 1300w,
/static/7e1d2255ac4c6e974671e264f4acd651/56caf/image-1.png 1712w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위와 같이 과거 프레제젠테이션 계층에서 엔티티를 수정할 수 있다는 문제를 막으려면 어떻게 해야할까? 생각보다 해결안은 간단하다. 트랜잭션 생명주기를 전 계층이 아니라, 프레젠테이션 계층을 제외한 비즈니스 계층에서만 살아있도록 만들면 될 것이다. 실제로 현재 OSIV 는 &lt;strong&gt;엔티티를 프레젠테이션에서 수정이 불가능하도록 막았다.&lt;/strong&gt; 엔티티를 프레젠테이션 계층에서 수정하려고 시도하면 &lt;code class=&quot;language-text&quot;&gt;LazyInitializationException&lt;/code&gt; 예외가 발생한다. &lt;strong&gt;즉, 현재 OSIV 방식은 프레젠테이션 계층을 제외한 비즈니스 계층에서만 트랜잭션의 생명주기가  살아서 유지된다.&lt;/strong&gt; 프레젠테이션 계층에선 엔티티 수정이 불가능하고, 단순히 엔티티 조회만 가능하도록 변경되었다.&lt;/p&gt;
&lt;p&gt;여기서 가장 중요한 점은, 과거/현재 OSIV 방식 그 무엇이던간에 OSIV 활성화시 한번 생성된 영속성 컨텍스트는 전 계층에서 계속 살아서 유지된다는 점을 기억하자.&lt;/p&gt;
&lt;h2 id=&quot;jpa-osiv-가-왜-문제가-될까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jpa-osiv-%EA%B0%80-%EC%99%9C-%EB%AC%B8%EC%A0%9C%EA%B0%80-%EB%90%A0%EA%B9%8C&quot; aria-label=&quot;jpa osiv 가 왜 문제가 될까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JPA OSIV 가 왜 문제가 될까?&lt;/h2&gt;
&lt;p&gt;이러한 OSIV 로 인해 왜 DataSource 라우팅이 안된것일까? 앞서 설명했듯이, OSIV 를 사용하면 영속성 컨텍스트가 한 API 요창내내 전 계층에서 살아있게 된다.&lt;/p&gt;
&lt;h3 id=&quot;entitymanager-영속성-컨텍스트-db-커넥션&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#entitymanager-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-db-%EC%BB%A4%EB%84%A5%EC%85%98&quot; aria-label=&quot;entitymanager 영속성 컨텍스트 db 커넥션 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EntityManager, 영속성 컨텍스트, DB 커넥션&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7b6b2349f0edef0b5b5c8aea3f692713/18539/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.963190184049076%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABe0lEQVR42n2Ti46CQAxF+f/vI7yCoAI+AEEEfGvX000NZJMlKWWmndt728GRz/N8PuVwOMjxeJxZ0zSyXC4lSRLJ81y6rlM7nU7qLef9fos9Dq/H4yF1Xcs4jjIMgxrfHAiCQDzPU9D9fi+bzUa2260SIA//er3+AlLpfD4r0NSIoQBWALquK2EYSlmWWrBt2/8BpwxZcwAWxHe7nTIEGMnksT8DvN/vcrvd9ODlclEQvIHSw/V6rXEYWRyPAYgC+og5VCSRHlIVNvQINqvVSsFMGgB936tc8ljjAfwyZHokA0C1qqoUaLFYKDvkcch6arI5Ry5n6S8ec4qi+F6H6/WqVWkBfSUxyzItQAx2HGLNfpqm4vu+FkYh5sAASVTDm2RYME0YAQKYxTCI4JFP3NqgDE0yGwSoHMex3j32KRRFkRaBDTHMLjc9ZNKYDgVpTJWAyeUb4xawtn28MWMN4Oza/EppPn+Dr/3APLPJ3tRoRRhG+gfxzTBtKD/2eYxP9+HRuwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7b6b2349f0edef0b5b5c8aea3f692713/a6d36/image-2.png&quot;
        srcset=&quot;/static/7b6b2349f0edef0b5b5c8aea3f692713/222b7/image-2.png 163w,
/static/7b6b2349f0edef0b5b5c8aea3f692713/ff46a/image-2.png 325w,
/static/7b6b2349f0edef0b5b5c8aea3f692713/a6d36/image-2.png 650w,
/static/7b6b2349f0edef0b5b5c8aea3f692713/e548f/image-2.png 975w,
/static/7b6b2349f0edef0b5b5c8aea3f692713/18539/image-2.png 1074w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;알아야 할 점이 있다. JPA 환경에선 기본적으로 한 요청당 하나의 &lt;code class=&quot;language-text&quot;&gt;EntityManager&lt;/code&gt; 를 사용하게 된다. 또한, 각 EntityManager 는 자신만의 고유한 영속성 컨텍스트를 생성하고 이 안에 본인에게 필요한 엔티티를 관리하고, 영속화하게 된다.&lt;/p&gt;
&lt;p&gt;이러한 &lt;code class=&quot;language-text&quot;&gt;EntityManager&lt;/code&gt; 는 데이터베이스 연결이 필요한 시점에(보통 트랜잭션을 시작할 때) DB 커넥션 풀로부터 커넥션을 획득하고, DB 에 쿼리를 날려 얻어낸 엔티티 결과를 영속성 컨텍스트에 저장(영속화)한다. 이때 EntityManager 는 &lt;strong&gt;커넥션 풀에서 커넥션 풀을 획득해오니, 우리가 지정한 DataSource 를 선택하고 사용한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;-osiv-로-인해-초기에-획득한-커넥션이-끊어지지-않고-그대로-유지된다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-osiv-%EB%A1%9C-%EC%9D%B8%ED%95%B4-%EC%B4%88%EA%B8%B0%EC%97%90-%ED%9A%8D%EB%93%9D%ED%95%9C-%EC%BB%A4%EB%84%A5%EC%85%98%EC%9D%B4-%EB%81%8A%EC%96%B4%EC%A7%80%EC%A7%80-%EC%95%8A%EA%B3%A0-%EA%B7%B8%EB%8C%80%EB%A1%9C-%EC%9C%A0%EC%A7%80%EB%90%9C%EB%8B%A4&quot; aria-label=&quot; osiv 로 인해 초기에 획득한 커넥션이 끊어지지 않고 그대로 유지된다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;😡 OSIV 로 인해 초기에 획득한 커넥션이 끊어지지 않고 그대로 유지된다&lt;/h3&gt;
&lt;p&gt;문제가 여기서 발생한다. 과거 OSIV, 현재 OSIV 그 무엇이던간에 별개로 근본적인 문제가 발생한다. &lt;strong&gt;OSIV 덕분에 한 번 생성된 영속성 컨텍스트와 DB 커넥션은 프레젠테이션 계층부터 레포지토리 계층 전 계층에서 죽지않고 그대로 살아 유지된다는 점이다.&lt;/strong&gt; 우리는 아래와 같이  &lt;code class=&quot;language-text&quot;&gt;AbstractRoutingDataSource&lt;/code&gt; 를 사용한 구현체를 사용하여 각 서비스 트랜잭션 메소드마다 매번 새로운 커넥션을 획득하여 분산처리를 의도했었다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Profile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dev&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SOURCE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; sourceDataSource&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;REPLICA1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; replica1DataSource&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;REPLICA2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; replica2DataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt; routingDataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; dataSources &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        dataSources&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;SOURCE&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sourceDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        dataSources&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;REPLICA1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; replica1DataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        dataSources&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;REPLICA2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; replica2DataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTargetDataSources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataSources&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDefaultTargetDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sourceDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Primary&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; determinedDataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sourceDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;replica1DataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;replica2DataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LazyConnectionDataSourceProxy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;determinedDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 OSIV 로 인해 영속성 컨텍스트는 전 계층에서 계속 살아서 유지되고, &lt;strong&gt;한번 커넥션을 얻어온&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;EntityManager&lt;/code&gt; 가 또 다시 새로운 커넥션을 생성하지 않는다. 앞서 살펴봤듯이 우리는 Argument Resolver 내에서 &lt;code class=&quot;language-text&quot;&gt;AuthService&lt;/code&gt; 내의 &lt;code class=&quot;language-text&quot;&gt;extractMemberId()&lt;/code&gt; 메소드를 호출하는데, 이때 EntityManager 가 영속성 컨텍스트를 생성하고 계속 유지한다. &lt;strong&gt;즉, Argument Resolver 가 AuthService 를 호출한 시점에서 이미 영속성 컨텍스트는 커넥션을 획득하고 계속 해당 커넥션을 유지하여 사용하는 상태이다.&lt;/strong&gt; 이 떄문에 &lt;code class=&quot;language-text&quot;&gt;extractMemberId()&lt;/code&gt; 실행 후 컨트롤러가 본격적으로 다른 서비스 메소드를 실행하는 시점엔 영속성 컨텍스트가 새로운 커넥션을 획득하지 않는다.&lt;/p&gt;
&lt;p&gt;결론적으로, &lt;strong&gt;OSIV로 인해 Argument Resolver 호출 시점에서 생성된 시점에 영속성 컨텍스트가 생성되고 계속 살아서 유지된다. 이에 따라 커넥션도 계속해서 끊지않고 유지하므로 DataSource 의 선책도 Argument Resolver 가 호출되는 시점에 단 1번만 이루어진다.&lt;/strong&gt; 이러한 이유로 DataSource 라우팅이 정상 수행되지 않았던 것이다.&lt;/p&gt;
&lt;h3 id=&quot;️-osiv-비활성화를-통한-datasource-라우팅-문제-해결&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EF%B8%8F-osiv-%EB%B9%84%ED%99%9C%EC%84%B1%ED%99%94%EB%A5%BC-%ED%86%B5%ED%95%9C-datasource-%EB%9D%BC%EC%9A%B0%ED%8C%85-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0&quot; aria-label=&quot;️ osiv 비활성화를 통한 datasource 라우팅 문제 해결 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;♻️ OSIV 비활성화를 통한 DataSource 라우팅 문제 해결&lt;/h3&gt;
&lt;p&gt;OSIV 를 비활성화하면 DataSource 분기 문제가 해결된다. OSIV 를 비활설화시 영속성 컨텍스트의 생명주기는 트랜잭션 시작부터 종료까지로 좁혀진다. 이를통해 영속성 컨텍스트가 매 트랜잭션(서비스 메소드)을 실행할 때 마다 새롭세 생성되고, 커넥션도 매번 새롭게 획득하게 된다. &lt;strong&gt;결국 DataSource 로 매번 트랜잭션이 실행될떄마다 새롭게 하게 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dbac18b4bb758b10bfe0bfd3cd6d6ffa/2cb21/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.94478527607362%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABtklEQVR42o2SW2sTURDH9wP6Jn4PfbBSbEFF1CKF4oM+C9E+tBvagtj0QSsVodFSksY0abcxzXW7m81eks3uZjf5uWdzwUoF5zD85zD/uZwzI/GXjMfjBBuNBptpmcxeBjnG7Pfs3D/j7H/+xM72FrsfP7CV3sSxbaR/JTz8luXBvYe8fLbK0sIj3r1dv+YXuLL6hsXl5zx9scbdhSdUftWQpv5r5GA0wj5T6O6kOU+l0OQN3GIB1/fn3ND3uH90wa3tA27L+9z58pOiZs46HE91YgdD6BlHhPXHlA6W6CvL+LpMrx/E3snxXZ+Ms8779hop5RVp9zUN8xKpnPuBZ3fpmR1yh1+plfPYloXjOBitOheFY7qtGgMvoFpRUOJ7pXRCR9fp2yZXSpHWWSHJ4Tg9pNP8MU5XJ/BchN2+VPAGAzRNQzkvc5LPUTotoukaptHBuqrH3avxACxM08ToaKjtFnac3HZuGIqQ4XCIYRgxyU7QsibBruvOOcIWrxD/HkWjJEbwpD/XIMGpep6HHw9hpuI+iofFlBuGIaqqJuvVbDapVqtJ8Rs7/F8RBURnIrnAKIr4Dawbm0iLYrDeAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/dbac18b4bb758b10bfe0bfd3cd6d6ffa/a6d36/image-3.png&quot;
        srcset=&quot;/static/dbac18b4bb758b10bfe0bfd3cd6d6ffa/222b7/image-3.png 163w,
/static/dbac18b4bb758b10bfe0bfd3cd6d6ffa/ff46a/image-3.png 325w,
/static/dbac18b4bb758b10bfe0bfd3cd6d6ffa/a6d36/image-3.png 650w,
/static/dbac18b4bb758b10bfe0bfd3cd6d6ffa/e548f/image-3.png 975w,
/static/dbac18b4bb758b10bfe0bfd3cd6d6ffa/3c492/image-3.png 1300w,
/static/dbac18b4bb758b10bfe0bfd3cd6d6ffa/2cb21/image-3.png 2174w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;우리 팀은 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;application.yml&lt;/code&gt; 에서 OSIV 를 비활성화여, Multi DataSource 라우팅 문제를 해결할 수 있게 되었다. 설정을 위와 같이 변경하고, 다시 테스트하면 DataSource 라우팅이 정상적으로 동작한다 😎&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;spring&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  jpa&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;in&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;view&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;레플리케이션 환경에서 DataSource 라우팅이 크게 어렵진 않을 것이라고 예상했지만, 내 생각과 달리 JPA 와 데이터베이스에 대한 높은 이해도가 요구되었다. 특히나 여지껏 OSIV 라는 키워드 자체를 몰랐기 떄문에, 다시 한번 반성하면서 동시에 큰 배움을 얻을 수 있었다. 앞으로도 프로덕션을 개발하면서 현재 사용하는 프레임워크와 라이브러리에 대한 깊은 이해도를 갖기위해 계속 노력해야겠다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@haron/Spring-Connection-Pool-%EC%9D%B4-%EB%B6%80%EC%A1%B1%ED%95%98%EB%8B%A4%EA%B3%A0%EC%9A%94&quot;&gt;https://velog.io/@haron/Spring-Connection-Pool-이-부족하다고요&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ykh6242.tistory.com/entry/JPA-OSIVOpen-Session-In-View%EC%99%80-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94&quot;&gt;https://ykh6242.tistory.com/entry/JPA-OSIVOpen-Session-In-View%EC%99%80-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eastc.tistory.com/entry/DataSource-%EB%9D%BC%EC%9A%B0%ED%8C%85%EC%9D%B4-%EC%95%88%EB%90%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-OSIV&quot;&gt;https://eastc.tistory.com/entry/DataSource-%EB%9D%BC%EC%9A%B0%ED%8C%85%EC%9D%B4-%EC%95%88%EB%90%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-OSIV&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cornswrold.tistory.com/337&quot;&gt;https://cornswrold.tistory.com/337&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL 8.0 레플리케이션과 스프링부트 DataSource 라우팅을 통한 부하 분산]]></title><description><![CDATA[💡 데이터베이스 레플리케이션에 대한 자세한 이론은 고가용성과 확장성을 위한 데이터베이스 레플리케이션(DB Replication…]]></description><link>https://haon.site/database/replication-mysql-springboot/</link><guid isPermaLink="false">https://haon.site/database/replication-mysql-springboot/</guid><pubDate>Wed, 02 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 데이터베이스 레플리케이션에 대한 자세한 이론은 &lt;a href=&quot;https://haon.blog/database/replication-theory/&quot;&gt;고가용성과 확장성을 위한 데이터베이스 레플리케이션(DB Replication)&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;실습-환경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%EC%8A%B5-%ED%99%98%EA%B2%BD&quot; aria-label=&quot;실습 환경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실습 환경&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8cdf3f07c2bca1ef6c591c39225a364b/b67f3/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.73619631901841%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABfklEQVR42p2SS0sCcRTF/Y4tW7aJPkCLlj0kCFwUVESoJJlkluMUZmZFuTCkx6D4SMJc5aN5P/IxMydnUtMcJTvwZ2Bmzu/+77nXhj9K13XzWSqV4HQfYGvbg1MyPPSf7bdJ0zTzDMO+gaIo4Tm2iMz5NLJJX+e7Zg0cdStD9XodkiiiaNzQNY9QYA4er8Ma2DUW8gUEjwlQFNUDNhoNcBxnHkOVShX2XRLLezfY8YWHCptArVPhPheH82zTzElTNbAsC57n0Wq1eoZmu0CWyuMpkcLba2kYqHeyaah1uDJrWE8voMjnIHFyG9S0bH+cBjKMFgNwpxzgPmnIkgJBEKCq6gCwXH4HQUbhP4nhLn5v3fIoGYNgGAaSJPVMNM0i5F0B4ZrB7eXh+Cl3TWYM+s87A0jTtLlOlWoVEf8sHsNTiIRWJ1+bbhGjdUWRkX8pYMlJwH4Ux8Y+OVnLVmBFVnB1kUDQf42HZPp/wH6T2F5umqmBF1jUPqpmxv1T/gI2FJEx26vddAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/8cdf3f07c2bca1ef6c591c39225a364b/a6d36/image-1.png&quot;
        srcset=&quot;/static/8cdf3f07c2bca1ef6c591c39225a364b/222b7/image-1.png 163w,
/static/8cdf3f07c2bca1ef6c591c39225a364b/ff46a/image-1.png 325w,
/static/8cdf3f07c2bca1ef6c591c39225a364b/a6d36/image-1.png 650w,
/static/8cdf3f07c2bca1ef6c591c39225a364b/e548f/image-1.png 975w,
/static/8cdf3f07c2bca1ef6c591c39225a364b/3c492/image-1.png 1300w,
/static/8cdf3f07c2bca1ef6c591c39225a364b/b67f3/image-1.png 1338w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;지난 레플리케이션 이론에 이어서, 이번엔 직접 레플리케이션을 실습해보고자 한다. 실습이 끝나다면, 우리 하모니 팀 모행 서비스에서 TPS 분석과 동시에 쿼리 성능 개선을 직접 확인해 볼 예정이다. 실습 환경은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS EC2, Ubuntu 22.04 LTS&lt;/li&gt;
&lt;li&gt;SpringBoot 3.xx&lt;/li&gt;
&lt;li&gt;MySQL 8.0&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;mysql&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mysql&quot; aria-label=&quot;mysql permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MySQL&lt;/h2&gt;
&lt;p&gt;우분투 서버에 MySQL 을 설치해주도록 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;$ apt &lt;span class=&quot;token keyword&quot;&gt;update&lt;/span&gt;
$ apt install mysql&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;server&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;외부-접속허용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%B8%EB%B6%80-%EC%A0%91%EC%86%8D%ED%97%88%EC%9A%A9&quot; aria-label=&quot;외부 접속허용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;외부 접속허용&lt;/h3&gt;
&lt;p&gt;별다른 설정이 없다면 MySQL 은 로컬 호스트내에서만 호출이 가능하다. 소스, 레플리카 서버 바인딩 가능한 주솟값을 로컬 호스트 외에도 모두 외부에서 접속이 가능하도록 설정해주자. 이 설정이 없다면 레플리카 서버에서 소스 서버로 동기화를 위해 접근해야 하는데, 이 행위가 불가능하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;vi &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysql&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;d&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cnf

# bind&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;address &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;127.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;
# mysqlx&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;bind&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;address &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;127.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;소스-서버-구축&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%86%8C%EC%8A%A4-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95&quot; aria-label=&quot;소스 서버 구축 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;소스 서버 구축&lt;/h2&gt;
&lt;p&gt;소스, 레플리카 서버 각기 다른 작업을 진행해 줄 것이다. 우선 소스 서버부터 작업을 시작해주자. repliation 이라는 네이밍을 가진 데이터베이스 서버를 하나 생성해주고, 해당 DB 서버 작업 내용들이 레플리카 서버에 동기화되도록 할 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;$ sudo mysql &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;u root &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token comment&quot;&gt;// 또는 sudo mysql &lt;/span&gt;
$ &lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;database&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;replication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// replication 데이터베이스 생성&lt;/span&gt;
$ &lt;span class=&quot;token keyword&quot;&gt;show&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;databases&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// replication 데이터베이스가 생성되었는지 확인&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또한 소스 서버에서 이벤트(변경사항)이 발생했을 떄 레플리카 서버가 소스 서버에 접근하여 바이너리 로그 파일내에 기록된 이벤트를 읽어와야 한다. 이처럼 소스 서버에 접근하고 복제를 하기 위해선 아무 서버에서 접근할 수 있는 것이 아니라, 레플리케이션 전용 계정을 통해서만 접근이 가능하다. 레플리케이션 전용 MySQL 계정을 하나 생성하고, 레플리케이션 권한을 부여하도록 하자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;$ &lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;USER&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;replication&apos;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@&apos;%&apos;&lt;/span&gt; IDENTIFIED &lt;span class=&quot;token keyword&quot;&gt;WITH&lt;/span&gt; mysql_native_password &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;password&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
$ &lt;span class=&quot;token keyword&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;REPLICATION&lt;/span&gt; SLAVE &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;replication&apos;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@&apos;%&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;MySQL 5.8 버전 이상부터는 Password Auth 방식이 &lt;code class=&quot;language-text&quot;&gt;caching_sha2_password&lt;/code&gt; 방식으로 변경되었다. 따라서 위와 같이 유저를 생성할 때 &lt;code class=&quot;language-text&quot;&gt;IDENTIFIED WITH mysql_native_password BY&lt;/code&gt; 로 생성해야한다. 그렇지 않으면 애러가 발생한다.&lt;/p&gt;
&lt;h3 id=&quot;mysqldcnf-소스-서버-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mysqldcnf-%EC%86%8C%EC%8A%A4-%EC%84%9C%EB%B2%84-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;mysqldcnf 소스 서버 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;mysqld.cnf 소스 서버 설정&lt;/h3&gt;
&lt;p&gt;이제 &lt;code class=&quot;language-text&quot;&gt;mysqld.cnf&lt;/code&gt; 파일을 수정하여, MySQL 설정을 변경해보자. 현재 서버를 레플리케이션 환경으로 구축하기 위해 소스 서버를 활성화하기 위한 설정 작업이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;$ vi &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysql&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;d&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysqld&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cnf

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;mysqld&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
max_allowed_packet&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;M
server&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
log&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;bin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mysql&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;bin
binlog_format &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ROW&lt;/span&gt;
max_binlog_size &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;M
sync_binlog &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
expire&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;logs&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;days &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;
binlog_do_db &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;replication&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[mysqld]&lt;/code&gt; : MySQL 서버의 전역설정 정보를 나타낸다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;max_allowed_packet&lt;/code&gt; : 클라이언트와 서버간에 교환되는 최대 네트워크 패킷 크기로, 패킷의 최대 크기를 1000MB 로 설정했다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;server-id&lt;/code&gt; : 레플리케이션을 위해 서버에 할당된 고유한 식별자이다. 하나의 레플리케이션 토폴로지 환경을 구성하는 각 서버는 고유한 서버 ID 값을 가져야한다. 따라서 레플리카 서버와 다른 값을 가져야한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;log-bin&lt;/code&gt; : 바이너리 로그 파일의 위치(경로) 를 지정한다. (&lt;code class=&quot;language-text&quot;&gt;/var/lib/mysql/mysql-bin.XXXXXX&lt;/code&gt; 형식으로 저장 될 것이다.)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;binlog_format&lt;/code&gt; : 바이너리 로그 파일에 저장되는 데이터의 저장 형식을 지정하는 것이다. &lt;code class=&quot;language-text&quot;&gt;STATEMENT&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ROW&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;MIXED&lt;/code&gt; 이 3가지 중 하나를 선택할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;max_binlog_size&lt;/code&gt; : 바이너리 로그 파일의 최대 크기로, 지정된 크기에 도달하면 새로운 파일이 생성된다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;sync_binlog&lt;/code&gt; : N개의 트랜잭션 마다 바이너리 로그를 디스크에 동기적으로 기록되도록 지정한 것이다. 위 설정 값 1은 가장 안정적이지만, 가장 느린 설정이다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;expire-logs-days&lt;/code&gt; : 바이너리 로그 파일의 만료기간으로, 위 경우 7일이 지나면 파일이 제거된다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;binlog_do_db&lt;/code&gt; : 레플리케이션을 적용할 데이터베이스 이름을 지정한다. 앞서 만든 replication 데이터베이스의 이름을 지정했다. 별도의 설정이 없다면 모든 데이터베이스를 대상으로 복제가 진행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;소스-서버-상태-조화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%86%8C%EC%8A%A4-%EC%84%9C%EB%B2%84-%EC%83%81%ED%83%9C-%EC%A1%B0%ED%99%94&quot; aria-label=&quot;소스 서버 상태 조화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;소스 서버 상태 조화&lt;/h3&gt;
&lt;p&gt;모든 설정이 끝났다면, MySQL 서버를 다시 재가동해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ service mysql restart&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 소스 서버의 상태를 조회해보자. 여기서 &lt;code class=&quot;language-text&quot;&gt;File&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;Position&lt;/code&gt; 가 중요하다. 레플리케이션은 &lt;code class=&quot;language-text&quot;&gt;File&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;Position&lt;/code&gt; 값을 기반으로 동기화가 진행되니, 이 값을 기억해두자. 또한 이 값들은 데이터베이스에서 이벤트가 발생할 때 마다 매번 변경된다. 레플리카 서버는 항시 동기화를 위해 소스 서버의 File 과 Position 값을 바라보며 추적한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;mysql&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;SHOW&lt;/span&gt; MASTER &lt;span class=&quot;token keyword&quot;&gt;STATUS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;------------------+----------+--------------+------------------+-------------------+&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;File&lt;/span&gt;             &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Position &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Binlog_Do_DB &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Binlog_Ignore_DB &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Executed_Gtid_Set &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;------------------+----------+--------------+------------------+-------------------+&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; mysql&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;bin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;000001&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;      &lt;span class=&quot;token number&quot;&gt;157&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;replication&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;                  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;                   &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;------------------+----------+--------------+------------------+---------------&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Position&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 Position : 바이너리 로그 파일 내에서 레플리카 서버가 어떤 위치부터 읽어야할지에 대한 위치 값을 뜻한다. 즉, 레플리카 서버는 Postiion 을 상시로 추적해가며 최근에 소스서버에서 이벤트가 발생한 위치에서부터 이벤트를 읽어들인다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;레플리카-서버-구축&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%94%8C%EB%A6%AC%EC%B9%B4-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95&quot; aria-label=&quot;레플리카 서버 구축 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레플리카 서버 구축&lt;/h2&gt;
&lt;p&gt;앞선 소스 서버와 마찬가지로 replication 이라는 데이터베이스를 생성해주자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;$ &lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DATABASE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;replication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// 슬레이브 서버도 동일한 명으로 DB 를 생성해준다&lt;/span&gt;
$ &lt;span class=&quot;token keyword&quot;&gt;show&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;databases&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;                &lt;span class=&quot;token comment&quot;&gt;// 잘 생성되었는지 확인&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;mysqldcnf-레플리카-서버-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mysqldcnf-%EB%A0%88%ED%94%8C%EB%A6%AC%EC%B9%B4-%EC%84%9C%EB%B2%84-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;mysqldcnf 레플리카 서버 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;mysqld.cnf 레플리카 서버 설정&lt;/h3&gt;
&lt;p&gt;레플리카 서버도 &lt;code class=&quot;language-text&quot;&gt;mysqld.cnf&lt;/code&gt; 파일을 수정하여 레플리케이션을 위한 설정을 진행해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ vi &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysql&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;d&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysqld&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cnf

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;mysqld&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
server&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
replicate&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;db &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; replication
read_only &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;server_id&lt;/code&gt; : 동일한 레플리케이션 토폴로지에선 각 서버가 유일한 ID 값을 지녀야한다고 했기 떄문에, &lt;strong&gt;server-id = 2&lt;/strong&gt; 로 부여해준다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;replicate-do-db&lt;/code&gt; : 소스 서버의 어떤 데이터베이스를 대상으로 동기화할 것인지 지정한다. 앞서 소스 서버에서 생성한 replication 데이터베이스를 지정해주었다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;read_only&lt;/code&gt; : 레플리카 서버는 읽기전용으로 구축하기 위해 &lt;strong&gt;read-only = 1&lt;/strong&gt; 을 부여해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;설정이 끝났다면 MySQL 서버를 재실행해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;$ service mysql restart&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;라우팅할-소스-서버-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9D%BC%EC%9A%B0%ED%8C%85%ED%95%A0-%EC%86%8C%EC%8A%A4-%EC%84%9C%EB%B2%84-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;라우팅할 소스 서버 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;라우팅할 소스 서버 설정&lt;/h3&gt;
&lt;p&gt;아직 레플리카 서버에는 어떤 소스 서버로 라우팅해야하는지에 대한 정보가 없다. 아래 쿼리를 순차적으로 실행하여, 라우팅에 대한 설정을 진행해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;RESET SLAVE&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

CHANGE MASTER &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt;
MASTER_HOST&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;xx.xxx.xx&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 소스 서버 Host IP&lt;/span&gt;
MASTER_USER&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;replication&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 레플리케이션 전용 계정 &lt;/span&gt;
MASTER_PASSWORD&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
MASTER_LOG_FILE&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;mysql-bin.000001&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
MASTER_LOG_POS&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;157&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;MASTER_HOST&lt;/code&gt; : 소스 서버의 IP 주소를 입력한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;MASTER_USER&lt;/code&gt; : 앞서 생성한 레플리케이션 전용 계정을 입력한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;MASTER_PASSWORD&lt;/code&gt; : 레플리케이션 전용 계정의 비밀번호를 입력한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;MASTER_LOG_FILE&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;MASTER_LOG_POS&lt;/code&gt; : 앞서 소스 서버에서 기억해두었던 FILE 값과 POSITION 값으로 설정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;마지막으로 레플리카 서버를 활성화한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;START&lt;/span&gt; REPLICA&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;레플리케이션-동작-확인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EB%8F%99%EC%9E%91-%ED%99%95%EC%9D%B8&quot; aria-label=&quot;레플리케이션 동작 확인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레플리케이션 동작 확인&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SHOW&lt;/span&gt; SLAVE &lt;span class=&quot;token keyword&quot;&gt;STATUS&lt;/span&gt;\G&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;레플리카 서버에서 레플리케이션이 잘 동작하는지 확인해보자. &lt;code class=&quot;language-text&quot;&gt;Slave_IO_Running&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;Slave_SQL_Running&lt;/code&gt; 이 둘다 &quot;Yes&quot; 가 되어야 정상 동작하는 것이다. 만약 하나라도 &quot;No&quot; 인 경우라면 에러 로그를 확인 후 처리해줘야한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/61d5904aae63e7b87379636e4b88e003/bb543/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.239263803680984%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABwElEQVR42m2Ra4ubQBSG/RFNXaPJaGKSmtUYdU3EezSJ5rLst9J+KAvtQmG7l7JQSgv97W/nTMlSlv3wMDMyPuc9Z6SqqkCs12vBdrtF27bY7XZidV0Xvu8jiiLEcYwkSZBlGbI0RcqhfVEU8DwPqqpC0jQNvV5PQB8mk4m4SOR5LgREXdcoyxJhGCIIAlHItm1RjKAzuSTGGPr9Pmg1DAOyLIs0h8NBJCcZyZumEWkcx4GiKM8hXiKR7ES328VoNBLtHo9H7Pd7IaXWF4uFKGhZFqbTqQhAAvrvVaHBLywuAlzyZFeXR5R5hn3boOJt1qsSeZrAn89RFrmYmWma0HVdFKH90Bz+EzIuUzlvmA7ZGGLi+Ui3DbwkQ7JpMI9TuHECKwgxsGcYux4sntDjcpodPQaNhDoQj6L2GZyeiptzAzczE7fBFD9XSzwlPn7kF3hYzvAYubgLz/GZ3/m2dFDxtPpgwDt7peW3XHiln+GL0cHBMvG9rXC3KTg57psSt3WGr6sY7913qJiCp/EZ8pEBhY+I/Tf/k1iidn2m4pPewX1o4/cqwp9N8swvfr4ea/hoyPjAOgJTU6G9kJ2EfwHgbBtg4XzFugAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/61d5904aae63e7b87379636e4b88e003/a6d36/image.png&quot;
        srcset=&quot;/static/61d5904aae63e7b87379636e4b88e003/222b7/image.png 163w,
/static/61d5904aae63e7b87379636e4b88e003/ff46a/image.png 325w,
/static/61d5904aae63e7b87379636e4b88e003/a6d36/image.png 650w,
/static/61d5904aae63e7b87379636e4b88e003/e548f/image.png 975w,
/static/61d5904aae63e7b87379636e4b88e003/3c492/image.png 1300w,
/static/61d5904aae63e7b87379636e4b88e003/bb543/image.png 1418w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;만약 레플리케이션이 동작하지 않아서 설정을 다시 해야한다면, 아래 명령어로 레플리카를 잠시 비활성화시키고 재설정 해주자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;STOP REPLICA&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  
&lt;span class=&quot;token comment&quot;&gt;# 에러를 해결하고나서 &quot;RESET SLAVE;&quot; 부터 다시 활성화시킬때, 레플리케이션을 먼저 종료해야한다&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;트러블슈팅1 - 소스 서버에 연결이 되지 않는다면?&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;Slave_IO_Running: Connecting
Slave_SQL_Running: YES
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;필자가 마주했던 트러블슈팅으로, 만약 &lt;code class=&quot;language-text&quot;&gt;Slave_IO_Running&lt;/code&gt; 이 계속 Connecting 상태로 연결을 계속 시도하는 중이라면 라우팅할 소스 서버 설정을 위한 쿼리를 잘못 작성했을 가능성이 크다. 또는 잘못된 방화벽 설정으로 인해 레플리카 서버가 소스 서버에 접근하지 못하는 가능성도 존재한다 따라서 이와 관련한 쿼리를 제대로 작성했는지 다시 점검해보거나, 방화벽 설정등을 다시 확인해보자.&lt;/p&gt;
&lt;h4&gt;트러블슈팅2 - POSITION 값을 추적하지 못하는 경우&lt;/h4&gt;
&lt;p&gt;레플리카 서버는 소스 서버의 POSITION 값을 추적해가면서 동기화를 수행할텐데, 매번 달라지는 POSITION 값을 제대로 추적하지 못하여 발생하는 에러도 마주했다. 즉, ``Read_Master_Log_Pos&lt;code class=&quot;language-text&quot;&gt;와&lt;/code&gt;Exec_masterlog_pos` 값이 서로 달라서 레플리케이션이 수행되지 않는 상황이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;Slave_IO_Running: YES
Slave_SQL_Running: &lt;span class=&quot;token keyword&quot;&gt;NO&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
Last_Errno: &lt;span class=&quot;token number&quot;&gt;1062&lt;/span&gt;
Last_Error: Error ‘&lt;span class=&quot;token keyword&quot;&gt;Duplicate&lt;/span&gt; entry ‘&lt;span class=&quot;token number&quot;&gt;116069&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10002826&lt;/span&gt;′ &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;’ &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; query&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; 
&lt;span class=&quot;token keyword&quot;&gt;Default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;database&lt;/span&gt;: ‘THEEYE’&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Query: ‘&lt;span class=&quot;token keyword&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; IDX&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; NAME&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; REG_DATE &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;VALUES&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; “GOOD”&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NOW&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;’&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;에러가 발생한 에러 코드를 확인해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;show&lt;/span&gt; slave &lt;span class=&quot;token keyword&quot;&gt;status&lt;/span&gt;\G&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 에서 errno 확인
Last_Errno: &lt;span class=&quot;token number&quot;&gt;1062&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 또는 1164, 1053 가 발생했을 수도 있다.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이를 해결하기 위해, &lt;code class=&quot;language-text&quot;&gt;mysqld.cnf&lt;/code&gt; 파일에 &lt;strong&gt;slave-skip-error&lt;/strong&gt; 옵션을 추가하고 에러가 발생한 코드가 Skip 되도록 설정한 후, 레플리카 서버의 MySQL 서버를 재시작하자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;mysqld&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
slave&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;skip&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;errors &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1053&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1062&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1146&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러고 다시 MySQL 에 접속하여 상태를 확인하면, 레플리케이션이 정상 동작하는 것을 확인할 수 있을 것이다. 그동안 맞춰지지 않았던 Exec_Master_Log_Pos 값이 점점 올라가면서 Read_Master_Log_Pos 값과 동일하게 맞춰지면 동기화 완료된 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;mysql&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;show&lt;/span&gt; slave &lt;span class=&quot;token keyword&quot;&gt;status&lt;/span&gt;\G&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;row&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;
Slave_IO_State: Waiting &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; master &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; send event
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
Slave_IO_Running: Yes
Slave_SQL_Running: Yes&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;스프링부트에서-라우팅-환경-구축&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8%EC%97%90%EC%84%9C-%EB%9D%BC%EC%9A%B0%ED%8C%85-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95&quot; aria-label=&quot;스프링부트에서 라우팅 환경 구축 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링부트에서 라우팅 환경 구축&lt;/h2&gt;
&lt;p&gt;앞선 과정으로 MySQL 서버를 소스, 레플리카로 이중화하였으므로, 스프링부트에서 사용하는 DataSource 도 이에 알맞게 라우팅해줘야한다. &lt;code class=&quot;language-text&quot;&gt;readOnly=true&lt;/code&gt; 인 트랜잭션은 레플리카 DataSource 를 사용하고, &lt;code class=&quot;language-text&quot;&gt;readOnly=false&lt;/code&gt; 트랜잭션은 소스 DataSource 를 사용하도록 처리해주자.&lt;/p&gt;
&lt;h3 id=&quot;applicationyml-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#applicationyml-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;applicationyml 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;application.yml 설정&lt;/h3&gt;
&lt;p&gt;일단 2개의 DataSource에 대한 설정을 application.yml 에 적어준다. 하나의 DataSource에 대해서는 스프링부트가 자동으로 빈으로 만들어 관리해줬지만, 2개 이상의 DataSource를 사용할 때에는 우리가 직접 빈을 만들어 사용해야한다. 일단 아래와 같이 설정하자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;
spring&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  datasource&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    master&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      username&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; haon
      password&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; password
      driver&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mysql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jdbc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;Driver&lt;/span&gt;
      jdbc&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; jdbc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;mysql&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;111.111&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.11&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3306&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;replication
    slave&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      username&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; haon
      password&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; password
      driver&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mysql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jdbc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;Driver&lt;/span&gt;
      jdbc&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; jdbc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;mysql&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;222.222&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.22&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3306&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;replication
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;다중-datasource-를-위한-빈-등록&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%A4%91-datasource-%EB%A5%BC-%EC%9C%84%ED%95%9C-%EB%B9%88-%EB%93%B1%EB%A1%9D&quot; aria-label=&quot;다중 datasource 를 위한 빈 등록 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다중 DataSource 를 위한 빈 등록&lt;/h3&gt;
&lt;p&gt;DataSource 가 여러개로 다중화되면서, 직접 빈을 생성하고 등록해주어야한다. 이와 관련한 전체 코드는 아래와 같다. 천천히 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; master &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; slave &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;slave&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;master&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ConfigurationProperties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prefix &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;spring.datasource.master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;masterDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;slave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ConfigurationProperties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prefix &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;spring.datasource.slave&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slaveDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;master&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; masterDataSource&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;slave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; slaveDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt; routingDataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 쿼리 요청을 적절한 서버로 분기할 때 활용됨&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; targetDataSourceMap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// targetDataSourceMap 객체에 분기할 서버들의 DataSource 빈을 저장&lt;/span&gt;
        targetDataSourceMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; masterDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        targetDataSourceMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;slave&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; slaveDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTargetDataSources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;targetDataSourceMap&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// DataSource 타깃을 설정한다.&lt;/span&gt;
        routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDefaultTargetDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;masterDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Primary&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; determinedDataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;masterDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slaveDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LazyConnectionDataSourceProxy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;determinedDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@ConfigurationProperties&lt;/code&gt; 를 사용하면 &lt;code class=&quot;language-text&quot;&gt;application.yml&lt;/code&gt; 에서 특정 prefix 에 해당하는 설정 값만을 자바 Bean 에다 매핑할 수 있다. 즉, 2개의 DataSource 타입의 빈에 대해 서로 다른 prefix 설정 값을 불러와 빈이 생성되도록 할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;소스-서버-레플리카-서버를-위한-datasource-빈-등록&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%86%8C%EC%8A%A4-%EC%84%9C%EB%B2%84-%EB%A0%88%ED%94%8C%EB%A6%AC%EC%B9%B4-%EC%84%9C%EB%B2%84%EB%A5%BC-%EC%9C%84%ED%95%9C-datasource-%EB%B9%88-%EB%93%B1%EB%A1%9D&quot; aria-label=&quot;소스 서버 레플리카 서버를 위한 datasource 빈 등록 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;소스 서버, 레플리카 서버를 위한 DataSource 빈 등록&lt;/h3&gt;
&lt;p&gt;소스, 레플리카에 대한 2개의 DataSource 타입 빈을 등록해주도록 한다. &lt;code class=&quot;language-text&quot;&gt;@Quaifier&lt;/code&gt; 를 사용한 것을 볼 수 있다. 기본적으로 의존관계 주입은 타입 을 기준으로 빈을 등록하고 매핑되기 떄문에 동일한 타입이 있을때 별도의 처리가 없다면 &lt;strong&gt;NoUniqueBeanDefinitionException&lt;/strong&gt; 이 발생한다. 이를 해결하도록 동일한 DataSource 타입을 가지는 2개의 빈에 대해 @Quaifier 로 추가 구분자명을 부여해서 2개의 빈이 구분되어 등록되도록 해주었다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; master &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; slave &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;slave&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;master&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@ConfigurationProperties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prefix &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;spring.datasource.master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;masterDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;slave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@ConfigurationProperties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prefix &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;spring.datasource.slave&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slaveDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;abstractroutingdatasource-상속&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#abstractroutingdatasource-%EC%83%81%EC%86%8D&quot; aria-label=&quot;abstractroutingdatasource 상속 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AbstractRoutingDataSource 상속&lt;/h3&gt;
&lt;p&gt;DataSourceConfig 내에 RoutingDataSource 라는 오브젝트가 보인다. 이 구현체는 무슨 역할을 할까?&lt;/p&gt;
&lt;p&gt;스프링은 &lt;code class=&quot;language-text&quot;&gt;AbstractRoutingDataSource&lt;/code&gt; 라는 추상 클래스를 제공하여, Multi DataSource 환경에서 여러 DataSource 를 묶고 분기해준다. &lt;code class=&quot;language-text&quot;&gt;determineCurrentLookupKey()&lt;/code&gt; 를 오버라이드 했는데, &lt;strong&gt;여러개의 DataSource 중에서 실제로 사용될 DataSource 를 결정하는 역할&lt;/strong&gt;을 한다. 즉, 소스와 레플리카 서버중에서 어떤 DataSource 로 라우팅할지 결정한다.&lt;/p&gt;
&lt;p&gt;어떤 DataSource 로 라우팅할지는 &lt;code class=&quot;language-text&quot;&gt;TransactionSynchronizationManager&lt;/code&gt; 를 통해 결정한다.  &lt;code class=&quot;language-text&quot;&gt;TransactionSynchronizationManager&lt;/code&gt; 현재 요청에 대한 트랜잭션이 읽기전용인지 쓰기전용인지를 구분하고, 그에 따른 DataSource 서버 Key 값 (문자열)을 리턴한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//  AbstractRoutingDataSource : Multi DataSource 환경에서 여러 DataSource 를 묶고 분기해줄 때 사용한다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AbstractRoutingDataSource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// determineCurrentLookupKey 메소드 : 여러 datasource 중에서 실제로 사용될 DataSource 를 결정하는 역할&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 현재 트랜잭션의 속성에 따라 targetDataSourceMap 의 조회 Key 를 결정하기위해 AbstractRoutingDataSource 를 상속받아서 determineCurrentLookupKey 를 구현했다.&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;determineCurrentLookupKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; isReadOnly &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionSynchronizationManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isCurrentTransactionReadOnly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;현재 트랜잭션 속성이 ReadOnly인가?:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; isReadOnly&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; isReadOnly &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;slave&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;routingdatasource-로-multi-datasource-빈들을-라우팅-타깃으로-등록&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#routingdatasource-%EB%A1%9C-multi-datasource-%EB%B9%88%EB%93%A4%EC%9D%84-%EB%9D%BC%EC%9A%B0%ED%8C%85-%ED%83%80%EA%B9%83%EC%9C%BC%EB%A1%9C-%EB%93%B1%EB%A1%9D&quot; aria-label=&quot;routingdatasource 로 multi datasource 빈들을 라우팅 타깃으로 등록 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;routingDataSource() 로 Multi DataSource 빈들을 라우팅 타깃으로 등록&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DataSourceConfig&lt;/code&gt; 로 다시 돌아와서, &lt;code class=&quot;language-text&quot;&gt;routingDataSource()&lt;/code&gt; 에 대해 살펴보자. 앞서 정의한 RoutingDataSource 를 사용해서, 트랜잭션 요청을 읽기/쓰기에 따라 적절한 DataSource 로 라우팅하기 위해 선언된 메소드이다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 에서 HashMap 을 생성하고 앞서 정의한 소스, 레플리카 서버에 대한 DataSource  빈을 저장하고 있다. 이때 소스, 레플리카 서버에 대한 DataSource 빈을 각각 &lt;code class=&quot;language-text&quot;&gt;&quot;master&quot;&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;&quot;slave&quot;&lt;/code&gt; 라는 key 값과 매핑하여 key-value 형태로 저장한다. 이후 &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 에서 DataSource 빈들을 &lt;code class=&quot;language-text&quot;&gt;RoutingDataSource&lt;/code&gt; 에 등록한다. &lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 에서는 기본으로 라우팅할 DataSource 를 소스 서버로 지정했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;master&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; masterDataSource&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;slave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; slaveDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

   &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt; routingDataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 쿼리 요청을 적절한 서버로 분기할 때 활용됨&lt;/span&gt;
   &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; targetDataSourceMap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;

   &lt;span class=&quot;token comment&quot;&gt;// targetDataSourceMap 객체에 분기할 서버들의 DataSource 빈을 저장&lt;/span&gt;
   targetDataSourceMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; masterDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
   targetDataSourceMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;slave&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; slaveDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

   routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTargetDataSources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;targetDataSourceMap&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (3) =&gt; DataSource 타깃을 설정한다.&lt;/span&gt;
   routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDefaultTargetDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;masterDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (4)&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;datasource&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#datasource&quot; aria-label=&quot;datasource permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;dataSource()&lt;/h3&gt;
&lt;p&gt;앞선 &lt;code class=&quot;language-text&quot;&gt;routingDataSource()&lt;/code&gt; 로 만든 DataSource 를 스프링부트 전반에서 사용될 DataSource 로 등록하기 위해, &lt;code class=&quot;language-text&quot;&gt;dataSource()&lt;/code&gt; 빈에서 사용해줘야한다.&lt;/p&gt;
&lt;p&gt;이때 &lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourceProxy&lt;/code&gt; 을 사용하였다. 스프링은 기본적으로 트랜잭션에 진입하자마자 즉시 &lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 를 가져오고 커넥션을 맺는다. 그러고나서 트랜잭션의 현재 상태(읽기/쓰기 상태)가 설정된다. 이 떄문에 RoutingDataSource 내에서 &lt;code class=&quot;language-text&quot;&gt;TransactionSynchornizationManager&lt;/code&gt; 에 트랜잭션 정보를 동기화 하는 작업, 즉 트랜잭션의 현재 상태(읽기/쓰기) 를 파악하고 라우팅할 DataSource 를 결정하는 작업은 DataSource 로 부터 이미 커넥션을 맺은 이후에 뒤늦게 동작한다. 따라서 우리는 커넥션을 획득하는 시점을 지연(Lazy) 시킬 필요가 있다. &lt;strong&gt;현재 트랜잭션의 Read/Write Only 상태가 설정되고난 뒤에 그에 따른 DataSource 가 결정되면서 커넥션을 획득하도록 해야한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이 문제를 해결하기 위해 &lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourceProxy&lt;/code&gt; 를 사용해야한다. &lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourceProxy&lt;/code&gt; 를 사용하면 트랜잭션 진입 시점에 실제 커넥션을 리턴하는 대신에 프록시 커넥션 객체를 대신 리턴한다. 이를통해 커넥션이 실제로 사용되는 시점까지 커넥션 획득을 지연시켜서, 정상적으로 AbstractRoutingDataSource 가 트랜잭션 현재 읽기/쓰기 상태를 정상적으로 읽어올 수 있게 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Primary&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; determinedDataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;masterDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slaveDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LazyConnectionDataSourceProxy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;determinedDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이렇게까지 설정해주면, 트랜잭션의 readOnly 상태에 따라 DataSource 가 정상 분기된다. 사실 MySQL 에서 레플리케이션을 구축하는 것 보다 스프링부트 내에서 Multi DataSource 라우팅을 구축하는 것이 훨씬 어려웠다 🤔 스프링이 DataSource와 Transaction을 관리하는 방법에 대해서 깊게 공부해봐야겠다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Real MySQL 8.0 2권, 백은빈 , 이성욱 저자(글)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/database-replication-with-springboot-and-mysql/&quot;&gt;https://hudi.blog/database-replication-with-springboot-and-mysql/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://faq.add4s.com/?p=469&quot;&gt;http://faq.add4s.com/?p=469&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@inhwa1025/MySQL-Replication&quot;&gt;https://velog.io/@inhwa1025/MySQL-Replication&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[서버 성능 최적화를 위한 성능 테스트(Performance Test) 환경 구축]]></title><description><![CDATA[💡 현재 포스트는 하모니 팀 기술 블로그 에 게시된 글 입니다. 성능 테스트(Performance Test…]]></description><link>https://haon.site/network/performance-test/</link><guid isPermaLink="false">https://haon.site/network/performance-test/</guid><pubDate>Wed, 02 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/performance-test/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;성능-테스트performance-test-란-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B1%EB%8A%A5-%ED%85%8C%EC%8A%A4%ED%8A%B8performance-test-%EB%9E%80-&quot; aria-label=&quot;성능 테스트performance test 란  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;성능 테스트(Performance Test) 란 ?&lt;/h2&gt;
&lt;p&gt;성능 테스트란 특정 상황에서 시스템이 어느 수준의 성능을 내는지 측정하는 테스트 방식이다. 성능 테스트를 통해 시스템의 자원 사용량, 확장성, 신뢰성 등을 검증할 수 있다. 서버에 일정량의 부하를 주는 방식으로 테스트하며, 보통 애플리케이션의 성능을 측정하고, 최적화하기 위해 사용한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3f591ae432b8fb390c0e81ee9e8fcd8e/f793b/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.122699386503065%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABwklEQVR42pWTQU8TQRTH15MnTbx51wOeSPTgST8BNw9y8uQXICbyATQhGoPZiMiFXsSYYFKCUAVJMIYDaRGbom040NKli9sW090u3d0u25mfZbsKRop15jBvXub937z//z2FU5aUMjznfjR5prvYfivyd49RTkb6FSTJ6xWuvCtyObHJeKYMwqd1LFlPgFJIxP4BllZA13XUbQc1b5H8boGh43tmO1WrB0DRyerN5igNv8GaWAnvluuiWXZoL2mr3Ji7xFDq3u8qDvfpgFNfKI2+J5hMsV4wuPZikYuP48xuVFC/xeiPKQzMXKUeRJx2BYz8QcOhkd6Ctgh3E0nOPIpz/ulb+iYW2LVrJLZVUoXFSCDRvWQZ/dBIFvl8P463UWb4Yxrl4TRnn8xwPfYB120S1A9oVMw/OuFvQHnUD0u3pnip3CF9+zVVz2Vofp3B6VXSepV6rcbyyie+ZrMIIXoTRY/nyPQ/p/hqreP326o2d0PTNk20fB7DKPcAeGwFbW4cs4JT0qjHbrI3dgEnM8+eVsKzLbp1+MmAotPZh8+bhSzVB+fYGlHYXxjBD6kR/zkpR7PXoXYnATkV6Xn8a/Z+Ah/tfUUrkP6CAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/3f591ae432b8fb390c0e81ee9e8fcd8e/a6d36/image.png&quot;
        srcset=&quot;/static/3f591ae432b8fb390c0e81ee9e8fcd8e/222b7/image.png 163w,
/static/3f591ae432b8fb390c0e81ee9e8fcd8e/ff46a/image.png 325w,
/static/3f591ae432b8fb390c0e81ee9e8fcd8e/a6d36/image.png 650w,
/static/3f591ae432b8fb390c0e81ee9e8fcd8e/e548f/image.png 975w,
/static/3f591ae432b8fb390c0e81ee9e8fcd8e/3c492/image.png 1300w,
/static/3f591ae432b8fb390c0e81ee9e8fcd8e/f793b/image.png 1404w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;일반적으로 개발을 하면서 부하 테스트와 스트레스 테스트 등의 키워드를 많이 접해봤을 것이다. 위 사진을 보면 해당 테스트들은 모두 성능 테스트 하위에 있는 것을 알 수 있다. 그만큼 성능 테스트는 매우 광범위한 범위를 갖고 있는데, 그 중 가장 많이 사용되는 테스트가 바로 부하 테스트와 스트래스 테스트이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4100e6647e3bd6546fd35b5d277850eb/511f0/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 92.02453987730061%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAAAsTAAALEwEAmpwYAAACXklEQVR42nWU6bKiQAyFff9nm7o/xuuCC4o7yA6CopjpL9iMY93pqjaQTic5JwcHz+dT3tfj8ZDqcpH7/d772raVqqp6H3fw1XWtu20fvX/Az8UkIIB1vV7leDxIkiSahF3XlRwOe0mNrywLud1umvx0Oorv+5KmifpYmrDI8z5hZS5vtxtTpJSiKORSlhpM0aq6SG5iKUp856vUR6eakAMu2mU79P2T7igKTVelHA/GdzppV3TLnTA86zt7v9vp3YHlw3LJ5cV8Js50onZuNh3wPDW+MAz1nc7WK1cmk7Hs93vJskxpGHwO5Wpap4PdbiuetxZvvdIilnS7Pu/9M5QkiZUrFlx4G89U3Sl0YDH14BwYyEeFfdgDOZfwDOSOGmJ7yLSKXFgMgMnRJZOGDjr8/h7K19cvGY9HsjJQKcQzkLFTY/uEn21/rqZp5Gw6TOL4pYanFobnyWQky8VcFmZTWBNaXVkR89w0N62IxQcCLMk7VI3yDHwGRI6ewyxLVXeqQ3PIMJBLmqa9xoDPO7tL2uq9PM/UwnMvbIIg2cJjSFEUqY3jSO7Ghyy0wMtShKF0hROlgy41IV0Bj4X64WM2c2RtJBMEvhbrBjDqB4Fv+1IDsEkKJT8OhYN3scOp6y5l43myNXu9WimH/9FhxwUywHIZeHBDVTiEBs4j85Vg6Zph2fPYwIUe4gZMjwBGDmwgO85UJTEc/tZPj+CZ4+g7ejsHgRTmXwdaOIeG5XLxsw75YhCu67qqPT58OKY6f2OWayshBtHJq/k75U++IPx6rdVn/e+F3+Pf+YPDP/DaZeH6SbJxAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/4100e6647e3bd6546fd35b5d277850eb/a6d36/image-1.png&quot;
        srcset=&quot;/static/4100e6647e3bd6546fd35b5d277850eb/222b7/image-1.png 163w,
/static/4100e6647e3bd6546fd35b5d277850eb/ff46a/image-1.png 325w,
/static/4100e6647e3bd6546fd35b5d277850eb/a6d36/image-1.png 650w,
/static/4100e6647e3bd6546fd35b5d277850eb/e548f/image-1.png 975w,
/static/4100e6647e3bd6546fd35b5d277850eb/511f0/image-1.png 1172w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;부하-테스트-load-test&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%80%ED%95%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-load-test&quot; aria-label=&quot;부하 테스트 load test permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;부하 테스트 (Load Test)&lt;/h3&gt;
&lt;p&gt;부하 테스트는 서버에 부하를 점진적으로 한계치까지 가하는 방식의 성능 테스트이다. 이 테스트로 애플리케이션의 최대 성능을 파악할 수 있다. 즉, 부하 테스트를 통해 우리 애플리케이션이 견딜 수 있는 한계치의 요청을 측정 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;스트레스-테스트-stress-test&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%8A%B8%EB%A0%88%EC%8A%A4-%ED%85%8C%EC%8A%A4%ED%8A%B8-stress-test&quot; aria-label=&quot;스트레스 테스트 stress test permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스트레스 테스트 (Stress Test)&lt;/h3&gt;
&lt;p&gt;스트레스 테스트는 정상적인 상황에서 발생할 수 없는 극단적인 상황을 가정하고, 서비스가 극한의 부하 상황에서 어떻게 동작하고 어떻게 견디는지를 확인하는 테스트이다. 극단적인 상황이라 함은, 버퍼 오버플로우, 메모리 누수등이 해당될 것이다. 이러한 극단적인 상황을 사전에 발견하고 해결하기 위해 사용된다. 이 테스트를 통해 애플리케이션이 예기치 못한 극단적인 상황, 트래픽 유입으로 인해 마비되지 않도록 사전에 예방할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;우리-팀이-사용할-테스트-종류는&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%B0%EB%A6%AC-%ED%8C%80%EC%9D%B4-%EC%82%AC%EC%9A%A9%ED%95%A0-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A2%85%EB%A5%98%EB%8A%94&quot; aria-label=&quot;우리 팀이 사용할 테스트 종류는 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;우리 팀이 사용할 테스트 종류는?&lt;/h3&gt;
&lt;p&gt;우리 하모니 팀의 경우 성능 테스트를 통해 애플리케이션의 성능을 측정하고, 그 측정 결과를 기반으로 최적의 설정 값과 아키텍처를 구성하는 것이 목표이다. 즉, HikariCP 커넥션 풀 사이즈, DB 레플리케이션 아키텍처 등 성능 개선을 위해, 성능 테스트를 이어가며 최적의 설정을 구상하는 것이 목표이다.&lt;/p&gt;
&lt;p&gt;따라서 우리 팀이 실행한 테스트는 부하 테스트 쪽에 가깝다. 부하 테스트를 통해 애플리케이션, 데이터베이스 등의 최대 성능을 파악할 예정이기 때문이다. 그리고 성능 테스트라는 포괄적인 의미로 표현해도 될 것이다.&lt;/p&gt;
&lt;h2 id=&quot;tps-transaction-per-seconds&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tps-transaction-per-seconds&quot; aria-label=&quot;tps transaction per seconds permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TPS (Transaction Per Seconds)&lt;/h2&gt;
&lt;p&gt;그렇다면 우리는 성능 테스트시 어떤 지표를 보고 &quot;성능이 개선되었다&quot; 라고 표현할 수 있을까? 객관적인 지표가 필요하다. 우리 팀은 가장 많이 사용되는 지표인 TPS(Transaction Per Seconds) 를 기반으로 성능 테스트를 수행하고자 한다.&lt;/p&gt;
&lt;p&gt;TPS 란 말 그대로 초당 처리된 트랜잭션 수를 의미한다. 웹 애플리케이션 맥락에서 트랜잭션은 Request와 Response의 한 쌍이 처리됨을 말합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 TPS = Virtual User / Average Response Time&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;TPS 는 위와 같은 공식으로 계산된다. Virtual User 란 가상 유저의 수, Average Response Time 은 요청 이후 응답이 되돌아오는 평균 시간을 뜻한다. 만약 200명의 유저가 접속하고 평균 응답속도가 10이라면 TPS 는 20이 된다.&lt;/p&gt;
&lt;h2 id=&quot;성능-테스트-도구-jmeter-선택&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B1%EB%8A%A5-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%8F%84%EA%B5%AC-jmeter-%EC%84%A0%ED%83%9D&quot; aria-label=&quot;성능 테스트 도구 jmeter 선택 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;성능 테스트 도구, Jmeter 선택&lt;/h2&gt;
&lt;p&gt;성능 테스트 도구로 어떤 것을 선택할지 고민이 많았다. Jmeter 와 k6 중에 가장 고민이 많았는데, 우리 팀은 우선 Jmter 를 선택하기로 했다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6bbfb9f475604afef080f5e9d1786df9/81315/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.239263803680984%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABrUlEQVR42oWRTUsbURSG86cC9g9k48rs6iLiqi5bCsWFpuAHKi6KTQIFUReikUaSqhU1xIgoLW0XtV1YVDQQNHFIJn5MElNn7jzemTt+ZuGFyzlz557nvuc9Ptu2URvucxthyShU/uwS/+U23dT38FxBVf77O1ye3Z83L3VoWxaNzUFE+d9joFMkLJUX8jAWhvyRJ0A0E20h3HixmOI03Hn3gO/2de0Evs7BeQXmJ2HgNez8uAU+pakicW2yG3yFnlzx1AoJ9C5vLMPgG4j1w4deGO2GdEr90/UKjUbjDuYUuiJ2cmx3fcSq1xGOQttr+VyHT8OwNCvjECxLpWPvYTqqGIZhcG1a2LUa5nEeoWlQ1qgWDGqlK88CNUAX+G0dRt7B31/w5yfk9iEh2470QfXSbc4tqmxvEenpIRqLMTM1RTL1hbX0Ktlslkwmozw0LiAqC4fewtw4fJ6AhRml1PHx0B2emlZ5PkFnIEBrW5AXLS10hEK0v2zH7/cTj8cV8GhPqXEgTnQGkpDQ1LSCH+x61pmC6noGLZejKFsul0rSW51isYgmv2vSDgd4Aw2VjYmP1pIBAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/6bbfb9f475604afef080f5e9d1786df9/a6d36/image-3.png&quot;
        srcset=&quot;/static/6bbfb9f475604afef080f5e9d1786df9/222b7/image-3.png 163w,
/static/6bbfb9f475604afef080f5e9d1786df9/ff46a/image-3.png 325w,
/static/6bbfb9f475604afef080f5e9d1786df9/a6d36/image-3.png 650w,
/static/6bbfb9f475604afef080f5e9d1786df9/e548f/image-3.png 975w,
/static/6bbfb9f475604afef080f5e9d1786df9/3c492/image-3.png 1300w,
/static/6bbfb9f475604afef080f5e9d1786df9/81315/image-3.png 1656w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Jemter 는 초기엔 웹 서버 테스트를 위해 개발된 성능 테스트 도구로 사용되었는데, 현재는 데이터베이스, TCP, FTP 등 여러 프로토콜의 성능을 테스트할 수 있도록 발전했다.&lt;/p&gt;
&lt;h3 id=&quot;gui&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#gui&quot; aria-label=&quot;gui permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GUI&lt;/h3&gt;
&lt;p&gt;무엇보다 가장 매력적인 점은 GUI 가 존재한다는 점이 가장 매력적이었다. k6 와 달리 직접 스크립트를 작성할 필요가 없기 떄문에 비교적 빠르게 적응할 수 있고 테스트가 가능하다. k6 도 k6 Test Builder 라는 간단한 GUI 가 존재하기 하지만, Jmeter 처럼 모든 기능을 사용할 순 없으며 기본적으로 스크립트 코드를 사용해야 하므로 완전한 GUI 를 제공한다고는 할 수 없다.&lt;/p&gt;
&lt;h3 id=&quot;풍부한-레퍼런스와-커뮤니티가-존재한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%92%8D%EB%B6%80%ED%95%9C-%EB%A0%88%ED%8D%BC%EB%9F%B0%EC%8A%A4%EC%99%80-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0%EA%B0%80-%EC%A1%B4%EC%9E%AC%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;풍부한 레퍼런스와 커뮤니티가 존재한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;풍부한 레퍼런스와 커뮤니티가 존재한다&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5690809d3874cd90113895cfb712d552/4a00e/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.21472392638037%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACAklEQVR42oVT3YrTQBTuC6jFNk0mf5NkJjNJJptu627bRWgXSxcRtAqCgoIg7IUKgvgAXvgIvoCv4J146Rv4Lt5/nkzd0CrixceZM5P5zjnfN+ktbq9QT+YomlPI6hjKHIOLCj7P4ScKQars2otlh1EkkWQZjErB8zFSfWTBpUEv5ClSqaDLCkrl4JkkQmWRiJywi+13UZJ1CBNBhYTdb8+DOIEXxug5rgtdlChNBZcxDEYuGmPwaLuF1AUVSVE3BrnWVJTygkDrVIgOQRTD8wMLIvRQmRqLszPEPMH1/gAPtxf48ek93t05RaArZFJg5DG64MP1CMy3eQvHZTbvCNvNtvpytUJZGdzo38T5ZoPPHz7i8u6G9OL2POIcYcwpJr/jYb7XIUNB+s0XCwilMRj2Uc8e4+LVV0zXb2hMEr9pqEvSNyG9M0GG7Ea9ijHtHxDqssT05IQqJXCIMJ8/xYPXX7BePyEX24sSQ8ftxryCHZ35f4/cmjKbU4fURd9xsDQzfHt5iRdTAyYlymZsx267TIWEyHPbHQvCDn+YYuzInPS4Rq5vpcbPzTM8VxOEOoPKpdWpdbO97IfRAck+bIdClRhPbpHLHP2Rh/NG4/vbe7g/1YiLmjQuERGhv/c8/oXewPUxO8qwnDcY0prRpsM8yJr+ApFAFpU1a/dsgv8S/gIqbTuAzsYLagAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/5690809d3874cd90113895cfb712d552/a6d36/image-4.png&quot;
        srcset=&quot;/static/5690809d3874cd90113895cfb712d552/222b7/image-4.png 163w,
/static/5690809d3874cd90113895cfb712d552/ff46a/image-4.png 325w,
/static/5690809d3874cd90113895cfb712d552/a6d36/image-4.png 650w,
/static/5690809d3874cd90113895cfb712d552/e548f/image-4.png 975w,
/static/5690809d3874cd90113895cfb712d552/3c492/image-4.png 1300w,
/static/5690809d3874cd90113895cfb712d552/4a00e/image-4.png 1406w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;JMeter는 1998년도에 출시된 오래된 도구이다. 그렇기 때문에 20년 이상 발전해오면서 무수한 사용 사례를 거쳐왔다. 아파치 공식 문서도 좋지만, 많은 사용 경험을 가진 개발자들이 작성한 훌륭한 자습서나 책도 많다. 이러한 장점은 JMeter를 처음 사용하기 위한 어려움을 상당히 줄여주고 자료를 쉽게 찾을 수 있게 해준다.&lt;/p&gt;
&lt;h3 id=&quot;메모리-점유율이-다소-크지만-큰-무리는-없다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%A0%90%EC%9C%A0%EC%9C%A8%EC%9D%B4-%EB%8B%A4%EC%86%8C-%ED%81%AC%EC%A7%80%EB%A7%8C-%ED%81%B0-%EB%AC%B4%EB%A6%AC%EB%8A%94-%EC%97%86%EB%8B%A4&quot; aria-label=&quot;메모리 점유율이 다소 크지만 큰 무리는 없다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메모리 점유율이 다소 크지만, 큰 무리는 없다&lt;/h3&gt;
&lt;p&gt;Jemeter 의 최장점은 GUI 와 전통적인 역사 아래 풍부한 레퍼런스로 빠르게 테스트 툴 사용에 적응하고, 성능을 측정할 수 있다는 점이다. 하지만 k6 에 비해 다소 메모리를 많이 사용한다. 3개의 요청을 보내는 간단한 테스트를 기준으로, JMeter는 600MB를 사용한다. 반면에 k6는 동일한 조건에서 100MB 정도를 사용하죠. 이 장점은 k6가 더 많은 가상 사용자를 생성하고 더 많은 부하를 가할 수 있게 만들어준다.&lt;/p&gt;
&lt;p&gt;하지만 우리 팀은 그럼에도 우선적으로 Jmeter 를 사용하기로 했다. 아직 우리 서비스에 대규모 부하를 가해야하는 테스트는 발생하지 않을 것이기 때문이다. 우선 빠르게 적응할 수 있는 Jmeter 로 적응한 뒤, 향후 메모리 점유율 문제가 생겼을 때 k6 로 마이그레이션 하는 것에 큰 무리가 없을 것이라 생각하기 때문이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이 외에도 nGrinder 라는 성능 테스트 툴이 존재하나, 낮은 인지도와 커뮤니티로 인해 선정에서 제외되었다. 무엇보다 WAS 기반으로 동작하기 때문에 EC2 인스턴스에 설치해야 하는데, 이것이 하나의 허들로 작용하였다. 또한 보안그룹 문제로 Agent를 제대로 사용하지 못하는 문제가 존재한다. 그렇다고 로컬에서 직접 돌리기엔 JMeter와 차이점이 없게된다 🧐&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;jmeter-사용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jmeter-%EC%82%AC%EC%9A%A9&quot; aria-label=&quot;jmeter 사용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jmeter 사용&lt;/h2&gt;
&lt;p&gt;이렇게 우리 팀은 Jmeter 로 성능 테스트를 수행하기로 결정했다. &lt;code class=&quot;language-text&quot;&gt;jpgc-graphs-basic&lt;/code&gt; 플러그인을 설치하면 핵심 지표인 TPS를 편하게 그래프로 확인할 수 있다 😎&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e56f5099a79aabc1b288a07385e5d827/e8814/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 79.75460122699387%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAACd0lEQVR42p2SWVPaUBTH861aNwKEyA4pInam0y/oizOdju04tg9tp8tLn1oqAqJVspqNQAig/HtuAhH1pWNmfpx7/+fcs1wuly9XUKntoLpTD8kWS+CFDJIZ8UlwQjaPXKmMXJFRQno7CyGbezJcMrNNSaINs4k0dSeI1GUE8/OkbfCp/yJOKOaLoU2JWSQZmSz4RbKtVBqbyXR4gNmH3Eu4FFmifKmCnLQLoVRDuighReTKEhIpAWtbfHhgPZG8x6MOV6tk6D4LVQlioRDD7pQVY92zOGYfkginEEK4NaryfDMRV4jGS8WsJ/jYz8bn6Z9kCZawe2bdP9vYCuM4Ucxhu1BEoVxFrbGHxqvXqO29jKnWG/ScdiHV6ihUJJRf7NAzq8dI9V2UaKo8nWdPkDvY30ez2cRp9wxdQmmdQj0hWm1oZOV2B4qsQNENKKoKVdWganpsFbIMWVHRpzju67fvuLmd4zaYYm67wDjAHPSFP/TNboDBCHM/CLX5PHIs7erHNO7w6BhGX4V3KcPzfHjBDB4dHvoTDEfEeBquPdPBUDHg2QMMqbjH9NEibgXuzbsjWFcaHC+A69/AHU7hjh7jjJlvgoFhY6CacJ1RFO/PaIBJHMcdvH2PfvcKpu3D9CawCHMQhHYV0400cziD5fiwZBM2YTnjSFvEcYfHn6D/1aGbIxjuJMIJ7tYPtYXVB1NcGx7MSwPXsgXNHkO3A0r44Qv6PQ19zYNsjSGbEQpbL/cr+h0+ZHtCvgCqbFO3Fq6pEPfx8w/0qEr7XEWrp6BL3bbOZHTIti+0kFPy/fzVxgnpbN9hGsUyvX2h4oxGb/1pot/6jX/VrHpMthfEuwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e56f5099a79aabc1b288a07385e5d827/a6d36/image-2.png&quot;
        srcset=&quot;/static/e56f5099a79aabc1b288a07385e5d827/222b7/image-2.png 163w,
/static/e56f5099a79aabc1b288a07385e5d827/ff46a/image-2.png 325w,
/static/e56f5099a79aabc1b288a07385e5d827/a6d36/image-2.png 650w,
/static/e56f5099a79aabc1b288a07385e5d827/e548f/image-2.png 975w,
/static/e56f5099a79aabc1b288a07385e5d827/3c492/image-2.png 1300w,
/static/e56f5099a79aabc1b288a07385e5d827/e8814/image-2.png 1392w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@yongtae923/k6-vs-JMeter&quot;&gt;https://velog.io/@yongtae923/k6-vs-JMeter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://seongwon.dev/ETC/20220919-%EC%84%B1%EB%8A%A5%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%B6%80%ED%95%98%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8A%A4%ED%8A%B8%EB%A0%88%EC%8A%A4%ED%85%8C%EC%8A%A4%ED%8A%B8%EB%9E%80/&quot;&gt;https://seongwon.dev/ETC/20220919-%EC%84%B1%EB%8A%A5%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%B6%80%ED%95%98%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8A%A4%ED%8A%B8%EB%A0%88%EC%8A%A4%ED%85%8C%EC%8A%A4%ED%8A%B8%EB%9E%80/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loosie.tistory.com/821&quot;&gt;https://loosie.tistory.com/821&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[스프링 로컬 캐시와 TTL 을 구현한 외부 서빙 API 요청 최적화]]></title><description><![CDATA[💡 현재 포스트는 하모니 팀 기술 블로그 에 게시된 글 입니다. 외부 API 요청이 매우 느린 문제상황 우리 하모니 팀이 개발중인 모행 서비스는 여행지 하나를 클릭했을 때 그와 성향이 비슷한 여행지 리스트 1…]]></description><link>https://haon.site/spring/local-cache-custom/</link><guid isPermaLink="false">https://haon.site/spring/local-cache-custom/</guid><pubDate>Sun, 29 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/local-cache-custom/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;외부-api-요청이-매우-느린-문제상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%B8%EB%B6%80-api-%EC%9A%94%EC%B2%AD%EC%9D%B4-%EB%A7%A4%EC%9A%B0-%EB%8A%90%EB%A6%B0-%EB%AC%B8%EC%A0%9C%EC%83%81%ED%99%A9&quot; aria-label=&quot;외부 api 요청이 매우 느린 문제상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;외부 API 요청이 매우 느린 문제상황&lt;/h2&gt;
&lt;p&gt;우리 하모니 팀이 개발중인 모행 서비스는 여행지 하나를 클릭했을 때 그와 성향이 비슷한 여행지 리스트 10개를 함께 천하고 있다. 비슷한 성향의 여행지를 추천받기 위해 AI 모델을 서빙할 FastAPI 서버에게 요청을 전송하고, 그와 비슷한 여행지를 응답받고 있다. 그리고 스프링부트 서버에선 응답받은 여행지 리스트를 현재 여행지와 동일한 생활정보를 보유한 여행지들로만 필터링하고 있다. &lt;strong&gt;쉽게말해, AI 서버로부터 응답받은 결과를 계속 필터링하여 여행지 10개가 채워질 때 까지 요청을 계속 전송하는 구조&lt;/strong&gt;이다. 이런 플로우를 통해 사용자가 여행하기에 좋을 최적의 여행지들을 추천하는 시스템을 구축하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0af104421ac293a02b5cab51f5833009/29007/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 97.54601226993866%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEfElEQVR42p2U+U+TWRSG+zdOMpMYMzEZM8osDmQWEFlEoLUUKbS1tIVSdkrZQYWitmXrBiIi6sAoDmVvpdANsBvP3H5gZok/zUne7573nnPf7y7nXtnOAqy7sjwfTPH7RJY3439jdTLL60cZ5vvjEjx9J7wcjvNqIMyydZ8X/Uf4+o/x94l4X0xqZS9XlllY9BOJhMnZ2dlZ7ksmk+YodMJBOIq50YJOfpfWOhUui5LJJjU2VRm9unqq69soq2miRGXmtqoZ2bvVTZYXV9ndPCB2mOT0OC0JJ0/T7LyPEdoJ0VpRSldFMU6DgglTHS0mK0Z9J81aM49b9YzrVQzVljCg0yFLJdMkkyk+niY5Tpzw8WNScOEfH0vCRx+C1BfdYFIjZ6a1jhlrGw+7ejBXVzKkVrA42IndrMXZbmTcYkYWi0WJCsTjsQvExfIjpIRozqIHQTqqCxnTa9A2mFh6YOPNSDsNhT/TrKjmd/sIyhodQ60dLD0cFjNMpUin02LPMuT83OzO+1KSYETMsL38JyrlBvJVVtYnenlorGdEWYFNXoa3Rc2lokYUCj1rY13IlpaW8Pl8PHv2jNnZWTweD16vl+mpKVbX/iC8t01F3lWulDRToLIxYGhEV3Gbzjul2MryMVXJ+bK8m+I7eiylvyHz+/04nU6mhIDD4ZB8l8uF2+3m7bt1PuxuUvj1V5Re/5auknx6a+8yXl/LI40al76ByQYlih/yqC/4jlvXriMLh8NEozHpEBKJhPCjnJyckM1mpSUfHBxyK7+QwrwfqS24QffNYoovX6Lo8hdUX73CUGkxt/O+p+KXmxR8cw0Zn7FcLeb2VDrlwzCt6nsM6PX0a9U8bTPRVlOKsaoIk9jDEXMLLTVK+huNNMmrxSkfnRIOxQmGPrC3t8f+/t5Fcf8/kyUiSdZFca8tP2d+YYnXq285CIY42NkisL3PYSROaHeH4GaAjS3BD6OEtjfFRQiIeFDwCKGtALtb26wHds+XvPL0AWPNBmyNjax45vANdmHTaQTX88bvYbqnBatOy4go3LV5N5OtBrq1Ouy9Pax5pxk1arDqDQw3Gc4FR0cmGKpTYKqsxG53MWgdYPReJUZVHdPTXmyWDkZqKjBrjczM+OgxGBlQitJp6RTV4cHaoKa3Ri7FJcGJgWE67lZxv/BXHgh/tLOb9qpy9KUlOCaf0Gdqou1OCU3VclxPHPRoNFjKi7HU1TP12EGHSkl7ZRk1t8rPBV+9WMRhf8DblUUO93fwu2eYddoJrL3kMLjH1FM783NOdtdXRV1u8/jRKC/m5wgG3rGz8Z6JsSFWlxfYXHt1LphKi0chGSWdPSZzJh6HVEJcv4Tgp2TPxMORioucOJnsiYDgyRjpTELkngqelHgmcyyNl72fhQ0PBLzn7Z/uTzzDpicp+Rvu87iUk2t9F7mei9yLNgeZUwlTdQL3PuGMacEdigiT5Xt4jTCrFX1q8Ajf0wgzDf/M/zdksxpw6wXu/we5PjHYb4Z5C/iahd+C9IM53WfyL/AXnTYp9Yw0QwgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/0af104421ac293a02b5cab51f5833009/a6d36/image-1.png&quot;
        srcset=&quot;/static/0af104421ac293a02b5cab51f5833009/222b7/image-1.png 163w,
/static/0af104421ac293a02b5cab51f5833009/ff46a/image-1.png 325w,
/static/0af104421ac293a02b5cab51f5833009/a6d36/image-1.png 650w,
/static/0af104421ac293a02b5cab51f5833009/e548f/image-1.png 975w,
/static/0af104421ac293a02b5cab51f5833009/3c492/image-1.png 1300w,
/static/0af104421ac293a02b5cab51f5833009/29007/image-1.png 1600w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;정말 좋은 기능이지만, 말만 들어도 성능상의 이슈가 발생할 것 같다. 로컬에서 테스트한 결과, 트래픽이 급증하는 상황에서 AI 모델 서빙 서버에 한번 API 요청을 보내고 응답 받기까지 무려 &lt;strong&gt;1,300ms 가 넘는 시간이 소요&lt;/strong&gt;된다. 이는 사용자 경험 측면에서 좋지 않는 상황이다.&lt;/p&gt;
&lt;p&gt;이러한 외부 API 요청으로 인해 성능 이슈를 어떻게 개선할지 고민이 많았다. 외부 API 요청은 데이터베이스처럼 인덱싱을 통해 성능 개선이 가능한 요소도 아니다. 고민 끝에 우리 팀은 캐싱을 도입하기로 결정했다. &lt;strong&gt;외부 API 요청 내역을 스프링 로컬 캐시로 캐싱하고, 캐싱된 비슷한 성향의 여행지 데이터를 빠르게 조회하는 방식으로 성능 개선을 고안했다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;캐싱된-이전-버전의-데이터만-읽어오는-문제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BA%90%EC%8B%B1%EB%90%9C-%EC%9D%B4%EC%A0%84-%EB%B2%84%EC%A0%84%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A7%8C-%EC%9D%BD%EC%96%B4%EC%98%A4%EB%8A%94-%EB%AC%B8%EC%A0%9C&quot; aria-label=&quot;캐싱된 이전 버전의 데이터만 읽어오는 문제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;캐싱된 이전 버전의 데이터만 읽어오는 문제&lt;/h2&gt;
&lt;p&gt;스프링은 자주 사용되면서 호출이 빈번한 데이터를 메모리에 쉽게 캐싱할 수 있도록 AOP 를 통해 &lt;code class=&quot;language-text&quot;&gt;@Cacheable&lt;/code&gt; 을 제공한다. AOP 를 사용하므로 내부 구현이 영향을 주지 않고, 특정 캐시 구현체(기술) 에 종속되지 않도록 구현할 수 있다.&lt;/p&gt;
&lt;p&gt;스프링은 별다른 설정이 없다면 기본 캐시 구현체로 &lt;code class=&quot;language-text&quot;&gt;ConcurrentMapCache&lt;/code&gt; 를 택하고 있다. 이 캐시 구현체는 내부적으로 &lt;code class=&quot;language-text&quot;&gt;ConcurrentHashMap&lt;/code&gt; 을 사용하고 있는 간단한 구현체이다. 우리 팀의 경우도 초기엔 특별한 캐시 구현체를 도입해야 할 이유가 없었기 때문에, 아래처럼 간단히 &lt;code class=&quot;language-text&quot;&gt;@Cacheable&lt;/code&gt; 을 명시하고 기본 캐시 구현체인  &lt;code class=&quot;language-text&quot;&gt;ConcurrentMapCache&lt;/code&gt; 를 사용하도록 하고 있었다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Cacheable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SIMILAR_TRIP_CACHE&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#contentId+#page&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FindSimilarTripWithContentIdResponses&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findSimilarTrips&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; contentId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;requestSimilarTrips&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SimilarTripRequests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;concurrentmapcache-은-ttl-을-지원하지-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#concurrentmapcache-%EC%9D%80-ttl-%EC%9D%84-%EC%A7%80%EC%9B%90%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;concurrentmapcache 은 ttl 을 지원하지 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ConcurrentMapCache 은 TTL 을 지원하지 않는다&lt;/h3&gt;
&lt;p&gt;하지만 문제가 있었다. AI 서버로부터 응답받는 비슷한 성향의 여행지는 항상 거의 동일한 데이터를 응답받는다. 그런데, ConcurrentMapCache 는 &lt;code class=&quot;language-text&quot;&gt;TTL(Time-To-Live)&lt;/code&gt; 기능을 지원하지 않기 떄문에 유저가 항상 캐싱해놓은 여행지 추천 결과만을 계속 조회하게 되는 문제가 발생하였다. 이 떄문에 유저가 다양한 여행지를 추천받는 것이 아닌, 항상 최초로 캐싱된 데이터들만 조회하게 되는 단점이 존재한다. 이 문제를 해결하기 위해 &lt;strong&gt;캐싱된 데이터는 캐시에서 영구 저장되지 않고 일정 시간이 지났을 때 만료되어 제거되는 기능이 필요했다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;하지만 &lt;code class=&quot;language-text&quot;&gt;ConcurrentMapCache&lt;/code&gt; 는 &lt;strong&gt;TTL(Time-To-Live) 를 지원하지 않는다.&lt;/strong&gt; 따라서 캐싱된 데이터의 만료 기한을 설정할 수 없고, 한번 캐싱된 데이터를 지우기 위해 스캐쥴링을 돌려서 모든 데이터를 일괄 삭제해야한다. 그런데 우리가 캐시하려고 하는 데이터는 현재 여행지와 비슷한 성향의 여행지 리스트이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/441c320b83233b73222909531680ea70/c4842/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.78527607361963%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABfUlEQVR42oWSTW/aQBCG+f9/oOqlh7aqemmIVFW9VuohJQURYmyytlnjLxZ/plBwxId5ajuFEKWos3ovo9lnZt+dFiex3++P+l9s1mseViseiqJRWZZNvvWv4hpYFxwbUFZnd1Qd3cGQWyHR7Ql9w8RxvUfgYZpV1a1OOhOXLM/xfR/DMHA9F7vrIn+ETK4Vxvcx8/s5A9PBzQu8vLqX/saUziOw/Au8M23etL/y6uMl3RuNsW0xuBlw3e1w+9lGXESMLkI67w2yWYZmO/RNj2+dPoYXY8nJ8wmtseTt5Rdef/jEcCTqd7PblYTTgF77rgGKtuLq3ZBEpWhWNWG2xKsk4/kT8NS7JElYLBasK8NrbbdbNtsNvjklGM0IRYSjBxTLFT298i1d4v8qMFWOsOUT8DBlFEW4rouu62iahlLq7C8LSzIUNlc/e4ysMWoWvQRmWYaUsvkMIUQDTNOUOIqfKYkT7vOMmZoSBgGnjNa5PTyszjnV/pbly339AxXZp0w6wobsAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/441c320b83233b73222909531680ea70/a6d36/image-3.png&quot;
        srcset=&quot;/static/441c320b83233b73222909531680ea70/222b7/image-3.png 163w,
/static/441c320b83233b73222909531680ea70/ff46a/image-3.png 325w,
/static/441c320b83233b73222909531680ea70/a6d36/image-3.png 650w,
/static/441c320b83233b73222909531680ea70/e548f/image-3.png 975w,
/static/441c320b83233b73222909531680ea70/3c492/image-3.png 1300w,
/static/441c320b83233b73222909531680ea70/c4842/image-3.png 1634w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;만약 캐시 메모리내의 모든 데이터를 일괄 삭제해버린다면, 캐시 메모리에 올려놓고 자주 사용되는 데이터가 불필요하게 캐시 메모리에서 제거되는 문제가 발생한다. &lt;strong&gt;자주 사용되는 데이터는 캐시에서 제거되지 않고 Hit 되어야한다. 그래야 Cache Hit 비율이 높아지고, 성능이 좋아질 것이다.&lt;/strong&gt;  다시말해, 자주 사용되는 데이터가 불필요하게 캐시에서 제거되면 Miss Rate 이 높아지므로 성능 저하 이슈가 발생한다.&lt;/p&gt;
&lt;p&gt;게다가 일괄적으로 캐시 메모리내 모든 데이터를 지워버린다면, 방금전에 막 캐싱된 데이터들도 제거대상이 된다. 이러한 이유들로 &lt;strong&gt;캐싱된 데이터들은 일괄 제거방식이 아니라, 각 데이터가 개별적인 만료 기간을 가져야한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;각 데이터에 대한 개별적인 TTL 기능을 지원하기 위해 &lt;code class=&quot;language-text&quot;&gt;EnCache&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;CaffinCache&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Redis&lt;/code&gt; 와 같은 캐시 구현체를 사용할 수도 있겠지만, 서드파티에 대한 종속성이 높아지고 자칫 불필요한 비용(돈) 이 발생할 수 있다는 문제점이 있다. 외부 서드파티 구현체를 서비스에 도입하고 틱틱 붙여대면 기능 구현도 빠르게 끝나고 편리할 것이다. 하지만 이 방법은 좋지 않은 개발자 역량이자 잘못된 개선 방향이라 생각한다. 게다가 TTL 기능 하나를 구현하기 위해 외부 의존성을 추가하는 것보다, 현재 주어진 자원으로 문제를 해결하는 방법을 계속 고민하는 것이 바람직하다는 생각이 들었다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 필자가 가장 중요하게 여기는 개발자 역량 중 하나는, 불필요한 비용을 없애고(비용 절약) 외부 서드파티 구현체에 대한 종속성을 최소화하기 위해 주어진 상황과 한정적인 자원으로 최대한 문제를 해결하는 것이 개발자의 중요한 역량이라고 생각한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;concurrentmapcache-을-상속한-캐시-구현체-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#concurrentmapcache-%EC%9D%84-%EC%83%81%EC%86%8D%ED%95%9C-%EC%BA%90%EC%8B%9C-%EA%B5%AC%ED%98%84%EC%B2%B4-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;concurrentmapcache 을 상속한 캐시 구현체 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ConcurrentMapCache 을 상속한 캐시 구현체 구현&lt;/h2&gt;
&lt;p&gt;이 문제를 해결하기 위해 &lt;code class=&quot;language-text&quot;&gt;ConcurrentMapCache&lt;/code&gt; 을 상속한 구현체 &lt;code class=&quot;language-text&quot;&gt;ExternalSimilarTripCache&lt;/code&gt; 를 직접 만들어내어 TTL 을 구현하도록 했다. &lt;code class=&quot;language-text&quot;&gt;ExternalSimilarTripCache&lt;/code&gt; 는 &lt;code class=&quot;language-text&quot;&gt;put()&lt;/code&gt; 을 통해 데이터를 캐시에 등록하고, &lt;code class=&quot;language-text&quot;&gt;lookup()&lt;/code&gt; 을 통해 캐시에 담겨있는 데이터를 꺼내온다. 더 자세히 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ExternalSimilarTripCache&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConcurrentMapCache&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; tripCache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConcurrentHashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; expiresDatePoint&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ExternalSimilarTripCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; expiresDatePoint&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;expiresDatePoint &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; expiresDatePoint&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lookup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt; expiredDate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tripCache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expiredDate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isCacheValid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expiredDate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lookup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        tripCache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;evict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt; expiredDate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;plusSeconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expiresDatePoint&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        tripCache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; expiredDate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isCacheValid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt; expiredDate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isBefore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expiredDate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;우선 &lt;code class=&quot;language-text&quot;&gt;put()&lt;/code&gt; 을 오버라이드 했다. &lt;code class=&quot;language-text&quot;&gt;put()&lt;/code&gt; 은 캐시에 데이터를 저장하는 시점에 &lt;code class=&quot;language-text&quot;&gt;@Cacheable&lt;/code&gt; 을 통해 지정된 key 값을 key 로 하고, value 를 현재시간(=  LocalDateTime.now( )) 에 지정된 만료시간만큼을 더한 날짜로 하여 저장한다. 우리 팀의 경우 현재시간으로부터 2시간 이후를 만료 날짜로 하는 정책에 기반하여 &lt;code class=&quot;language-text&quot;&gt;expiredDatePoint&lt;/code&gt; 변수에 값을 주입하도록 했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt; expiredDate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;plusSeconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expiresDatePoint&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    tripCache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; expiredDate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;lookup()&lt;/code&gt; 은 캐시 메모리내에 담긴 데이터 중 원하는 데이터를 key 값을 찾아내는 기능을 가지고 있다. 원하는 데이터가 캐시내에 존재한다면, 즉 &lt;strong&gt;캐시 히트(Cache Hit)&lt;/strong&gt; 라면 캐싱된 해당 데이터를 리턴한다. 반대로 &lt;strong&gt;캐시 미스(Cache Miss)&lt;/strong&gt; 라면 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; 을 리턴한다.&lt;/p&gt;
&lt;p&gt;이러한 기능을 가진 &lt;code class=&quot;language-text&quot;&gt;lookup()&lt;/code&gt; 을 오버라이드하여, 요청한 데이터가 만료되었다면 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; 을 리턴하고 해당 데이터를 캐시에서 제거한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lookup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt; expiredDate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tripCache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expiredDate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isCacheValid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expiredDate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lookup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    tripCache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;evict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;캐시-구현체-등록&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BA%90%EC%8B%9C-%EA%B5%AC%ED%98%84%EC%B2%B4-%EB%93%B1%EB%A1%9D&quot; aria-label=&quot;캐시 구현체 등록 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;캐시 구현체 등록&lt;/h2&gt;
&lt;p&gt;이렇게 구현한 &lt;code class=&quot;language-text&quot;&gt;ExternalSimilarTripCache&lt;/code&gt; 캐시 구현체를 서비스에서 사용하도록 등록해주도록 한다. &lt;code class=&quot;language-text&quot;&gt;@EnableCaching&lt;/code&gt; 을 명시하고, &lt;code class=&quot;language-text&quot;&gt;SimpleCacheManager&lt;/code&gt; 을 통해 만든 케시를 등록해주자.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;SimpleCacheManager&lt;/code&gt; 는 사용할 캐시를 직접 등록해줄 때 사용하는 캐시 매니저이다. 보통 캐시 클래스를 직접 만드는 경우 사용하기에 적당하다. 또한 &lt;code class=&quot;language-text&quot;&gt;LIFE_CYCLE&lt;/code&gt; 을 통해 앞선 만료시간을 지정해줬는데, 설명했듯이 우리 팀의 경우 2시간을 만료기간으로 지정했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@EnableCaching&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CacheConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;EXTERNAL_SIMILAR_TRIP_CACHE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SIMILAR_TRIP_CACHE&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;LIFE_CYCLE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CacheManager&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cacheManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;SimpleCacheManager&lt;/span&gt; simpleCacheManager &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SimpleCacheManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        simpleCacheManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCaches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ExternalSimilarTripCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EXTERNAL_SIMILAR_TRIP_CACHE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;LIFE_CYCLE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; simpleCacheManager&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;캐시-만료시간을-어느정도로-해야할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BA%90%EC%8B%9C-%EB%A7%8C%EB%A3%8C%EC%8B%9C%EA%B0%84%EC%9D%84-%EC%96%B4%EB%8A%90%EC%A0%95%EB%8F%84%EB%A1%9C-%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;캐시 만료시간을 어느정도로 해야할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;캐시 만료시간을 어느정도로 해야할까?&lt;/h2&gt;
&lt;p&gt;정말 많은 고민이 있었던 요소이다. 캐싱된 데이터의 만료기간을 어느정도로 해야 적당할까? 결론적으로 우리 팀은 앞서 계속 설명했듯이 2시간으로 설정했다. 우선 AI 서빙 서버는 데이터베이스와 같이 완벽히 제어할 수 있는 요소가 아니다. 따라서 비슷한 여행지 응답 결과가 올바른지에 대해 알 수 없다.&lt;/p&gt;
&lt;p&gt;따라서 캐시 만료기간을 너무 길게 설정하면 유저가 자칫 잘못된 성향의 비슷한 여행지를 볼 수도 있다. 그렇다고 만료기간을 너무 짧게 잡자니 캐시 미스가 자주 발생해서 성능이 저하될 수 있다. 너무 길지도 않고, 너무 짧지도 않은 적당한 만료 기간이 필요하다.&lt;/p&gt;
&lt;p&gt;기본적으로 AI 알고리즘을 수행했을 때, 매우 높은 확률로 정확하게 비슷한 성향의 알고리즘을 추천해준다. 또한 잘 설계된 알고리즘 덕분에, 현재 여행지와 비슷한 여행지를 추천받은 결과는 항상 같은 데이터셋을 제공받는다. 거의 변하지 않는 특성 때문에 캐싱을 도입한 것이기도 하다. 이런 점을 고려했을 떄 캐시 만료 기간을 너무 짧게 설정하면 캐시 미스가 자주 발생하고 성능이 저하될 것이다. 따라서 임의로 2시간 정도의 만료 기간이 적합하다고 판단했다. 다만, 우리 서비스를 실제로 테스트 및 운영하면서 이 만료 정책이 최선일지는 더 보강해야할 점이라고 생각한다.&lt;/p&gt;
&lt;h2 id=&quot;매일-자정-스캐쥴링을-통해-만료된-데이터-캐시에서-제거&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%A4%EC%9D%BC-%EC%9E%90%EC%A0%95-%EC%8A%A4%EC%BA%90%EC%A5%B4%EB%A7%81%EC%9D%84-%ED%86%B5%ED%95%B4-%EB%A7%8C%EB%A3%8C%EB%90%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%BA%90%EC%8B%9C%EC%97%90%EC%84%9C-%EC%A0%9C%EA%B1%B0&quot; aria-label=&quot;매일 자정 스캐쥴링을 통해 만료된 데이터 캐시에서 제거 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;매일 자정 스캐쥴링을 통해 만료된 데이터 캐시에서 제거&lt;/h2&gt;
&lt;p&gt;그런데 문제점이 아직 남아있다. 캐싱된 데이터 중에 만료기간이 지났음에도 해당 데이터를 사용하기 대한 재요청이 발생하지 않는다면 영원에 캐시 메모리에 남아있다는 점이다. 따라서 해당 데이터는 캐시 메모리에서 영원히 불필요히게 공간을 점유하고 있게된다. 이를 위해 스프링에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;@Scheduled&lt;/code&gt; 로 스캐쥴링하여, 매일 자정 만료된 캐시를 지우도록 구현했다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;evictAllTrips()&lt;/code&gt; 을 수행하여 유효하지 않은 캐시 데이터, 즉 만료된 데이터를 제거하도록 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ExternalSimilarTripCache&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConcurrentMapCache&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; tripCache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConcurrentHashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; expiresDatePoint&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ExternalSimilarTripCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; expiresDatePoint&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;expiresDatePoint &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; expiresDatePoint&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;evictAllTrips&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ConcurrentMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; currentCache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getNativeCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        currentCache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keySet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isCacheValid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tripCache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;evict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@Scheduled&lt;/code&gt; 로 매일 자정마다 &lt;code class=&quot;language-text&quot;&gt;evictAllTrips()&lt;/code&gt; 을 실행하여 만료된 모든 캐시를 제거하도록 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@EnableCaching&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@EnableScheduling&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CacheConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;EXTERNAL_SIMILAR_TRIP_CACHE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SIMILAR_TRIP_CACHE&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;LIFE_CYCLE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Scheduled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cron &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0 0 0 * * *&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;evict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ExternalSimilarTripCache&lt;/span&gt; externalSimilarTripCache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ExternalSimilarTripCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token function&quot;&gt;cacheManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EXTERNAL_SIMILAR_TRIP_CACHE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        externalSimilarTripCache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;evictAllTrips&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;최적화를 진행하기전에는 비슷한 여행지 조회 API 실행시 평균적으로 580ms 가량의 실행시간이 걸렸다. 운이좋게 가장 빠른 경우는 약 430ms 가량의 성능이 보이기도 했다. 반면 로컬 캐시를 도입한 이후 평균 응답시간이 약 120ms 가량으로 감소했다. &lt;strong&gt;캐싱 적용전 대비 약 4.8배 성능이 개선되었다&lt;/strong&gt; 😎&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html&quot;&gt;https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/cache/concurrent/ConcurrentMapCache.html&quot;&gt;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/cache/concurrent/ConcurrentMapCache.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jiwondev.tistory.com/282&quot;&gt;https://jiwondev.tistory.com/282&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/optimizing-external-calendar-api-calling-with-spring-local-cache/&quot;&gt;https://hudi.blog/optimizing-external-calendar-api-calling-with-spring-local-cache/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loosie.tistory.com/806&quot;&gt;https://loosie.tistory.com/806&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bcp0109.tistory.com/385&quot;&gt;https://bcp0109.tistory.com/385&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@yyong3519/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-Cache-%EC%82%AC%EC%9A%A9-4&quot;&gt;https://velog.io/@yyong3519/스프링부트-Cache-사용-4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://parkmuhyeun.github.io/woowacourse/2023-09-09-Concurrent-Hashmap/&quot;&gt;https://parkmuhyeun.github.io/woowacourse/2023-09-09-Concurrent-Hashmap/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[JPA 비관적 락(Pessimistic Lock) 을 사용한 동시성 이슈 해결기]]></title><description><![CDATA[💡 현재 포스트는 하모니 팀 기술 블로그 에 게시된 글 입니다. 우리 하모니 팀의 모행 프로젝트에서 발생한 동시성 이슈가 발생하고 있었다. 이를 JPA 의 비관적 락을 사용하여 해결한 경험에 대해 팀원들에게 공유하고자 한다 😎 JPA…]]></description><link>https://haon.site/database/pessmistic-lock/</link><guid isPermaLink="false">https://haon.site/database/pessmistic-lock/</guid><pubDate>Fri, 27 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/pessmistic-lock/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;우리 하모니 팀의 모행 프로젝트에서 발생한 동시성 이슈가 발생하고 있었다. 이를 JPA 의 비관적 락을 사용하여 해결한 경험에 대해 팀원들에게 공유하고자 한다 😎&lt;/p&gt;
&lt;h2 id=&quot;jpa-낙관적-락-비관적-락&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jpa-%EB%82%99%EA%B4%80%EC%A0%81-%EB%9D%BD-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BD&quot; aria-label=&quot;jpa 낙관적 락 비관적 락 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JPA 낙관적 락, 비관적 락&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 JPA 낙관적 락, 비관적에 대한 자세한 이론은 &lt;a href=&quot;https://haon.blog/database/optimistic-pessimistic-lock/&quot;&gt;JPA 낙관적 락과 비관적 락으로 엔티티에 대한 동시성 이슈 해결하기&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;지난 &lt;a href=&quot;https://haon.blog/database/optimistic-pessimistic-lock/&quot;&gt;JPA 낙관적 락과 비관적 락으로 엔티티에 대한 동시성 이슈 해결하기&lt;/a&gt; 에서 다루었듯이, JPA 의 락 메커니즘은 특정 엔티티에 락을 거는 기법이다. 모든 트랜잭션은 ACID 성질에 따라 &lt;strong&gt;원자성(Atomic) 과 격리성(Isolation)&lt;/strong&gt; 하게 동작해야한다. 이를위해 트랜잭션 격리수준을 최고 수준으로 설정하여 동시성 이슈를 해결할 수 있지 않을까 생각해볼 수 있지만, 애당초 격리수준은 동시성 이슈를 해결하기 위해 등장한 개념이 아니다. 격리수준을 최고 단계인 &lt;code class=&quot;language-text&quot;&gt;SERIALIZABLE&lt;/code&gt; 로 올리면 락을 획득할 필요가 없는 기능들의 모든 트랜잭션에 &lt;code class=&quot;language-text&quot;&gt;s-lock&lt;/code&gt; 이 걸린다. 이 떄문에 서비스내의 몰론 데드락에 빠질 위험도 훨씬 커진다. 무엇보다, &lt;strong&gt;트랜잭션 격리수준을 자칫 잘못 설정했다간 락을 획득할 필요가 없는 모든 쓰레드가 s-lock 을 획득하여 심각한 성능 저하가 발생할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;우리 팀은 왜 동시성 이슈를 제어하기 위해 JPA 비관적 락을 사용했는가?&lt;/p&gt;
&lt;h2 id=&quot;여행지-방문-수-기록&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%AC%ED%96%89%EC%A7%80-%EB%B0%A9%EB%AC%B8-%EC%88%98-%EA%B8%B0%EB%A1%9D&quot; aria-label=&quot;여행지 방문 수 기록 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;여행지 방문 수 기록&lt;/h2&gt;
&lt;p&gt;우리 하모니 팀의 모행 서비스에는 각 여행지에 대한 총 방문 수를 기록하고 있다. 이 방문 수를 기준으로 상위 30개의 여행지를 사용자에게 추천하고 있다. 랜딩 페이지에서 비회원에게 우리 서비스 사용을 권장하기 위해, 어떤 서비스인지를 표현할 수 있도록 아래와 같이 랜덤 키워드 기반 여행지를 추천할 떄, 상위 30개의 여행지를 방문 수 기준 오름차순으로 보여주고 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fb163fdd24776502385f502f4045d3f8/5fada/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.49079754601227%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB2klEQVR42m2S+0/SYRTG+aONcnNzViqNbNWc/iDhF0XxhnKbXFXE28ivaUwREYdgLSpUboF8b5/eQGu1nu1s5+x59pz3PXtMrYZC6XOD759uOM/lOclkyWbPuSoWUHRQdQNNU1FUjbbWnXVV4U7VRY/gNHTBt1QDAzCJIr0dIzw7RcC1wPnHI/b9bgLOGaIeL1k5SdRpZ8kusZ9IcBwP45cm8UzPkPkgs7viYkVyEFpy02o2u4Zhj58F6yB2yzDbGwmWbRPMDfWz8E5id2OLxTcvcQwOEPWvEvH4WLQ+w2G1srO5g9duY94ywPT4BK1GvWvomV9maqCPkUdmAr4QztExbL09jL8YIbIaQxp6zpi5B5c0i2fOjaO/l1ePnxAOxnC+HWWyz8zrpxaqlcr9lw9l1oNe0skEpWKerbUwyXiEQvqIy1yGtaCP1F6cL/ksqYMk8ZCfs4M9SleXbK9HeL8Z5SIlo7TbXcNatcJ1+RuqcocqDl4WfaNeFQdXaDZ/CO4ritJENzSq1Vtub8pC1+5orx+0YjYMo2v4P6iqSi53QaFQJHV0yqF8Qub0TCQhR61W72h+GfwL0z3Trd8QETB08SoRj1Ybn+2YqWFZnKOCpiudZX+kf5v+BMcXL2yfZPzWAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/fb163fdd24776502385f502f4045d3f8/a6d36/image.png&quot;
        srcset=&quot;/static/fb163fdd24776502385f502f4045d3f8/222b7/image.png 163w,
/static/fb163fdd24776502385f502f4045d3f8/ff46a/image.png 325w,
/static/fb163fdd24776502385f502f4045d3f8/a6d36/image.png 650w,
/static/fb163fdd24776502385f502f4045d3f8/e548f/image.png 975w,
/static/fb163fdd24776502385f502f4045d3f8/3c492/image.png 1300w,
/static/fb163fdd24776502385f502f4045d3f8/5fada/image.png 1706w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;또한 최초 회원가입시 관심 여행지를 선택하도록 유도하고 있다. 입력한 관심 여행지를 기반으로 AI 맞춤 추천 여행지를 추천하기 떄문이다. 이때, 사용자가 선택할 관심 여행지 리스트를 보여주기 위해 마찬가지로 상위 30개의 여행지를 오름차순으로 보여주고 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a16847b9c826427288e1b3a6b022ddd8/dd104/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 76.07361963190185%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAACvElEQVR42n2TjU9SYRTG/YfLlW2t5nK1+TE0FNvS/BYH+BGpJMzUBIHyY1JpAoogoMQNCT8gBbnc++vlhk3RPNvZ+z7vs/PsnPPcWyMFSiQ2Zcrnz6BKdD1L0COx/SlF0Cux8zmlYb/7B6HltJZBT5KAO0nUl9NqpIDyL2uoCpUihcJvLi7OyJ9nyefPKIh7IZ8Td/FezAv+HFk+R1WL1eXUqIoQUTQlIZDnYDdEMrSD3x8mKaVJhCMcbAfwb4n3H4cko1H2A36CgV0SCQkq9ZepdaiqqqaePUoz12fA/KoTc2cHG24XblMvZoN4M7Sz4XHiHR/CYujA3KFnef4DanWHVwVT6Qz9XQPYDc10NbfhWvRgNo5i0zfS3fKSpUU3FpOVmfYmupp0zDnmbo58VTDzK81QWxumxgb0dXXYJ6ex9vRgfF5P+6M6HFM2pgYHMb6oxyDw5OhYZfHqdUEqjRfOcyzNO/A4Z9n9usxhIorXtYB7wUF4Y5WMtM+yGNs5ZyeyuUY8HEQulW52eBnFizyxyA7SfoTD9CGn2VMOYmGBw2IdKbI5geMRkvFd0oI/PjmqOFotWGn5LHuCd2yYmaEBLG96+eL1sGYbZbqvF0vXG3wC++xWbP09mF93s7rk4urKbuzw5PiYd0YjtldtjLTrWXUtYZ94y5RBx4hez4rAjndTgm9lsFXHitN5t2Dm6IjXOgOW509pvv+AsVErI90DjDx7TEvtQybGJzH1DWNqeELzvVrGTH9NURTl9pHzv7PMTpiZNg2yOGEi9M3Hx/eTGvZOj2vYPTuDdbhP4wPrK7d3eBmy+K3CIT/xvR1yp2nh+inRyDZb332kpDjFQo6o4PaEu2X+LJcRYsr/XS7JF8I0GaUka46Xsxyx6B6x2J7GqYJTxCkLrpx3CpZHVyt5Fd/Kq/8+32uCfwCUuDNqg+RVwgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/a16847b9c826427288e1b3a6b022ddd8/a6d36/image-1.png&quot;
        srcset=&quot;/static/a16847b9c826427288e1b3a6b022ddd8/222b7/image-1.png 163w,
/static/a16847b9c826427288e1b3a6b022ddd8/ff46a/image-1.png 325w,
/static/a16847b9c826427288e1b3a6b022ddd8/a6d36/image-1.png 650w,
/static/a16847b9c826427288e1b3a6b022ddd8/e548f/image-1.png 975w,
/static/a16847b9c826427288e1b3a6b022ddd8/dd104/image-1.png 1064w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;trip-엔티티&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#trip-%EC%97%94%ED%8B%B0%ED%8B%B0&quot; aria-label=&quot;trip 엔티티 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Trip 엔티티&lt;/h3&gt;
&lt;p&gt;실제로 우리 팀의 Trip 엔티티 설계 코드이다. &lt;code class=&quot;language-text&quot;&gt;visitedCount&lt;/code&gt; 라는 컬럼으로 모든 유저가 현재 여행지에 방문한 총 방문 수를 기록하고있다. 또한 &lt;code class=&quot;language-text&quot;&gt;increaseVisitedCount()&lt;/code&gt; 를 사용하여 여행지 조회시 방문 수를 1증가시키도록 비즈니스 로직을 설계했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;trip&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseEntity&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MAX_NAME_LENGTH&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MIN_NAME_LENGTH&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GeneratedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenerationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;visited_count&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nullable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; visitedCount&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;incrementVisitedCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;visitedCount&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;문제-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%A0%9C-%EC%83%81%ED%99%A9&quot; aria-label=&quot;문제 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문제 상황&lt;/h2&gt;
&lt;p&gt;그런데 문제가 있다. 이상하게도 실제 유저들이 방문한 횟수와 달리 올바르게 방문 수가 증가하지 않고, 이로인해 상위 30개의 여행지가 비정상적으로 조회되고 있다. 즉, 방문 수 카운팅 로직에 동시성 이슈가 발생하고 있었다. 100명의 유저가 같은 여행지를 동시에 방문했을 떄 방문 수가 100이 증가하지 않고, 이에 훨씬 못미치는 수준으로 증가하고 있었다.&lt;/p&gt;
&lt;p&gt;이는 &lt;a href=&quot;https://haon.blog/database/race-condition-pattern/&quot;&gt;경쟁 상태의 2가지 패턴 - Read-Modify-Write, Check-Then-Act&lt;/a&gt; 에서 다루었던 경쟁상태의 대표적인 형태 중 &lt;strong&gt;Read-Modity-Write&lt;/strong&gt; 패턴이다. &lt;strong&gt;즉, visitedCount 공유 자원에 대해 이전 상태를 기준으로 객체의 현재 상태를 변경하면서 문제가 발생한 것이다.&lt;/strong&gt; 쓰레드1 이 데이터를 읽어오고(Ready) 변경(Modify) 을 시도하고 반영(Write) 하기 이전에, 다른 쓰레드2 가 아직 반영(Write) 되지 않은 옛날 값을 읽어옴으로써 데이터 정합성 문제가 발생한 것이다.&lt;/p&gt;
&lt;p&gt;이 동시성 문제를 어떻게 해결할 수 있을까? 우리 팀은 크게 트랜잭션 격리수준, 자바에서 제공하는 키워드인 &lt;code class=&quot;language-text&quot;&gt;synchornized&lt;/code&gt;, JPA 낙관적 락, 그리고 비관적 락 중 어떤 방법으로 선택할지 많은 고민이 있었다. 결론적으로는, JPA 락 메커니즘에서 제공하는 비관적 락을 사용하여 동시성 이슈를 해결했는데, 이 고민 과정을 설명해보고자 한다.&lt;/p&gt;
&lt;h3 id=&quot;트랜잭션-격리-수준으로-해결하기엔-좋지-못한-방법이다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B2%A9%EB%A6%AC-%EC%88%98%EC%A4%80%EC%9C%BC%EB%A1%9C-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0%EC%97%94-%EC%A2%8B%EC%A7%80-%EB%AA%BB%ED%95%9C-%EB%B0%A9%EB%B2%95%EC%9D%B4%EB%8B%A4&quot; aria-label=&quot;트랜잭션 격리 수준으로 해결하기엔 좋지 못한 방법이다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 격리 수준으로 해결하기엔 좋지 못한 방법이다&lt;/h3&gt;
&lt;p&gt;지난 이론 설명 포스팅과 앞서 설명했듯이, &lt;strong&gt;트랜잭션 격리수준 REPEATABLE READ 으로는 두 번의 갱실 분실 문제(Second Lost Updates Problem) 를 해결할 수 없다.&lt;/strong&gt; 무엇보다, 가장 높은 격리성을 보장하는 &lt;code class=&quot;language-text&quot;&gt;SERIALIZABLE&lt;/code&gt; 은 현재 락을 획득할 필요가 없는 모든 트랜잭션이 최소한 &lt;code class=&quot;language-text&quot;&gt;s-lock(공유 락)&lt;/code&gt; 을 획득하게 된다. 위 문제상황에선 여행지 조회시 &lt;code class=&quot;language-text&quot;&gt;visitedCount&lt;/code&gt; 컬럼에 대한 기능에 대해서만 락을 걸면 될텐데, 만약 &lt;code class=&quot;language-text&quot;&gt;SERIALIZABLE&lt;/code&gt; 로 격리수준을 올려버린다면 이 기능과 연관없는 서비스내의 전체 기능에 대해서 락을 걸어버리게 된다. 즉, 시스템내에 생성되는 모든 트랜잭션은 불필요하게 락을 획득하기 위해 대기하게 되고, 자칫 데드락에 빠질 수 있다. &lt;code class=&quot;language-text&quot;&gt;x-lock&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;s-lock&lt;/code&gt; 은 서로 양립할 수 없기 떄문에, 데드락에 빠질 가능성이 더 커진다.&lt;/p&gt;
&lt;h3 id=&quot;자바에서-제공하는-synchornized-로도-해결하기엔-비효율적이다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EB%B0%94%EC%97%90%EC%84%9C-%EC%A0%9C%EA%B3%B5%ED%95%98%EB%8A%94-synchornized-%EB%A1%9C%EB%8F%84-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0%EC%97%94-%EB%B9%84%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B4%EB%8B%A4&quot; aria-label=&quot;자바에서 제공하는 synchornized 로도 해결하기엔 비효율적이다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자바에서 제공하는 synchornized 로도 해결하기엔 비효율적이다.&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FindTripWithSimilarTripsResponse&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findWithSimilarOtherTrips&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; tripId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; memberId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt; trip &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tripRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    trip&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;incrementVisitedCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;자바 차원에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;synchorized&lt;/code&gt; 로 해결하자니, 이 또한 성능한 손해를 많이 본다. 자바에선 &lt;code class=&quot;language-text&quot;&gt;synchronized&lt;/code&gt;  라는 키워드를 제공해, &lt;strong&gt;모니터 기반으로 상호 배제(Mutual Exclusion) 기능을 제공한다.&lt;/strong&gt; 상호배제를 구현한 동시성 제어 방식은 공유 자원 그 자체를 사용하는 특정 &lt;code class=&quot;language-text&quot;&gt;임계 영역(Critial Section)&lt;/code&gt; 에 대해 동시성을 제어할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;즉, 한 임계영역에 대해 쓰레드를 하나씩 진입시켜 작업을 수행시키는 방법으로, 베타적으로 실행된다. 하지만 이 특성 떄문에 서비스에 트래픽이 발생할 경우 심각한 성능 저하가 발생할 수 있다.&lt;/strong&gt;  JPA 락 메커니즘과 달리 한 엔티티에 대한 접근을 제어하는 것과 달리, synchorized 는 여행지 조회라는 로직 그 자체를 실행하는 모든 쓰레드에 대해 접근을 제어하는 것이므로, 성능상 더 손해를 입게된다.&lt;/p&gt;
&lt;h3 id=&quot;jpa-낙관적-락으로도-해결할-수-없다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jpa-%EB%82%99%EA%B4%80%EC%A0%81-%EB%9D%BD%EC%9C%BC%EB%A1%9C%EB%8F%84-%ED%95%B4%EA%B2%B0%ED%95%A0-%EC%88%98-%EC%97%86%EB%8B%A4&quot; aria-label=&quot;jpa 낙관적 락으로도 해결할 수 없다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JPA 낙관적 락으로도 해결할 수 없다&lt;/h3&gt;
&lt;p&gt;낙관적 락은 애플리케이션 레벨에서 동시성 이슈를 해결할 수 있는 방법이다. JPA 비관적 락 메커니즘과 달리, 대부분의 트랜잭션이 충돌이 발생하지 않을 것이라 낙관적인 상황을 가정하는 방법이다. &lt;code class=&quot;language-text&quot;&gt;@Verison&lt;/code&gt; 을 활용하여 버전 필드를 사용하고, 아래처럼 엔티티 버전 값을 기반으로 찾는 방식이라고 이전 포스팅에서 설명했었다.&lt;/p&gt;
&lt;p&gt;낙관적 락을 사용하면 엔티티의 변경감지를 version 필드를 통해 알아낸다. 다른 트랜잭션에 의해 버전 값이 증가했다면 WHERE 문으로 찾을 수 없다. 즉, 버전의 불일치가 발생하여 예외가 발생한다. 이는 다시말해, 먼저 커밋된 (버전 값을 먼저 증가시킨) 트랜잭션만을 성공시킨다. &lt;strong&gt;다시 말해, 충돌이 발생한 트랜잭션에 대해선 모두 롤백시킨다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;낙관적 락은 애당초 충돌이 거의 발생하지 않는 상황에서만 사용하는 것이 바람직하다. &lt;strong&gt;충돌이 빈번한 로직에서 엔티티에 충돌이 발생하면, 모든 트랜잭션을 롤백했을 때의 비용이 매우 크기 떄문이다.&lt;/strong&gt; 만약 100개의 쓰레드가 충돌이 발생했다면 맨 처음으로 커밋된 1개의 트랜잭션만이 인정되고, 나머지 99개의 트랜잭션을 모두 롤백시켜줘야한다. 스프링 차원에서 &lt;code class=&quot;language-text&quot;&gt;@Retryable&lt;/code&gt; 어노테이션을 지원하여 해당 메소드에 대한 재시도 기능을 제공하지만, 얼만큼 재시도 할지에 대한 기준을 세우기 명확하지 않다. &lt;strong&gt;재시도 비용 또한 오버헤드가 크게 발생하며, 재시도를 했을 떄도 최초 커밋에 실패한 트랜잭션들 모두에 대해 또 다시 롤백시켜줘야한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;jpa-비관적-락을-사용한-동시성-이슈-해결&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jpa-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BD%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0&quot; aria-label=&quot;jpa 비관적 락을 사용한 동시성 이슈 해결 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JPA 비관적 락을 사용한 동시성 이슈 해결&lt;/h2&gt;
&lt;p&gt;우리 팀은 결국 JPA 에서 제공하는 비관적 락을 사용하기로 결정했다. 낙관적 락과 반대로 충돌이 자주 발생할 것이라 비관적으로 가정하고 우선적으로 락을 걸고보는 방법이다. 낙관적 락은 데이터베이스에 락을 걸지 않는 반면, &lt;strong&gt;비관적 락은 데이터베이스의 실제로 특정 행(엔티티)에 대해 락을 거는 방식이다.&lt;/strong&gt; 이 떄문에 충돌이 거의 발생하지 않는 상황에선 낙관적 락을 사용하는 것이 오버헤드가 적다.&lt;/p&gt;
&lt;p&gt;하지만, 여행지 조회 기능 특성상 충돌이 자주 발생할 수 밖에 없다. 서비스 목적 상 여행지를 조회하는 것은 서비;스내의 대부분의 기능에서 노출되어있다. 앞서 낙관적 락을 사용했을 떄 &lt;code class=&quot;language-text&quot;&gt;@Retryable&lt;/code&gt; 로 재시도할 수 있다고 설명했지만, 커밋에 실패한 모든 트랜잭션에 대해 처리해줘야하는 롤백 비용, 그리고 재시도 비용으로 인한 오버헤드가 매우 클 것으로 생각했다.&lt;/p&gt;
&lt;h3 id=&quot;엔티티에-비관적-락-적용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%94%ED%8B%B0%ED%8B%B0%EC%97%90-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BD-%EC%A0%81%EC%9A%A9&quot; aria-label=&quot;엔티티에 비관적 락 적용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;엔티티에 비관적 락 적용&lt;/h3&gt;
&lt;p&gt;우리 팀은 아래와 같이 Repository 레이어의 여행지 조회 메소드에 비관적 락을 적용했다. 비관적 락은 LockModeType 을 &lt;code class=&quot;language-text&quot;&gt;PESSIMISTIC_WRITE&lt;/code&gt; 으로 지정하여 적용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LockModeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PESSIMISTIC_WRITE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@QueryHints&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@QueryHint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;javax.persistence.lock.timeout&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;SELECT t FROM Trip t WHERE t.id IN :tripId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Trip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findByIdForUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; tripId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;PESSIMISTIC_WRITE&lt;/code&gt; 옵션은 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;FOR UPDATE&lt;/code&gt; 를 추가하여 베타 락을 건다. 베타 락을 사용하여 다른 트랜잭션에 의해 읽기와 쓰기 연산이 모두 수행되지 않음을 보장한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; trip &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ? &lt;span class=&quot;token keyword&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;UPDATE&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;성능상-손해를-많이-볼까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B1%EB%8A%A5%EC%83%81-%EC%86%90%ED%95%B4%EB%A5%BC-%EB%A7%8E%EC%9D%B4-%EB%B3%BC%EA%B9%8C&quot; aria-label=&quot;성능상 손해를 많이 볼까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;성능상 손해를 많이 볼까?&lt;/h3&gt;
&lt;p&gt;JPA 비관적 락을 사용할 경우 &lt;code class=&quot;language-text&quot;&gt;x-lock&lt;/code&gt; 을 사용하기 때문에 다른 트랙잭션들은 락을 얻기위해 대기상태에 빠진다. 이 대기시간이 길어질수록 성능상 손해를 볼 수 밖에 없다. 그렇다고 낙관적 락을 걸자니 충돌이 자주 발생할 기능이라 적합하지 않다고 판단했다. 재시도 비용이 더 클 것으로 예상됐기 떄문이다.&lt;/p&gt;
&lt;p&gt;무엇보다 MySQL 기준 &lt;code class=&quot;language-text&quot;&gt;REPEATABLE_READ&lt;/code&gt; 격리수준에선 잠금 없는 읽기 기능이 지원된다. 락을 획득할 필요가 없는 위 기능을 제외한 나머지 기능들은 모두 성능에 손해를 입지 않는다. 또한 테이블 단위가 아닌, 엔티티 단위에 대해 락을 거는 것이다. 충돌이 발생할 특정 엔티티 외의 다른 엔티티에 대해서는 락을 걸지 않아 성능을 엄청나게 떨어뜨리는 수준은 아닐것이다.&lt;/p&gt;
&lt;h3 id=&quot;데드락에-대한-대처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EB%93%9C%EB%9D%BD%EC%97%90-%EB%8C%80%ED%95%9C-%EB%8C%80%EC%B2%98&quot; aria-label=&quot;데드락에 대한 대처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데드락에 대한 대처&lt;/h3&gt;
&lt;p&gt;앞서 Spring Data JPA 를 통해서 정의한 &lt;code class=&quot;language-text&quot;&gt;findByIdForUpdate()&lt;/code&gt; 을 보면 @Lock(LockModeType.PESSIMISTIC_WIRTE) 로 건 것을 확인할 수 있다. 우리는 동시성 제어한 필요한 곳에서 스터디를 조회할 때 해당 메소드를 활용해주고 있는데, @Lock(LockModeType.PESSIMISTIC_WIRTE) 의 설명을 살펴보면 &quot;동시 업데이트 트랜잭션 중 데드락 또는 실패 가능성이 높다.&quot; 라고 데드락 발생 가능성에 대해서 경고하고 있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cb1124fb42932a284f416b0574616fcc/e515d/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 11.042944785276074%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAYAAABYBvyLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAX0lEQVR42kWKUQ6AMAhDPc9gDMZMdOr9j1VhMfHjpS2PbTwdfRr6afA7U7HHLbtPRXoP7+kj7dD141f0SDGB9A+t2JoLWg6r0NGQO6XuwRBwZRDRgpkXxNlpZSk/VAgv6tU+gu5R0OUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/cb1124fb42932a284f416b0574616fcc/a6d36/image-2.png&quot;
        srcset=&quot;/static/cb1124fb42932a284f416b0574616fcc/222b7/image-2.png 163w,
/static/cb1124fb42932a284f416b0574616fcc/ff46a/image-2.png 325w,
/static/cb1124fb42932a284f416b0574616fcc/a6d36/image-2.png 650w,
/static/cb1124fb42932a284f416b0574616fcc/e548f/image-2.png 975w,
/static/cb1124fb42932a284f416b0574616fcc/3c492/image-2.png 1300w,
/static/cb1124fb42932a284f416b0574616fcc/e515d/image-2.png 1430w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;예를 들면 다음과 같은 상황이 될 수 있겠다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;쓰레드 1 : A 정보를 구하고 잠금
쓰레드 2 : B 정보를 구하고 잠금
쓰레드 1 : B 정보를 구하려고 하지만 잠겨있음
쓰레드 2 : A 정보를 구하려고 하지만 잠겨있&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이런 교착 상태를 해결하기 위한 방법으로는 락을 잡고 있는 최대 시간을 지정하기 위해, (잠금 시간 초과 설정, Setting Lock Timeout) &lt;code class=&quot;language-text&quot;&gt;@QueryHints&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;@QueryHint&lt;/code&gt; 어노테이션을 통해서 최대 시간을 3초로 지정했다.&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이렇게 해서 동시성 문제에 대해서 해결을 해줄 수 있게 되었다. 또한 비관적 락을 사용함으로써 발생하는 데드락 문제와 같은 것에 대해서도 대비를 해줄 수 있었다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[고가용성과 확장성을 확보하기 위한 아키텍처 전략]]></title><description><![CDATA[…]]></description><link>https://haon.site/network/high-availability-architecture/</link><guid isPermaLink="false">https://haon.site/network/high-availability-architecture/</guid><pubDate>Wed, 25 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/high-availability-architecture/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;하모니 팀의 모행 서비스를 개발하며 어떻게 했을 떄 견고하고 좋은 아키텍처인지를 고민하고 있다. 무엇을 기준으로 좋은 시스템을 설계했는지 판단할 명확한 지표가 필요했는데, 아키텍처에서 중요하게 여길 지표가 바로 &lt;strong&gt;가용성(Availability)&lt;/strong&gt; 임을 알게 되었다. 사실 가용성이란 용어를 얼핏 듣긴 했지만, 생각이 정교화되지 않아 이번 포스트에서 자세히 다루어보고자 한다.&lt;/p&gt;
&lt;h2 id=&quot;가용성availability&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%80%EC%9A%A9%EC%84%B1availability&quot; aria-label=&quot;가용성availability permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;가용성(Availability)&lt;/h2&gt;
&lt;p&gt;가용성이란 &lt;strong&gt;서비스를 운영하는 전체 기간 중 서비스가 정상적으로 살아서 동작하는 비율이 얼마나 되는지를 나타내는 지표&lt;/strong&gt;다. 이 떄문에 가용성은 시스템의 신뢰도를 평가할 때 자주 사용된다.
가용성이 높다는 것은 그만큼 서비스가 중단되지 않고 정상 운영되며, 신뢰할만한 시스템이라는 뜻이 된다. 가용성은 아래와 같이 수식으로 표현된다. &lt;strong&gt;업타임(uptime)&lt;/strong&gt; 이란 서비스 정상 이용 가능 시간, &lt;strong&gt;다운타임(downtime)&lt;/strong&gt; 이란 서비스 불가(중단) 시간을 뜻한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 가용성(availability) = 업타임 / (업타임 + 다운타임)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;고가용성ha-high-availability&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%A0%EA%B0%80%EC%9A%A9%EC%84%B1ha-high-availability&quot; aria-label=&quot;고가용성ha high availability permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;고가용성(HA, High Availability)&lt;/h2&gt;
&lt;p&gt;고가용성이란 높은 가용성을 확보한 상태를 뜻한다. 가용성은 일반적으로 백분율로 나타내는데, &lt;strong&gt;9-nines&lt;/strong&gt; 이라는 표기법을 사용한다. 주로 숫자 9 로 구성된 퍼센트 표기법을 뜻한다. 즉, 가용성을 99.999...9% 와 같은 백분율로 표현한다.&lt;/p&gt;
&lt;p&gt;그런데 궁금한점이 있다. 어느정도의 수치를 가져야 고가용성 시스템이라고 할 수 있을까? 어림잡아 90% 만 달성해도 가용성이 높다고 할 수 있을까? 아래 표를 한 번 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7a4ea1269ad15d3a0b9161297035c627/c5bb3/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 97.54601226993866%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAADN0lEQVR42j1U2babMAzM//9b29vb7OxgFhvMmhBI0qNq5NAHDj62NJoZyd6Nt5nqppVvGO+UZjldvJBM3ZLKSzpfrqSNpTTl/atPhuPiJCM/iDinoyBKOMYjVVT0fv+lHRb744XO14BqO1CcFvR99CnNDaWqop+/j5TlWva/vg9U6oaiJKfv/YnXlhLe/8X7Yazo+XzRLoxiuno+4d92Az2WJ0VxzGw8us8LJczY80Oabnc6nM6UqZym+0zH84XVKHoxq6Ks6HS+0vp80q6stEiL04wqXdP8WOSPPYCUVUU5q8A64RhtGroxoMoLWWM/UwWryR2gF4Qs12PffPERgHGSil/9MIo/fhjRON3ET7AaxxvtDydhO4wTHY4nYbyuK3vIlWAyGALwPj/Y+IZNLiURcopSC6CwqhuJA5iua4kvtWafC/aQGfrMMOfkurGSBIba1AQrtuBSG7JtJ5IR19hOJBoGX9anFFUbICREcSKfqa00AgDwBQWyPJeCYBZw4yo+A1AQxhIHP8EWxdYVHvqBbChO7PrBgSgH0na9eIa17TqZBLAfOMYRaP43K+E4aQqSUwEsOKkXaWgKQCDNgVdSLPlMQmNbXivpMixSSnHh1DFEdyWpLAUMFQEGD4dPU7CGd2BrpClWiuIMNmXlQLkZ6fl6fTzkQ1RHAJhAGq4gCkBaJjNXs28RgxuxBTFgienwQsbIKic5iCIxGJIBhrnaGOHmbCzAHMmIw3zuD0e5QS0Xhf/aGMcQA4oDdGzkJHQNnUR1JGJsAIRiuKLuUbCyxqw6W7SQEoY+dxmyCmbVMYC7Su7+ostgBKk4Q/MACAWwCoWHaaakGOgUlDKTuz/7o1ydrQlgCD/BAIAohsZtDEN+rmALHg9c2crgVSq4qdpJ3pph217MxkjIteImQDIG28mfGNyNk4yQzGcljSt4Qip+ROSmwB9cPxzMj1UYYp3za4MCAIY8WAHP8CEGihADAtpOZNq7YwgZP35+SdK8LAICFn4YSpdzZh+ybLBCDHwcp0m8BHMUrwyDNr17YG3bCouRfVmWle4fhpYr35gVzreBR3e7vheGGHAUwWctjxjHvd5v+gesxs+60ZD5PwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7a4ea1269ad15d3a0b9161297035c627/a6d36/image.png&quot;
        srcset=&quot;/static/7a4ea1269ad15d3a0b9161297035c627/222b7/image.png 163w,
/static/7a4ea1269ad15d3a0b9161297035c627/ff46a/image.png 325w,
/static/7a4ea1269ad15d3a0b9161297035c627/a6d36/image.png 650w,
/static/7a4ea1269ad15d3a0b9161297035c627/c5bb3/image.png 680w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;신기하게도 가용성 90% 의 서비스는 하루에 약 2.4시간, 연간으로는 36.53일의 다운타임이 발생한다. 겉보기에 숫자 퍼센트는 매우 높아보이지만, 우리와 생각과 달리 다운타임이 매우 큰 것을 알 수 있다.&lt;/p&gt;
&lt;p&gt;가용성이 99% 정도라면 신뢰할만한 시스템일 것 같다는 생각이 든다. 하지만 시스템 세계에서 가용성 99% 는 1% 의 시간은 이용 불가능한 상태가 된다는 뜻이다. 99% 의 가용성일때의 다운타임은 절대 발생하지 않을 것 같지만, 연간 기준 3.65일의 다운타임이 발생한다.&lt;/p&gt;
&lt;p&gt;보통 고품질의 프로덕션을 운영하는 서비스에서 목표로 하는 가용성 수치는 &lt;strong&gt;five-nines&lt;/strong&gt; 라고 한다. &lt;strong&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EA%B3%A0%EA%B0%80%EC%9A%A9%EC%84%B1&quot;&gt;(참고)&lt;/a&gt;&lt;/strong&gt; 즉, 연간 5분 가량의 다운타임을 허용하는 의미인 five-nines 를 만족했을 때 고가용성 시스템이라고 표현한다. 우리는 &lt;strong&gt;five nines&lt;/strong&gt; 를 만족하는 서비스를 위한 고가용성 아키텍처를 설계할 필요가 있다.&lt;/p&gt;
&lt;h2 id=&quot;고가용성을-확보하기-위한-전략&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%A0%EA%B0%80%EC%9A%A9%EC%84%B1%EC%9D%84-%ED%99%95%EB%B3%B4%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%EC%A0%84%EB%9E%B5&quot; aria-label=&quot;고가용성을 확보하기 위한 전략 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;고가용성을 확보하기 위한 전략&lt;/h2&gt;
&lt;h3 id=&quot;신장전략-중심의-구성-vs-심장전략-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A0%EC%9E%A5%EC%A0%84%EB%9E%B5-%EC%A4%91%EC%8B%AC%EC%9D%98-%EA%B5%AC%EC%84%B1-vs-%EC%8B%AC%EC%9E%A5%EC%A0%84%EB%9E%B5-&quot; aria-label=&quot;신장전략 중심의 구성 vs 심장전략  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;신장전략 중심의 구성 (vs 심장전략) 🔑&lt;/h3&gt;
&lt;p&gt;가용성을 높이기 위해 취할 수 있는 접근 방법은 크게 &lt;strong&gt;심장전략&lt;/strong&gt; 과 &lt;strong&gt;신장전략&lt;/strong&gt; 이 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;심장전략 (고품질-소수전략)&lt;/code&gt; : 소수의 고사양 인스턴스로만 구성하여 가용성을 높이는 전략 (즉, &lt;strong&gt;Scale Up&lt;/strong&gt; 중심의 아키텍처 전략)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;신장전략 (저품질-다수전략)&lt;/code&gt; : 시스템을 구성하는 인스턴스들의 사양을 계속해서 높이기보다는 &quot;사물은 언젠가 망가진다&quot; 란 전제하여 저사양의 여러 인스턴스로 구성하는 전략 (즉, &lt;strong&gt;Scale Out&lt;/strong&gt; 중심의 아키텍처 전략)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;과거에는 위 2가지 전략 중 어느 쪽이 더 효율적인가를 확실히 알지 못했기 떄문에 2가지 방법을 모두 추구했다. 하지만 &lt;strong&gt;현재는 거의 모든 회사에서 신장전략(Scale Out 중심) 전략을 취하고 있다.&lt;/strong&gt; 고사양의 인스턴스를 조금만 사용하는 소수정예보단, 다소 저사양이라도 물량작전으로 서비스를 보완한다는 구조를 취하고 있다. 서비스를 구성할 때, 저사양의 인스턴스 어려대로 구성한 &lt;strong&gt;신장전략&lt;/strong&gt; 중심의 구조를 고려해보자.&lt;/p&gt;
&lt;h3 id=&quot;단일-장애점-spof-제거&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%9D%BC-%EC%9E%A5%EC%95%A0%EC%A0%90-spof-%EC%A0%9C%EA%B1%B0&quot; aria-label=&quot;단일 장애점 spof 제거 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단일 장애점 (SPOF) 제거&lt;/h3&gt;
&lt;p&gt;고가용성 아키텍처를 설계할 때 가장 중요한 점중 하나는 &lt;strong&gt;단일 장애 지점(SPOF, single point of failure)&lt;/strong&gt; 를 제거하는 것이다. SPOF 란 시스템을 구성하는 컴포넌트 중에 하나에서 장애가 발생하면 시스템 전체가 이용 불가능한 상태가 되는 지점을 뜻한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/91e79f6e59dc3fb7d51cd8036da7b8ab/6976b/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.852760736196316%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABsUlEQVR42o2SXWvTYBTH8xn9AN6IH0O8cqBXXg1B2U1B8WaTIiLTyYZjKGOzKk22NnVN26XJlqR56ZOXNsnPp4mV+kY9cHg45zmc8z//81eKouB3K8vypy/jhV17FofH737JrdYvTGGN1cX10FfnWzw4uI3ufq7iJZhC1hRVnWzY0dTqYzkhjmM66hm98y6u4/7ICSzT5qX2mPvvb6FdHRNOpszzefWfpSm2ZdYIPzkBoqibzeayQA49Gaq81T+S5zmmadLpdvD9gCQXXE8vK8SGYaDrOmkS80G12Nw+wfIEyoYHWlIw6vc5bbXkO+S5vsdGqyEbZMQiJooi0jRZ4a6sthoMBnKQT2P3CzfvPmXn4AxlOyyx5yWpECQSeiJXfn1xxFa7STCb4sm1x9a4omL1GAv0QeCTy+1e7J9y7+ETmvttlPIvR8jyGTPJj2S2Qtfr6YyMS/oTlab2iHF0QZbM0GXesizcUPC1OyISaX3lcs2lc9k8jjJ22pvceXODPf0ZxrchE3/yh4TWyqaWRS2Pq+mQ3W4DV1hVHEYhjuPgeR62bUsKgv9r+G991sdZ9e+4d/dbumkkqgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/91e79f6e59dc3fb7d51cd8036da7b8ab/a6d36/image-4.png&quot;
        srcset=&quot;/static/91e79f6e59dc3fb7d51cd8036da7b8ab/222b7/image-4.png 163w,
/static/91e79f6e59dc3fb7d51cd8036da7b8ab/ff46a/image-4.png 325w,
/static/91e79f6e59dc3fb7d51cd8036da7b8ab/a6d36/image-4.png 650w,
/static/91e79f6e59dc3fb7d51cd8036da7b8ab/e548f/image-4.png 975w,
/static/91e79f6e59dc3fb7d51cd8036da7b8ab/3c492/image-4.png 1300w,
/static/91e79f6e59dc3fb7d51cd8036da7b8ab/6976b/image-4.png 2172w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;만약 위와 같이 구성된 아키텍처에서 SPOF 는 어디일까? 아쉽게도, &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 이 SPOF 가 된다. 웹 서버에서 장애가 터지면 클라이언트의 요청은 WAS 로 도달할 수 없다. 데이터베이스 서버 또한 장애가 터지면 데이터베이스에 접근이 불가능하다. SPOF 를 제거하여 가용성을 개선해야만 고가용성을 확보할 수 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 필자가 가장 중요하게 여기는 고가용성을 보장해야 할 컴포넌트는 데이터베이스이다. 만약 WS, WAS 에서 문제가 터진다면 서버에 다운타임이 일시적으로 발생하는 것에 그치지만, DB 에 문제가 생길경우 서비스내 유저 데이터는 몰론 금융 데이터등 데이터가 유실되는 것 만큼 최악의 경우도 없을 것이다. DB 는 아키텍처를 구성하는 컴포넌트 중 가장 민감한 요소이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;다중화와-로드밸런싱-무중단-배포-️&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%A4%91%ED%99%94%EC%99%80-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC-%EF%B8%8F&quot; aria-label=&quot;다중화와 로드밸런싱 무중단 배포 ️ permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다중화와 로드밸런싱, 무중단 배포 ♻️&lt;/h3&gt;
&lt;p&gt;SPOF 를 제거하기 위해선, SPOF 지점을 &lt;strong&gt;다중화(redundancy)&lt;/strong&gt; 하여 해결할 수 있다. 다중화를 통해 하나의 인스턴스에서 장애가 발생하여도 다른 서버가 요청을 처리하거나 빠르게 장애 복구가 가능하기 떄문에 높은 가용성을 확보할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1813ef4ab00d97bf22aa7b5530746404/f1c64/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.44171779141104%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACUUlEQVR42nVTTW/TQBD13+TCiZ8BAnHhBlJB4oIqIK04gCikBZXSUgFJaFInwYnT2I7jj8Sx820ncWI7if3Y3aaigBhpvNbu7Js3b2Y5/GEJ+8bxmniMJEkQhiFmsxmm0yl830cQBJuYGGEQYeEH8GdzBIuQxXNxcgkyDyM8+HyOr7pzDZrsz+fQDR11qY5KpQJRFFkCXTPAyxnUHR7mUIGkiYjCJbiri1Gc4O6HHN5keFSFEmoXdfT7fcbMHYxgyE3IkgzDMDAaDWG1OjgW9rD7bQuZ6jG0lkKqicBVBAEnJyc4+nQIU9fR1Zr4+GwLvXYLumlg4PRxJGfxtPwaiq7CaltwHAfj8Rim0cL7d2mk02mMR+PLkjVNQz5/hkKhgAtJQvGcx+72C5iqCrvrwDYtPDx9jpt7t3F69h21qgiVnKlqAw25gXWUIJiH8FyP6cr1SFnZH2dMfAouKzKK5TLOeZ6xWCwWODg4wMudFEQCRi0IA/SdAUSTR8E+RNuXILUrlxpSoVOpHaLLCD5pQENRsE9KUMhqWRbT0Op00DKJBEQS6jRJy2gjK3zBo1f3kHq7jWpNwGq1BmeT4Fwuh2azyViuVius12u2LpdLdMh5qVRCvpBHsVhEJpNBr9dDnMSYejNIooKfZQGD/pCx5+hl1x0TBqQBw+HvidyME2XT7XZh2zYDov9RFOF/xgVkFJ7cf4zONNkA/TXq1zYoazqXNMlkMmFyUJ3pXLquyxJxnqbixq07qHQ2LyD5N+sVKL1AAeiLoSt1z/MYGO0BlegXYloo6RBndloAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/1813ef4ab00d97bf22aa7b5530746404/a6d36/image-5.png&quot;
        srcset=&quot;/static/1813ef4ab00d97bf22aa7b5530746404/222b7/image-5.png 163w,
/static/1813ef4ab00d97bf22aa7b5530746404/ff46a/image-5.png 325w,
/static/1813ef4ab00d97bf22aa7b5530746404/a6d36/image-5.png 650w,
/static/1813ef4ab00d97bf22aa7b5530746404/e548f/image-5.png 975w,
/static/1813ef4ab00d97bf22aa7b5530746404/3c492/image-5.png 1300w,
/static/1813ef4ab00d97bf22aa7b5530746404/f1c64/image-5.png 1390w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;특히 로드밸런싱을 통해 부하 분산을 하는 방식을 꾀하여, 서버가 부담해야 할 비용을 낮출 수 있을 것이다. 무엇보다 &lt;strong&gt;로드밸런서와 다중화를 활용하여 무중단 배포를 도입&lt;/strong&gt;하면, 새로운 버전의 서비스를 배포할 떄 발생하는 다운타임도 제거하고 더 높은 가용성을 확보할 수 있다. 이와 관련한 내용은 &lt;a href=&quot;https://haon.blog/haon/infra/ci-cd/jenkins-nginx-blue-green/&quot;&gt;Jenkins 와 Nginx 를 활용한 Blue/Green 배포 환경 구축하기&lt;/a&gt; 에서 다룬적이 있다.&lt;/p&gt;
&lt;h3 id=&quot;예산과-비용을-고려하여-최적의-다중화-구성하기-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%88%EC%82%B0%EA%B3%BC-%EB%B9%84%EC%9A%A9%EC%9D%84-%EA%B3%A0%EB%A0%A4%ED%95%98%EC%97%AC-%EC%B5%9C%EC%A0%81%EC%9D%98-%EB%8B%A4%EC%A4%91%ED%99%94-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0-&quot; aria-label=&quot;예산과 비용을 고려하여 최적의 다중화 구성하기  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;예산과 비용을 고려하여 최적의 다중화 구성하기 🎯&lt;/h3&gt;
&lt;p&gt;신장전략에 기반하여 동일한 기능의 컴포넌트를 병렬화하는 것을 클러스터링이라고 한다. 클러스터링은 &lt;a href=&quot;https://haon.blog/database/clustering/&quot;&gt;고가용성과 확장성을 위한 데이터베이스 클러스터링(DB Clustering)&lt;/a&gt; 에서 소개한 적이 있다. 클러스터 구성으로 같은 기능을 가진 서버를 늘리면 늘릴수록 시스템 전체에서 장애 발생률이 낮아진다. 예를들어, 어떤 서버의 고장률이 10% 라고 해보자. 동일한 사양의 서버를 계속해서 늘려갈 때 시스템 전체의 장애 발생률은 어떻게 변할까?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 서버 1대 : 10% (0.1)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 서버 2대 : 1% (0.1 x 0.1)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 서버 3대 : 0.1% (0.1 x 0.1 x 0.1)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 서버 4대 : 0.01% (0.1 x 0.1 x 0.1)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;신기하게도 서버 1대를 더 추가했을 때 장애률이 무려 9% 나 감소했다. 고작 1대의 서버만 더 추가했을 뿐인데, 현저히 줄어든 모습을 확인할 수 있다. 그렇다면 서버를 계속해서 더 추가한다면 장애률이 어떻게 변하는가? 아래 그래프를 통해 알아낼 수 있는점은 크게 2가지다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 514px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/eaf351339372174f701d75430efac7e5/dea13/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.920245398773%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABq0lEQVR42kWT6W7bMBCE/f6vUaBA0SJBrFqndfEmJTnNE01mV3H6Y0CJBj/NDNcXFxcsbsbsJtXkRqxxhU0ONnuV3yLCnhCPrGvgKs/lsWF737FTj49DdYnZoB07/L6+oupqVEODwUwwOXxr8hajM/yQR9wFSuBekI4N+XFQOwqhAr+EbNFMHX78+oma4NEbAmd088j9O/d6vNY3/Kne8HKrMNoV/Tqjn5nGGIxmhc8JG8EK9MnSwYJr1+BuF9TTgLf2xucVczCs4HRmcoRJgc+UrqwjRjjux31jfAEyss8n8ITMqPoGU7BYolPJQRUhAlQwZUVJgOx2O+OnQx06zIzZMFo7DxrXandeVz1Y0rfcJmKPhPiSVQIUl/kEetwJubY1/vYt3RosdLjSnUJL+ALxMC9CVlsiQUlhEjls5yXJRy6heAzrhJfqSvCibqU7Aa7J6y3/dyfQpEBXzv40st54ocPMsSGwY9Qn8E74SMeLQL+6NOWMLd1JZJlFry65z349U+hMUpxDKd3qYXHl+ONzkJMOMPt5Z+G8wfzvqZ3v3CNA/gCeKZ4D/gmmEZNioL6xgwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/eaf351339372174f701d75430efac7e5/dea13/image-1.png&quot;
        srcset=&quot;/static/eaf351339372174f701d75430efac7e5/222b7/image-1.png 163w,
/static/eaf351339372174f701d75430efac7e5/ff46a/image-1.png 325w,
/static/eaf351339372174f701d75430efac7e5/dea13/image-1.png 514w&quot;
        sizes=&quot;(max-width: 514px) 100vw, 514px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;우선 &lt;strong&gt;가용성(가동률) 100% 는 이론적으로 불가능&lt;/strong&gt;하다. 인스턴스를 무한정 추가해도 가용성은 절대 100% 가 되지 않는다. 이는 모든 서버나 네트워크 기기가 동시다발로 고장나는 엄청난 우연의 가능성을 완전히 배제할 수 없기 때문이다.&lt;/p&gt;
&lt;p&gt;또한 서버 대수를 추가할수록 1대함에 따라 얻을 수 있는 &lt;strong&gt;가용성 향상 폭이 일정하지 않고, 되려 점점 폭이 작아진다&lt;/strong&gt;는 점도 알 수 있다. 서버를 1대에서 2대로 추가함에 따라 얻을 수 있는 가용성 향상 혹은 90% 에서 99% 로 9% 이지만, 2대에서 3대로 증가할 때에는 99% 에서 99.99% 가 되어 0.9% 밖에 증가하지 않는다. &lt;strong&gt;즉, 가용성은 인스턴스 갯수에 정비례하여 향상되지 않는다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;가용성을 확보하기 위해 &lt;strong&gt;five-nines&lt;/strong&gt; 라는 수치에 도달하기란 생각보다 쉽지않다.  &lt;strong&gt;five-nines&lt;/strong&gt; 는 사실상 이상적인 수치에 가깝기 떄문에, 만약 주어진 비용과 예산이 한정적이라면 다중화 대수와 장애 발생률을 적절히 고려하여 시스템을 설계해보자.&lt;/p&gt;
&lt;h3 id=&quot;장애-극복-failover-기능&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%95%A0-%EA%B7%B9%EB%B3%B5-failover-%EA%B8%B0%EB%8A%A5&quot; aria-label=&quot;장애 극복 failover 기능 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장애 극복 (FailOver) 기능&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://haon.blog/database/clustering/&quot;&gt;고가용성과 확장성을 위한 데이터베이스 클러스터링(DB Clustering)&lt;/a&gt; 에서 다룬 데이터베이스 클러스터링 기법이 FailOver 를 위한 다중화 기법 중 하나이다. 클러스터링과 같은 기법을 통해, 장애가 발생했을시 곧바로 장애를 탐지하고 장애를 극복하는 자동화 시스템이 필요하다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4a2d7ea7469b96741114ba47cd1cc453/01a87/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.21472392638037%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABlklEQVR42pWRvU4CQRSFfQFfQX0JCxMbGxNtLHwCExtbLe1sSei0N7EjGp/AREUBTRAxoAgIQdTIn0DYndn52eOdWTCYCIabzM7suXe+e/buFEz4frC136BKcfDKPVjxFuLrE4I5gHAByaB5D16vYzXhdOF7lFMSwzH1600wKmIoZp4QTVwhlUmiWq5QJwWpNTLPL7i+iiL3kED2MQfJCSj5aKBPLlCvoV66wV55A/vVXbRF0+beOxy5jw54dB3qfBn5XBKeooR0RwO1cOz+zNLYLqxhp7yGV5kPmpkHrwNn81Cn0zSSo0D3emMcmllpZW+n+CVOMofkpNgfsw6gjQugeGBIVKuDO2OByoNvoBTtrzaOIxFIQZoZPkEHP9CeaX6+GDNDaEmNCUpFynyK5gRj2NrcwNLiArLpOypSJHdtc59y6Df/Gzjstu/EZQwrq6uYmZ1DLBazmlJq1LX/gYVCAeFwGKFQCPF47EefGDgIRg6FEBbkui4cx7GaOZvFOZ8MOHA7WI1GA81m0+61Wg2tVusX8BsKYUU/6FCYdAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/4a2d7ea7469b96741114ba47cd1cc453/a6d36/image-6.png&quot;
        srcset=&quot;/static/4a2d7ea7469b96741114ba47cd1cc453/222b7/image-6.png 163w,
/static/4a2d7ea7469b96741114ba47cd1cc453/ff46a/image-6.png 325w,
/static/4a2d7ea7469b96741114ba47cd1cc453/a6d36/image-6.png 650w,
/static/4a2d7ea7469b96741114ba47cd1cc453/e548f/image-6.png 975w,
/static/4a2d7ea7469b96741114ba47cd1cc453/01a87/image-6.png 1288w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;또한, 로드밸런서로 &lt;strong&gt;헬스 체킹(health checking)&lt;/strong&gt; 하여 FailOver 를 대처할 수 있다. 현재 로드밸런서가 라우팅하고 있는 여러 컴포넌트들에게 장애가 발생하지 않고 정상 작동하는지 헬스 체크하고, 장애가 탐지되면 그 즉시 해당 컴포넌트를 라우팅 대상에서 제외하여 FailOver 할 수 있다. 즉, 유저의 트래픽이 장애가 발생한 컴포넌트로 향하지 않게되어 고가용성을 확보할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;백업과-지리적-분산-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B1%EC%97%85%EA%B3%BC-%EC%A7%80%EB%A6%AC%EC%A0%81-%EB%B6%84%EC%82%B0-&quot; aria-label=&quot;백업과 지리적 분산  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;백업과 지리적 분산 🌍&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1cc39885ea162e564b74b6f85d15eceb/d4377/image-7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.533742331288344%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABHElEQVR42p2S226DMAyG+/7v00nbXmBUaqVJ3UEtpSANCA2UQwgkkPAvsIqLlXZovnHkxJ//2F7gjnUXz6TCjuTwzwyUiTE+ZYsriHmtdTeehaiR1S0OBwcry4IX0l/lbgC7y32fvPVzxIUBVRIxpTiXAoQmcFwPxKiUTYumaaCUmgf83O3x/PSIL5r+xHgNy03xFhQIUw5CCBhjRr34G8iaDq/bDyyXD4hNv0rO4RuwH0awbdsoLCBkr1DO66FoNZhQqJret6gqjlPG4RxdbNYbeCSBbBVEXc8DXhUwiTRjeI9KHGOOtGoH1Vrr/wGHNhjFdmCG4p8QDF+W89dmahtL01d7b2O9epm/NveslBpBksP1I5C8Hgc4BfwGjGEMbgNhHWUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/1cc39885ea162e564b74b6f85d15eceb/a6d36/image-7.png&quot;
        srcset=&quot;/static/1cc39885ea162e564b74b6f85d15eceb/222b7/image-7.png 163w,
/static/1cc39885ea162e564b74b6f85d15eceb/ff46a/image-7.png 325w,
/static/1cc39885ea162e564b74b6f85d15eceb/a6d36/image-7.png 650w,
/static/1cc39885ea162e564b74b6f85d15eceb/e548f/image-7.png 975w,
/static/1cc39885ea162e564b74b6f85d15eceb/d4377/image-7.png 1294w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;언제 어디서든 재난문제, 천재지변으로 우리가 운영하는 서비스의 인스턴스가 공격받을 수 있다. 특히 재해로 인해 데이터베이스 서버가 장애가 터져, 모든 데이터가 유실될 잠재적 위험은 항시로 존재한다.  만약 지진이나 태풍으로인해 DB 하드웨어가 설치된 시설이 파괴되었다면 어떻게 될까? 단일 데이터베이스라면 모든 데이터가 유실된 위험은 몰론 서비스 이용이 장기간 불가능하다. 데이터베이스 다중화를 구성했다고 한들, 만약 재해가 발생한 동일한 지역내에 데이터베이스 서버를 모두 구축했다면 위험에 빠질 수 있다. &lt;strong&gt;실제로 2022년 카카오 데이터센터 화재로 인해 서비스 이용에 차질을 입고, 자칫 데이터 유실이 될 뻔한 사례가 존재한다. &lt;a href=&quot;https://www.hani.co.kr/arti/society/society_general/1062797.html&quot;&gt;기사 : 카카오, 데이터센터 화재로 서비스 중단… 카뱅까지 영향&lt;/a&gt;&lt;/strong&gt; 언제 어디서든 닥칠 위험에 대비하여 &lt;strong&gt;지리적 분산&lt;/strong&gt; 환경을 구축하자.&lt;/p&gt;
&lt;p&gt;또한 재해로부터 데이터를 지키기위한 &lt;strong&gt;백업과 복구 프로세스&lt;/strong&gt;를 구축해야한다. 백업과 복구 프로세스가 잘 갖춰져있는 아키텍처에서는 설령 데이터가 유실되었다고 하더라도, 빠르게 복구하여 서비스를 재개할 수 있을 것이다. MySQL 과 같이 다중화에 유리한 DBMS 를 사용한다면 &lt;a href=&quot;https://haon.blog/database/replication-theory/&quot;&gt;고가용성과 확장성을 위한 데이터베이스 레플리케이션(DB Replication)&lt;/a&gt; 에서 다룬 데이터베이스 레플리케이션을 통해 백업과 복구 프로세스를 구축할 수 있을것이다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;데이터베이스 첫걸음 - 미크, 기무라 메이지&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EA%B3%A0%EA%B0%80%EC%9A%A9%EC%84%B1&quot;&gt;https://ko.wikipedia.org/wiki/%EA%B3%A0%EA%B0%80%EC%9A%A9%EC%84%B1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/high-availability-architecture/&quot;&gt;https://hudi.blog/high-availability-architecture/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://yechankk.tistory.com/9&quot;&gt;https://yechankk.tistory.com/9&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://itwiki.kr/w/%EB%8B%A8%EC%9D%BC_%EC%9E%A5%EC%95%A0%EC%A0%90_%EB%AC%B8%EC%A0%9C&quot;&gt;https://itwiki.kr/w/단일_장애점_문제&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.hani.co.kr/arti/society/society_general/1062797.html&quot;&gt;https://www.hani.co.kr/arti/society/society_general/1062797.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[고가용성과 확장성을 위한 데이터베이스 레플리케이션(DB Replication)]]></title><description><![CDATA[학습배경 💡 데이터베이스 클러스터링에 대한 이론은 고가용성과 확장성을 위한 데이터베이스 클러스터링(DB Clustering…]]></description><link>https://haon.site/database/replication-theory/</link><guid isPermaLink="false">https://haon.site/database/replication-theory/</guid><pubDate>Tue, 24 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 데이터베이스 클러스터링에 대한 이론은 &lt;a href=&quot;https://haon.blog/database/clustering/&quot;&gt;고가용성과 확장성을 위한 데이터베이스 클러스터링(DB Clustering)&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;지난 클러스터링에 이어, &lt;strong&gt;데이터베이스를 다중화하여 가용성을 높이는 방법으로 레플리케이션에 대한 학습이 필요함을 알게 되었다.&lt;/strong&gt; 레플리케이션이 처음에는 단순히 부하 분산 처리 용도로만 사용되는 것인줄 알았는데, 학습해보니 레플리케이션을 구축함으로써 얻는 이점이 꽤 많다는 것을 알게 되었다. 이번 포스팅에선 레플리케이션을 통해 얻는 이점과 사용 이유에 대해 학습해보도록 한다. 또한 MySQL 기준으로 레플리케이션 구조 원리에 대해 정말 간단한 이론을 다루어보고자 한다. (더 자세한 MySQL 레플리케이션 이론은 향후 포스팅에서 다루어보겠다.)&lt;/p&gt;
&lt;h2 id=&quot;레플리케이션-repliation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-repliation&quot; aria-label=&quot;레플리케이션 repliation permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레플리케이션 (Repliation)&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f331be5276eba5116bb565d3e9e5a86e/d52e5/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 89.57055214723925%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAAAsTAAALEwEAmpwYAAACAUlEQVR42q2U228SQRTG+f+fvMR4SXyq0MYUW9DwZmOF0oJJCSUpSUFRsFx3d6677IXdz5nZpKHVBTSe5ITNfHN+nMvM5LDB4jg2niTJvV/tWZbLEnTwv1guC9TpdFDIF1CpVFAqlVCtVlEul1EsFtFqtf4eyBhDs9lEvV5Hr9dDu93GdbdrYKPhcDdg4AeQ0oOMYiyjFWLFTtb6tYoi1b8EgXI3iCBdz8RkAj1vCToZw/1Sg2zWQL/2QJkw0DhegXEBdtOFbHyGbDVAx7cmJhuo/o3/GCDce4bgaA/u5QW45+txG6j+ds9OEO6/hF85BO/fwAvCHYBvniN4+xrysvE78PwTwvwL+OUD8G/9HYE6w/1XCnjxR2CgdP84vwNQNZk6BI5lw5rNwQkFpwxhGML3fVBCwByKxXQOahOzd+l5m4FMB1EHjm1BqKPDFNR1XQgh1Jpt1rTGKDHa0t0GVBtPDxc4fjrD7CeBFBz6aOrz6UqOuVo7ejwzewQnJiYTqNPXZZwczPHuyRS3A8dADDBOgePvDoqPpvhYUC1hW4Cmh6rkq5qN8w8WrClRWbC7DCXnWEwI6u8XuDqzTTVbM7TnBKMeweDawWR4v2ShgNOR0vupbs3UUJZbgLrR3AwjdcH43R0X6qZwmk4+1dVQNt2U9fdu3R++j+v+8JnL4T/bL5tPZzi499kQAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/f331be5276eba5116bb565d3e9e5a86e/a6d36/image.png&quot;
        srcset=&quot;/static/f331be5276eba5116bb565d3e9e5a86e/222b7/image.png 163w,
/static/f331be5276eba5116bb565d3e9e5a86e/ff46a/image.png 325w,
/static/f331be5276eba5116bb565d3e9e5a86e/a6d36/image.png 650w,
/static/f331be5276eba5116bb565d3e9e5a86e/d52e5/image.png 848w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;레플리케이션을 번역하면 복제이다. 데이터베이스 복제란 뜻 그대로 한 서버의 데이터를 다른 서버에 그대로 동기화시키는 것을 의미한다. 이때 원본 데이터를 보유하고 복제(동기화)시키는 측의 데이터베이스를 &lt;strong&gt;마스터(master) 서버&lt;/strong&gt;, 복제를 당하는(동기화되는) 측의 데이터베이스를 &lt;strong&gt;슬레이브(slave) 서버&lt;/strong&gt; 라고한다. 즉, 마스터(주인) 과 슬레이브(노예) 서버간에 주인과 노에의 관계가 생긴다. 하지만, 마스터와 슬레이브라는 명칭은 최근부터 윤리적인 이유로 사용되고 있지 않다. 대신 마스터를 &lt;strong&gt;소스(Source) 서버&lt;/strong&gt;, 슬레이브를 &lt;strong&gt;레플리카(Replica) 서버&lt;/strong&gt; 라고 명칭하고 있다.&lt;/p&gt;
&lt;h2 id=&quot;레플리케이션의-이점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%98-%EC%9D%B4%EC%A0%90&quot; aria-label=&quot;레플리케이션의 이점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레플리케이션의 이점&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c83d92e0f8f1660154f103b4f1555610/9f9a4/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.14723926380368%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABJklEQVR42o2S3U6DQBBG+/6vY3qn8cqoqRf+pEkptVURCgi0sAsLu4Xjpkm1sa3lu5rM7p6Z+XYGnFDXdT9xqTe4UYGfChLZ0O6d/dXgALSF/ca1qijqDbPXOaP7W7ww4TRuD7i7lCvDS5ATraWNNVmaklcNwVfG/O2dcCWoG40xhrZtzwOL2uC4M66vLvmM021uJRUjb20LFUSFIggChBBorc8DpYGn8YSL4ZDYPhZSskxzvCBk6jhEWUFVN9sOe3lYmxapW9QGRGOoqpKkqFjYccfPj7brDGXzumn6AQ8KKEW8FkziEjcWZJWhsON2R/w7C9zZIOwvT/wUxwtZriSqrvuvzTFsZcefui4Pdzd8LON+a/OfSuupn+Qs/IgoV3axTwO/AQfpDJwM/eiFAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/c83d92e0f8f1660154f103b4f1555610/a6d36/image-1.png&quot;
        srcset=&quot;/static/c83d92e0f8f1660154f103b4f1555610/222b7/image-1.png 163w,
/static/c83d92e0f8f1660154f103b4f1555610/ff46a/image-1.png 325w,
/static/c83d92e0f8f1660154f103b4f1555610/a6d36/image-1.png 650w,
/static/c83d92e0f8f1660154f103b4f1555610/e548f/image-1.png 975w,
/static/c83d92e0f8f1660154f103b4f1555610/3c492/image-1.png 1300w,
/static/c83d92e0f8f1660154f103b4f1555610/9f9a4/image-1.png 1560w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;부하-분산-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%80%ED%95%98-%EB%B6%84%EC%82%B0-&quot; aria-label=&quot;부하 분산  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;부하 분산 💦&lt;/h3&gt;
&lt;p&gt;일반적인 웹 애플리케이션과 마찬가지로 데이터베이스 다중화도 &lt;strong&gt;수평 확장(Scale Out)&lt;/strong&gt; 의 개념이다. 수평적으로 확장된 상태에서 하나의 서버가 읽기 연산 쿼리만을 처리하고, 다른 서버가 쓰기 연산 쿼리만을 처리함으로써 요청을 분산하여 쿼리 성능 향상을 기대해볼 수 있다.&lt;/p&gt;
&lt;p&gt;쓰기를 담당하는 소스(Source) 서버 1대와 읽기를 담당하는 레플리카(Replica) 서버 여러대를 배치하여 부하를 분산하는 것이 일반적인 레플리케이션 방식이다. 일반적인 애플리케이션이라면 통계상 쓰기 연산에 비해 읽기 연산의 비중이 매우 높기 떄문에, 읽기 연산을 처리하는 서버를 여러대 배치하는 것이다. &lt;strong&gt;즉, 상대적으로 비중이 낮은 쓰기 연산을 처리하는 서버는 1대만이 담당하고, 비중이 높은 읽기 연산에 대해선 여러 서버가 요청을 분산하여 부하를 줄인다.&lt;/strong&gt; 이를통해 읽기 연산에 대한 부하를 분산하여 성능을 개선할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;백업-️&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B1%EC%97%85-%EF%B8%8F&quot; aria-label=&quot;백업 ️ permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;백업 ♻️&lt;/h3&gt;
&lt;p&gt;레플리케이션은 데이터를 동기화하는 작업이므로, 백업에도 매우 유용하게 사용된다. 백업 프로그램을 통해 정기적으로 데이터베이스를 백업하는 방식을 사용하면 서비스에 영향을 줄 수 있다. 반면 &lt;strong&gt;레플리케이션은 데이터베이스가 애플리케이션의 요청을 분산하는 것은 몰론 백업 과정 또한 동시에 수행할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;하지만 레플리케이션 단독으로만 DB 아키텍쳐를 구성해선 완벽한 백업을 수행할 순 없다. 만약 Source 서버에 개발자가 실수로 모든 데이터를 제거하는 경우, 레플리케이션 실시간 동기화로 인해 다른 서버들 모두의 데이터가 동일하게 제거될 것이다. 따라서 백업을 위해서 레플리카 서버는 실시간으로 동기화되도록 하고, 이와 별개로 레플리카 서버에 스캐쥴러를 실행하여 일정 주기로 별도의 백업하는 설계 구조를 갖추는 것이 필요하다.&lt;/p&gt;
&lt;h3 id=&quot;지리적-분산-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%80%EB%A6%AC%EC%A0%81-%EB%B6%84%EC%82%B0-&quot; aria-label=&quot;지리적 분산  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;지리적 분산 🌍&lt;/h3&gt;
&lt;p&gt;구글과 같이 전세계적으로 사용되고 있는 서비스들은 DB 서버를 어떤 곳에 위치시킬지 고려해야한다. 만약 특정 키워드를 구글링한 결과, 내가 서칭한 데이터가 다른 나라 데이터베이스 저장소에 위치한다면 어떻게 될까? 대한민국에서 구글링을 시도했는데 검색한 데이터가 미국 데이터베이스 서버에 존재한다면? 대한민국은 미국과 지리적으로 멀리 떨어져있기 떄문에 느린 응답을 받을 수 밖에 없다. 따라서 각 국가별로 DB 복제 서버를 배치한 후, 실시간으로 데이터를 동기화시켜서 지리적 이슈를 해결할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;장애-극복failover-과-재해대책-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%95%A0-%EA%B7%B9%EB%B3%B5failover-%EA%B3%BC-%EC%9E%AC%ED%95%B4%EB%8C%80%EC%B1%85-&quot; aria-label=&quot;장애 극복failover 과 재해대책  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장애 극복(FailOver) 과 재해대책 🚒&lt;/h3&gt;
&lt;p&gt;지리적 분산을 통해 빠른 재해대책과 FailOver 를 극복할 수 있다. 만약 지진이나 태풍으로인해 DB 하드웨어가 설치된 시설이 파괴되었다면 어떻게 될까? 단일 데이터베이스라면 모든 데이터가 유실된 위험은 몰론 서비스 이용이 장기간 불가능하다. 만약 DB 서버가 다른 지역에 골구로 여러곳에 분산되어 있다면 데이터 유실 방지는 몰론, 서비스를 계속해서 운영할 수 있을 것이다.&lt;/p&gt;
&lt;h2 id=&quot;mysql-의-레플리케이션-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mysql-%EC%9D%98-%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;mysql 의 레플리케이션 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MySQL 의 레플리케이션 방식&lt;/h2&gt;
&lt;p&gt;MySQL 의 복제 방식은 &lt;strong&gt;바이너리 로그 기반 복제&lt;/strong&gt; 방식으로 이루어진다. MySQL 내에서 발생하는 &lt;strong&gt;모든 변경사항(이벤트)&lt;/strong&gt; 들은 소스 서버의 &lt;strong&gt;바이너리 로그(binary log)&lt;/strong&gt; 파일에 순차적으로 기록된다. 레플리케이션은 레플리카 서버가 소스 서버의 로그를 읽어와서(복제) 레플리카 본인 서버의 바이너리 로그 파일에 순차적으로 로그를 기록한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f23912ff4e8206f0d46b9964ac0c24ea/66632/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.44171779141104%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACEElEQVR42pVSy27TQBTtf7FhyS7f01KJphKbsoRA9k2REEVICKmqQtrCjjycFhqihNRNHOfhmdiJn7GdxIe5k+aBxIYrjUczc++5x+eeHTxEYg+Q8DuMOAfnI3S7Xfi+j3+F4zjQNE3mmKa5rE8Sue/QZyEOjmkgtg20Oxp+1WpotVqwbRuu6yIIgjWQ63no9/tQFAX1eh2MMUynU5m7AVwkghWDUi6i3e7IB1VVoVQqMAwDURTJu0AUErPLQgGTyQQ9XcfXqyvZgEDXgLPZDHEcI5N5jS/5vHwolYo4TKfhjQfwTRWdZgWRfY+hdouDg0P0ejqq1Sr2dncluzCcyt/+C/Do6AXOzs4lYEW5xv6z54hdDSErIjavEfHvmHS/4Th3AmtkQFNrOHn7DvNZDP9BFgk4n89gWi5c3kAyulwqH7QQDy8w4L7UjoKPQ9h372HpRfwun2KunWJhXUBXS6Le2QCKGaHRVHFTFmC9D+gzGwkTifU8Pn0+hzliMotxC3b/Bh67RWCJ5tEEwf1HRN4Anh9uM5yvLRJFIVKpFAqFpZY04e0pU6SFti9fZeT50eMn+PGzRr4Rw11sNFyNnTGObDaLipgwhSdsQouCchJRlM2+QS53LC2z/3QPzUZDYqyHQsgrv5EdiC2B0E4swjBcs6X78XgMy7IkIL3TTndbGv5fUKPhcCibk09prQD/ANenL8G82uz6AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/f23912ff4e8206f0d46b9964ac0c24ea/a6d36/image-2.png&quot;
        srcset=&quot;/static/f23912ff4e8206f0d46b9964ac0c24ea/222b7/image-2.png 163w,
/static/f23912ff4e8206f0d46b9964ac0c24ea/ff46a/image-2.png 325w,
/static/f23912ff4e8206f0d46b9964ac0c24ea/a6d36/image-2.png 650w,
/static/f23912ff4e8206f0d46b9964ac0c24ea/e548f/image-2.png 975w,
/static/f23912ff4e8206f0d46b9964ac0c24ea/3c492/image-2.png 1300w,
/static/f23912ff4e8206f0d46b9964ac0c24ea/66632/image-2.png 1504w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;트랜잭션 처리 쓰레드를 제외했을 때, 복제 과정에는 3가지 종류의 쓰레드가 참여한다. 그 중 레플리케이션 쓰레드는 소스 서버에서, 나머지 레플리케이션 I/O 쓰레드와 레플리케이션 SQL 쓰레드는 레플리카 서버에서 실행된다.&lt;/p&gt;
&lt;p&gt;우선 소스 서버의 &lt;strong&gt;바이너리 로그&lt;/strong&gt;에 이벤트가 발생하면 &lt;strong&gt;바이너리 로그 덤프 쓰레드&lt;/strong&gt; 가 이 이벤트를 읽고 레플리카 서버로 해당 이벤트에 대한 바이너리 로그를 전송한다. 레플리카 서버의 &lt;strong&gt;레플리케이션 I/O 쓰레드&lt;/strong&gt; 는 이 변경사항을 &lt;strong&gt;릴레이 로그 파일&lt;/strong&gt; 에 저장한다. 아직까지는 변경사항이 레플리카 서버에는 반영되지 않은 상태인데, 이를 반영하기 위해 &lt;strong&gt;레플리케이션 SQL 쓰레드&lt;/strong&gt; 가 변경내용을 &lt;strong&gt;데이터 파일&lt;/strong&gt; 에 저장한다.&lt;/p&gt;
&lt;p&gt;각 쓰레드의 역할을 자세히 정리해보자면 다음과 같다.&lt;/p&gt;
&lt;h3 id=&quot;바이너리-로그-덤프-쓰레드-binary-log-dump-thread&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%94%EC%9D%B4%EB%84%88%EB%A6%AC-%EB%A1%9C%EA%B7%B8-%EB%8D%A4%ED%94%84-%EC%93%B0%EB%A0%88%EB%93%9C-binary-log-dump-thread&quot; aria-label=&quot;바이너리 로그 덤프 쓰레드 binary log dump thread permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;바이너리 로그 덤프 쓰레드 (Binary Log Dump Thread)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;레플리케이션 작업이 레플리카 서버로부터 요청 되었을 때 소스 서버로부터 생성되는 쓰레드이다.&lt;/strong&gt; 이 쓰레드는 바이너리 로그에 기록된 이벤트를 읽고, 레플리카 서버로 전송하는 역할을 수행한다.&lt;/p&gt;
&lt;h3 id=&quot;레플리케이션-io-쓰레드-replication-io-thread&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-io-%EC%93%B0%EB%A0%88%EB%93%9C-replication-io-thread&quot; aria-label=&quot;레플리케이션 io 쓰레드 replication io thread permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레플리케이션 I/O 쓰레드 (Replication I/O Thread)&lt;/h3&gt;
&lt;p&gt;레플리카 서버로부터 레플리케이션 작업이 시작되면, 레플리케이션 I/O 쓰레드를 생성하고 소스 서버로부터 바이너리 로그를 가져온다. &lt;strong&gt;가져온 바이너리 로그내에 기록된 이벤트들은 레플리카 서버의 릴레이 로그(relay log) 에 저장한다.&lt;/strong&gt; 릴레이 로그는 소스 서버와 레플리카 서버를 중계하는 로그라는 뜻을 내포하고 있다.&lt;/p&gt;
&lt;p&gt;이 과정에서 &lt;strong&gt;레플리케이션 I/O 쓰레드는 소스 서버로부터 전달받은 바이너리 로그를 읽고(Input), 읽어들인 내용을 릴레이 로그에 쓰는(Output) 작업&lt;/strong&gt;을 수행한다.&lt;/p&gt;
&lt;h3 id=&quot;레플리케이션-sql-쓰레드-replication-sql-thread&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-sql-%EC%93%B0%EB%A0%88%EB%93%9C-replication-sql-thread&quot; aria-label=&quot;레플리케이션 sql 쓰레드 replication sql thread permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레플리케이션 SQL 쓰레드 (Replication SQL Thread)&lt;/h3&gt;
&lt;p&gt;레플리케이션 I/O 쓰레드가 릴레이 로그를 작성하면, &lt;strong&gt;레플리케이션 SQL 쓰레드는 로그에 기록된 이벤트를 읽고 실행&lt;/strong&gt;하는 역할을 한다.&lt;/p&gt;
&lt;h2 id=&quot;모행-서비스에-적합한-db-아키텍쳐-구조--레플리케이션-vs-클러스터링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A8%ED%96%89-%EC%84%9C%EB%B9%84%EC%8A%A4%EC%97%90-%EC%A0%81%ED%95%A9%ED%95%9C-db-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90-%EA%B5%AC%EC%A1%B0--%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-vs-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81&quot; aria-label=&quot;모행 서비스에 적합한 db 아키텍쳐 구조  레플리케이션 vs 클러스터링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;모행 서비스에 적합한 DB 아키텍쳐 구조 : 레플리케이션 vs 클러스터링&lt;/h2&gt;
&lt;p&gt;이번 레플리케이션을 학습하면서 이전에 &lt;a href=&quot;https://haon.blog/database/clustering/&quot;&gt;고가용성과 확장성을 위한 데이터베이스 클러스터링(DB Clustering)&lt;/a&gt; 에서 학습한 클러스터링 내용을 비교했다. 우리 하모니 팀이 개발중인 모행 서비스에는 어떤 데이터베이스 아키텍쳐 구조가 적합할까?&lt;/p&gt;
&lt;p&gt;클러스터링을 사용하는 이유는 데이터베이스 서버를 다중화하여 성능을 개선하고, FailOver (장애 대응) 를 하기 위함이다. 하지만, 이는 레플리케이션에서도 충분히 지원하는 기능이다. 또한 클러스터링의 취약점은 여러 DB 서버에서 스토리지를 단 1대만 사용하기 떄문에, 병목 현상이 발생하고 성능 저하로 이어질 가능성이 크다. 반면 레플리케이션은 스토리지를 여러대 사용하고 데이터를 실시간으로 복제하므로, 각 스토리지에서 병목이 발생할 위험이 적다. 클러스터링과 달리 병목 현상이 발생하지 않고, 부하 분산이 가능하여 쿼리 성능이 개선되며, 백업까지 가능하고, 지리적 분산을 통해 재해대책을 쉽게 수립 가능하다.&lt;/p&gt;
&lt;p&gt;필자가 생각하기에 레플리케이션에 비해 클러스터링이 갖는 이점을 뽑자면 장애 대응이 조금 더 유연하다는 점이다. 하지만 레플리케이션도 소스 서버에서 장애가 발생시 이를 탐지하고, 레플리카 서버를 소스 서버로 빠르게 승격시켜서 충분히 장애대응을 할 수 있다고 한다.&lt;/p&gt;
&lt;p&gt;우리 팀은 클러스터링의 모든 이점을 갖고 있으며, 더 나아가 많은 이점을 보유하는 레플리케이션을 적용하는 것을 생각중이다. 레플리케이션 외에 성능 개선을 위해 샤딩이나 파티셔닝도 존재하지만, 이들은 빠른 데이터 탐색을 위한 용도이지, 장애 대응이나 부하 분산을 위한 용도로 등장한 기법은 아니라고 생각한다. 레플리케이션으로 높은 가용성을 구축한 뒤, 샤딩과 파티셔닝의 도입을 고려하도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;데이터베이스 첫걸음 - 미크, 기무라 메이지&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/mysql-replication/&quot;&gt;https://hudi.blog/mysql-replication/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=GmGxY93pizc&quot;&gt;https://www.youtube.com/watch?v=GmGxY93pizc&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[고가용성과 확장성을 위한 데이터베이스 클러스터링(DB Clustering)]]></title><description><![CDATA[학습배경 하모니 팀의 모행 서비스는 높은 가용성과 확장성을 확보하기 위해 다양한 성능 개선을 시도하고 있다. 톰캣 서버 튜닝, HikariCP 사이즈, N+…]]></description><link>https://haon.site/database/clustering/</link><guid isPermaLink="false">https://haon.site/database/clustering/</guid><pubDate>Mon, 23 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;하모니 팀의 모행 서비스는 높은 가용성과 확장성을 확보하기 위해 다양한 성능 개선을 시도하고 있다. 톰캣 서버 튜닝, HikariCP 사이즈, N+1 문제 해결, 인덱스 적용등 여러 시도를 하고 있지만, &lt;strong&gt;결국 데이터베이스에서 병목이 발생하면 시스템 전체 성능 저하가 발생&lt;/strong&gt;할 수 있다는 것을 알게되었다. 특히 인덱스 적용, N+1 해결등으로 쿼리 성능이 많이 개선되었지만, 더 최적화할 수 있는 방법은 데이터베이스를 다중화하여 읽기/쓰기 성능을 개선할 수 있다는점을 알게되었다. 즉, 데이터베이스 아키텍쳐를 개선하여 부하 분산 처리가 가능하다. 또 이와 관련한 내용이 바로 클러스터링, 레플리케이션, 샤딩등이 있었다.&lt;/p&gt;
&lt;p&gt;막무가내로 데이터베이스 아키텍처를 개선하는 것은 좋지 않기떄문에, 데이터베이스 다중화에 관련하여 하나씩 특징을 학습하여 우리 팀에 가장 효율적인 DB 다중화 환경을 구성하고자 한다. 이번에는 데이터베이스 다중화  아키텍처 구성방식 중 하나인 클러스터링에 관해 학습해보고자 한다.&lt;/p&gt;
&lt;h2 id=&quot;데이터베이스-클러스터링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81&quot; aria-label=&quot;데이터베이스 클러스터링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 클러스터링&lt;/h2&gt;
&lt;p&gt;일반적인 WAS 애플리케이션은 요청에 대한 로직을 처리중일 떈 일시적으로 데이터를 보존하기도 하지만, 처리가 끝난 후 까지 데이터를 계속 보존할 필요가 없다. 그래서 데이터를 유지하는 매체의 신뢰성이나 다중화에 그다지 신경 쓸 필요가 없다.&lt;/p&gt;
&lt;p&gt;하지만 데이터베이스는 &lt;strong&gt;영속 계층(Persistence Layer)&lt;/strong&gt; 에 위치하여 필요 요건이 WAS 에 비해 많다. 데이터베이스는 대량의 데이터를 영구적으로 보존해야 하고, 그에 따른 성능도 요구되기 때문에 데이터를 보존하는 매체에 필요한 요건이 높은 것이다. 서버 내부의 메모리 계층, 간단한 로컬 저장소등으로는 영속성을 만족시킬 수 없기 때문에 &lt;strong&gt;외부 저장소를 사용하게 된다.&lt;/strong&gt; 스토리지(저장소)란 간단히 HDD 나 SDD 등의 2차 메모리(Secondary Memory) 로 생각하자. &lt;strong&gt;결국, 데이터베이스 서버의 아키텍쳐는 스토리지와 함께 묶여서 설계해야한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;db-서버와-스토리지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#db-%EC%84%9C%EB%B2%84%EC%99%80-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80&quot; aria-label=&quot;db 서버와 스토리지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DB 서버와 스토리지&lt;/h3&gt;
&lt;p&gt;필자가 학습하면서 혼동되었던 점은, DB 서버와 스토리지는 다른 개념이다. 데이터베이스 서버는 SQL 을 실행하고, 스토리지는 데이터를 보존하는 저장소이다. 가령 MySQL 을 사용한다면, MySQL 은 데이터베이스 서버이며, 실제로 데이터가 저장되는 스토리지는 별도로 존재한다.&lt;/p&gt;
&lt;h3 id=&quot;클러스터링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81&quot; aria-label=&quot;클러스터링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클러스터링&lt;/h3&gt;
&lt;p&gt;데이터베이스를 다중화하는 작업을 생각보다 까다로운 작업이다. 데이터베이스내의 데이터는 항상 추가, 수정, 삭제 등이 수시로 발생하기 떄문에 여러 서버에서 &lt;strong&gt;데이터 정합성을 일관되게 보장&lt;/strong&gt;해야 한다. 만약 정합성 고려없이 마구잡이로 서버와 저장소를 늘렸다간, 어이에선 데이터가 추가도고 어디에선 데이터가 없어서 서비스에 장애가 발생할 것이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/856aba39f451637f0290358013510e6e/0d292/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.73619631901841%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABtklEQVR42o2Sy27aQBSGeYlmB7kt2DV9AR4AhQfgEaKo6WukUZs9q2SPkk2yDanUIBVXYYFUqZVTDBgcG5yQEIGNPZe/M2OuUk3yS0dz5szoO5eZBKbiDKyrC/sDPDXB+w0VU8ZIZGrPhU/ncblfUGLmycPBPbxeC8avn3Dbd+haJvo9G6ABeOjDNg10mnUMHnuwWgbI8Akg4xXA0ANlHNptFXv7H3F1VcJo5EUNiEoe+318+XqMw89HaHcskcdfBaQg3otyPx0cYG3tHXK5HCilCial6zpSySTS6TTOzs9VjIxH8UA6Hir39PQE29tbyOfzah8EgVpd1xVJdrGeSuGmXFYxGnjxQB5Gh4QQWNY9SqVvqFS02ZWKpuHi8hJO14nGQMRsY1sWr8b9ZzHHEZiqlMO126hWymjqv9Fp3OHH92s8dC1Zl7rD/YHI7scAI2pkAk5pqCINw8Dmxjo+7LwXVVtRm3TyhbD8Zf4DnItNHuJvvY5MJoNsNgvTNGcvHqdYoFQYhtDE3AqFAorFImq1Gl7TSqCsRH6bqeRj2bYNx3HUKkcg/TcDF8HTNhljS7aYUAL/AZ8EgXP1fJ8zAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/856aba39f451637f0290358013510e6e/a6d36/image.png&quot;
        srcset=&quot;/static/856aba39f451637f0290358013510e6e/222b7/image.png 163w,
/static/856aba39f451637f0290358013510e6e/ff46a/image.png 325w,
/static/856aba39f451637f0290358013510e6e/a6d36/image.png 650w,
/static/856aba39f451637f0290358013510e6e/e548f/image.png 975w,
/static/856aba39f451637f0290358013510e6e/3c492/image.png 1300w,
/static/856aba39f451637f0290358013510e6e/0d292/image.png 1620w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;그렇다면 &lt;strong&gt;데이터 스토리지를 딱 1개만 두고, 여러 데이터베이스 서버만을 다중화&lt;/strong&gt;하여 같은 스토리지를 바라보게 구성하면 어떨까? 이 경우 데이터가 보존되는 저장소가 단 1개라서 정합성을 신경 쓸 필요가 없어진다. 이런 방식을 데이터베이스 클러스터링이라 한다. 이떄, 클러스터란 하나의 기능을 수행하는 집합의 최소 단위를 뜻한다.&lt;/p&gt;
&lt;h2 id=&quot;데이터베이스-클러스터링-종류&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EC%A2%85%EB%A5%98&quot; aria-label=&quot;데이터베이스 클러스터링 종류 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 클러스터링 종류&lt;/h2&gt;
&lt;p&gt;클러스터내의 데이터베이스 서버 여러대 모두를 동시에 활성화(active) 할지, 소수만 활성화하고 나머지는 대기(standBy) 할지에 따라 &lt;strong&gt;Active-Active&lt;/strong&gt; 와 &lt;strong&gt;Active-StandBy&lt;/strong&gt; 로 나뉜다.&lt;/p&gt;
&lt;h3 id=&quot;active-active&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#active-active&quot; aria-label=&quot;active active permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Active-Active&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/17957f305e8999e0ae26d68176699f73/541fe/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.282208588957054%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB20lEQVR42o2SvW4TQRDH8wwgHgMoKfwCoKThPUIkREeDRENlKUIWDXQUnCgCKBUShYVEJIv4IzixiZ2YOHFOtowP3xd3vr3dH3t3tgMGA6MbaW5G+9v/zM4KU1NRgOztIftNlHWMsrsoJXVBu4whFlmM0l+cxfPcha3MgYEL/pBR/wt7O+85qe7TaTb0f08DJygR0Dk+pLFbo3d0SrvdIgo8mHhLgLE+5NmEB4eUPr9js/mAullGBCKty+EAa7eC0XzGdukh4503hEcfwB1OAWoBqBWoyXcIBfnzde52V3kyuJ/VkjYjOHcarJ/d4lHpBpWTp1qdQAbfZoQFoJ5hojKxt9YLNlo3eX6Wh5FNPP6a5p1oxOPuPe50b9MIP2XKJ+6SljWQOEpjIQUeDttbrym/NLSSkFDnW5t5zIOPuPjZ2+g21dIZRvpIqG+LfGTopS10O21qtTJ985TWfo36KwNpW+nFCWjmfwSmQ5UXayJFprZaqXDl8iWuX7vKYDDI2kzWJVmlxKeP8TtwwaTM9qtarZLL5VhdW8M0zendatmx5cB0lkJQLBYpFAoYhkG9Xudf9ldgosTzPKIoIo5jfN/Hsixs22Y8Hs/j/wb+DJ6567pzT2CO4/wC/AGs9op3iTf5nwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/17957f305e8999e0ae26d68176699f73/a6d36/image-1.png&quot;
        srcset=&quot;/static/17957f305e8999e0ae26d68176699f73/222b7/image-1.png 163w,
/static/17957f305e8999e0ae26d68176699f73/ff46a/image-1.png 325w,
/static/17957f305e8999e0ae26d68176699f73/a6d36/image-1.png 650w,
/static/17957f305e8999e0ae26d68176699f73/e548f/image-1.png 975w,
/static/17957f305e8999e0ae26d68176699f73/3c492/image-1.png 1300w,
/static/17957f305e8999e0ae26d68176699f73/541fe/image-1.png 1654w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;클러스터를 구성하는 컴포넌트를 동시에 활성화(active) 한 상태로 운영하는 방식을 &lt;strong&gt;Active-Active&lt;/strong&gt; 라고 한다. 이렇게 모든 서버를 구동시켜 놓으면, 한 서버가 다운되어 불능 상태가 되더라도, 남은 서버가 대신 처리를 도맡아 계속하므로 다운타임이 거의 발생하지 않는다. 또한 &lt;strong&gt;부하 분산이 되기 떄문에 처리 성능이 향상된다&lt;/strong&gt;. 요청을 처리하는 DB 서버 갯수가 증가할수록 동시간대에 가동하는 CPU 나 메모리도 증가하기 떄문에 성능이 향상되며, 부하도 분산된다.&lt;/p&gt;
&lt;h3 id=&quot;한계점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%9C%EA%B3%84%EC%A0%90&quot; aria-label=&quot;한계점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;한계점&lt;/h3&gt;
&lt;p&gt;하지만 여러 DB 서버가 하나의 스토리지를 공유하기 떄문에, &lt;strong&gt;스토리지에서 병목 현상이 발생&lt;/strong&gt;할 수 있고 성능 저하의 원인이 될 수 있다. 이 떄문에 클러스터내에 서버 대수를 늘린다고해서 무조건 서버 대수가 정비례하여 처리 성능이 향상되는 것이 아니다.&lt;/p&gt;
&lt;p&gt;게다가 Active-Active 구성이 가능한 DBMS 는 Oracle 과 DB2 뿐이라고 한다. 다른 DBMS 는 Active-StandBy 구성만 가능하다고 하니, 사용 가능 여부가 DBMS 에 따라서 제한된다.&lt;/p&gt;
&lt;h3 id=&quot;active-standby&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#active-standby&quot; aria-label=&quot;active standby permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Active-StandBy&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/af7c1e2d981737ab179da1b9b94c03f0/bf286/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.21472392638037%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABk0lEQVR42pVSSy8DURTuT/AjPH4HIhGx8gcsSOzEX2DNolas7D0SCwuxIhKNoExajLQmqhqqZhjmdZ+fe+cibWIqbnLmzD2P73z3nJODPlIa5T9C3J+APV2C1S2IDw+CRoAWFgMkhNBCE4gkMHbO0H5yHTeVJKMPVC9KODo+glU6R6P2oEshpgylm1sUCgXY5VNc2xVIlphCWYBSO1stPDsnWHCmsVqfxzt9S30PPsFV4wXxwRjiwwnYlQqEUA4aZgMKGqTajs8xezuKudoYGswxxfQnvAf2esG3e0DqO8ZOgi4MdU8ET7OLyT42y2u4c2pfbZbQhNDcBZwVU0LFStrtyarhYASS8/T+6nrY2lgH51TZmCn2E6zgVQ+7AuqJpSxVINdPEQloEmJmahLjoyO4q9h6csr8boDUtFPgTMB2tl+rFIYhBoeG0dc/AMuyTK+FyEr7DVB2AFbVNJcWF5FfzqNYPMNfJ/dXQBzHYMwsbxRFCIIg1Zq5/ieE/A/wm+23tNSeep4H13XRbDbh+34H4CesZEYGKlZwwgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/af7c1e2d981737ab179da1b9b94c03f0/a6d36/image-2.png&quot;
        srcset=&quot;/static/af7c1e2d981737ab179da1b9b94c03f0/222b7/image-2.png 163w,
/static/af7c1e2d981737ab179da1b9b94c03f0/ff46a/image-2.png 325w,
/static/af7c1e2d981737ab179da1b9b94c03f0/a6d36/image-2.png 650w,
/static/af7c1e2d981737ab179da1b9b94c03f0/e548f/image-2.png 975w,
/static/af7c1e2d981737ab179da1b9b94c03f0/3c492/image-2.png 1300w,
/static/af7c1e2d981737ab179da1b9b94c03f0/bf286/image-2.png 1688w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Active-StandBy 클러스터링 구성에선 일부 데이터베이스 서버를 &lt;strong&gt;대기 상태(StandBy)&lt;/strong&gt; 로 둔다. 평상시 StandBy 상태의 DB 서버는 사용되지 않다가, Active DB 서버에서 장애가 발생했을 떄만 Active 상태로 변경되어 작업을 수행한다.&lt;/p&gt;
&lt;p&gt;StandBy 서버는 Active 서버가 주기적으로 정상 동작하는지 확인한다. 이 주기적인 확인 과정을 &lt;strong&gt;하트비트(HeartBeat) 모니터링&lt;/strong&gt; 이라고 한다. 또한 장애가 터졌을 때 StandBy 가 신속히 장애를 대처하기 위해 상태가 변경되는데, 이를 &lt;strong&gt;장애 극복 기능(FailOver)&lt;/strong&gt; 라고 한다. 말 그대로 실패(Fail) 을 끝내는(Over) 작업이다.&lt;/p&gt;
&lt;h3 id=&quot;한게점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%9C%EA%B2%8C%EC%A0%90&quot; aria-label=&quot;한게점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;한게점&lt;/h3&gt;
&lt;p&gt;하지만, StandBy 상태에서 Active 로 전환되는데 까지 수초에서 수십분가량의 다운타임이 발생한다. 즉, &lt;strong&gt;장애극복(FailOver)&lt;/strong&gt; 하는데까지 다운타임이 발생할 수 밖에 없기 떄문에 가용성이 떨어질 수 있다. 그 대신 Active-Active 방식보다는 비용 절감이 가능하다.&lt;/p&gt;
&lt;h3 id=&quot;hot-standby-cold-standby&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hot-standby-cold-standby&quot; aria-label=&quot;hot standby cold standby permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Hot-StandBy, Cold-StandBy&lt;/h3&gt;
&lt;p&gt;Active-StandBy 방식은 또 다시 &lt;strong&gt;Hot-StandBy&lt;/strong&gt; 방식과 &lt;strong&gt;Cold-StandBy&lt;/strong&gt; 방식 2가지로 나뉜다. &lt;strong&gt;Hot-StandBy&lt;/strong&gt; 방식은 StandBy 서버를 항상 활성화(Active) 상태로 두는 방식이다. Active 서버의 장애 발생시 즉각 FailOver 하여 다운타임을 줄일 수 이지만, 장애가 터졌을시의 다운타임을 최소화하기 위해 평상시 Active 서버를 항상 가동해야 한다는 점에서 비용이 많이 들기에 비효율적이다.&lt;/p&gt;
&lt;p&gt;반면 &lt;strong&gt;Cold-StandBy&lt;/strong&gt; 방식은 평소에 StandBy 를 가동(Active) 하지 않고, Active DB 서버가 장애가 발생하는 시점에 Active 하여 장애 극복하는 방식이다. Hot-StandBy 방식에 비해 비용이 절감디겠지만, 그만큼 장애 발생시 다운타임이 길 것이다.&lt;/p&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;클러스터링 각 구성방식을 가용성과 성능이 좋은 순으로 정리하면 다음과 같다. (성능 관점에선 Hot-StandBy 와 Cold-StandBy 사이에는 큰 차이가 없다.)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; Active-Active&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; Acitve-StandBy (Hot-StandBy)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; Active-StandBy (Cold-StandBy)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;반대로 말하면 가용성이 높을 수록 소요되는 라이센스 비용이 올라간다.&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;다음에는 데이터베이스 레플리케이션에 관하여 학습해보도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;데이터베이스 첫걸음 - 미크, 기무라 메이지&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/database-clustering/&quot;&gt;https://hudi.blog/database-clustering/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@kwontae1313/DB-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81DB-Clustering%EC%9D%B4%EB%9E%80&quot;&gt;https://velog.io/@kwontae1313/DB-클러스터링DB-Clustering이란&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://code-lab1.tistory.com/205&quot;&gt;https://code-lab1.tistory.com/205&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[JPA 낙관적 락과 비관적 락으로 엔티티에 대한 동시성 이슈 해결하기]]></title><description><![CDATA[💡 현재 포스트는 하모니 팀 기술 블로그 에 게시된 글 입니다. JPA…]]></description><link>https://haon.site/database/optimistic-pessimistic-lock/</link><guid isPermaLink="false">https://haon.site/database/optimistic-pessimistic-lock/</guid><pubDate>Sat, 21 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/optimistic-pessimistic-lock/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;jpa-낙관적-락-비관적-락&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jpa-%EB%82%99%EA%B4%80%EC%A0%81-%EB%9D%BD-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BD&quot; aria-label=&quot;jpa 낙관적 락 비관적 락 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JPA 낙관적 락, 비관적 락&lt;/h2&gt;
&lt;p&gt;자바 스프링 기반의 웹 애플리케이션은 기본적으로 멀티 쓰레드 환경에서 구동된다. 따라서 여러 쓰레드가 함께 접근할 수 있는 &lt;strong&gt;공유자원에 대한 경쟁상태(race condintion)&lt;/strong&gt; 가 발생하지 않도록 별도의 제어가 필요하다.&lt;/p&gt;
&lt;p&gt;자바는 &lt;code class=&quot;language-text&quot;&gt;synchornized&lt;/code&gt; 라는 키워드를 제공해, &lt;strong&gt;모니터 기반으로 상호 배제(mutual exclusion)&lt;/strong&gt;	기능을 제공한다. 하지만, 이는 데이터베이스에서 제공하는 락의 종류가 아니다. 일반적으로 웹 애플리케이션에서 공유 자원으로 데이터베이스를 가장 많이 사용하다보니, 데이터베이스에 대한 동시성 문제를 상호 배제를 구현하여 해결한 사례가 많을 뿐이다. 상호배제를 구현한 동시성 제어 방식은 공유 자원 그 자체를 사용하는 특정 &lt;code class=&quot;language-text&quot;&gt;임계 영역(Critial Section)&lt;/code&gt; 에 대해 동시성을 제어하는 것이다.&lt;/p&gt;
&lt;p&gt;이와 달리 JPA 낙관적 락과 비관적 락은 데이터베이스에 락을 거는 기법이다. 정확히는, &lt;strong&gt;데이터베이스내의 특정 행(엔티티)에 대한 락을 걸어 무결성을 유지할 수 있게 해주는 동시성 제어 메커니즘을 뜻한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;트랜잭션-격리수줁&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%81&quot; aria-label=&quot;트랜잭션 격리수줁 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 격리수줁&lt;/h3&gt;
&lt;p&gt;트랜잭션의 ACID 성질 중 격리성(Isolation)을 더 완벽히 보장하기 위해선 성능상 더 많은 손해를 보게된다. DBMS 에서 각 &lt;a href=&quot;https://haon.blog/database/transaction-isolation-level/&quot;&gt;트랜잭션 격리수준&lt;/a&gt;을 얼만큼 격상할지에 따라 4단계로 구분하고 있다. 이때 병행성과 격리성은 서로 반비례한다. 병행성이 높은 격리수준에 가까워질수록 격리성이 낮아지기 떄문에, 성능은 매우 빨라지지만 동시성 이슈에 대해 취약해진다. 반대로 병행성이 낮을수록 격리성이 높아지므로 성능은 저하되지만, 격리성이 보장된다.&lt;/p&gt;
&lt;p&gt;겉보기엔 트랜잭션 격리수준을 가장 높은 수준으로 올렸을 떄 모든 동시성 이슈가 해결될 것 같아보인다. 성능을 버리고, 동시성에 안전해지고 싶어서 일 것이다. 하지만 이런 트랜잭션 격리수준으로도 해결하지 못하는 경우가 다수 존재한다. 해결되지 않는 대표적인 패턴이 바로 &lt;code class=&quot;language-text&quot;&gt;두 번의 갱실 분실 문제(Second Lost Updates Problem)&lt;/code&gt; 인데, 이와 관련한 내용은 아래에서 학습해보도록 한다.&lt;/p&gt;
&lt;p&gt;혼동하지 말아야할점은, 트랜잭션 격리수준은 동시 접근을 제어하기 위해 등장한 개념이 아니라는 점이다. &lt;strong&gt;트랜잭션 격리수준은 한 트랜잭션이 시작된 뒤 끝날 떄 까지 항상 일관된 데이터 값을 읽어오기 위해 적용&lt;/strong&gt;하는 것이다. 즉, 격리수준이 높을수록 트랜잭션 수행동안 연산을 처리하는 데이터가 변하지 않음을 보장할 수 있다. 반대로 격리수준이 낮다면 연산 처리중인 데이터가 도중에 변할 가능성이 크다. 격리수준이 낮다면 그 만큼 다른 트랜잭션이 개입하여 데이터의 일관성을 해칠 위험이 커진다. 정의 그대로 트랜잭션간의 고립성 수준을 설정하는 것이다.&lt;/p&gt;
&lt;p&gt;반면 &lt;strong&gt;JPA 락 메커니즘은 특정 엔티티에 대한 동시 접근을 제어하기 위해 사용한다.&lt;/strong&gt; 트랜잭션 격리수준이 모든 트랜잭션간의 고립성 수준을 제어하는 것이라면, JPA 락 메커니즘은 특정 엔티티에 접근하는 소수의 트랜잭션에 대해서만 격리하고, 동시 접근을 막는 것이다.&lt;/p&gt;
&lt;h2 id=&quot;second-lost-updates-problem-두-번의-갱신-분실-문제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#second-lost-updates-problem-%EB%91%90-%EB%B2%88%EC%9D%98-%EA%B0%B1%EC%8B%A0-%EB%B6%84%EC%8B%A4-%EB%AC%B8%EC%A0%9C&quot; aria-label=&quot;second lost updates problem 두 번의 갱신 분실 문제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Second Lost Updates Problem (두 번의 갱신 분실 문제)&lt;/h2&gt;
&lt;p&gt;왜 트랜잭션 격리수준만으로는 동시성 이슈를 해결할 수 없을까? 격리수준을 높이면 결국 트랜잭션간의 고립성을 높일 수 있으므로, 절대적인 동시성 제어가 가능하지 않을까? 하지만 격리수준은 앞서 설명했듯이 동시성 제어를 위한 것이 아니다. 동시성 이슈에 민감한 송금 관련 트랜잭션을 예시로 들어보겠다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a43876d59d614815e920304bf9a4ea26/d5f92/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.44171779141104%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABiklEQVR42n1S2U7DMBDs/38QTzyAxCGBKOVUgaRAaZumqZ3DOZzLw67bAL1YybJ3tZ4dz7gHDmPs1s4SGEdgFauaUSO0wTlQhejCzBX1SbpgsB09swbTpcbN1TW8ryniOIaUkvYEYulDBhOUZQGtS1RVhZTq768OiqJY8/kF7nUHrTXckQvP85DnGVSSIFEpFlMHL3enWC4FlFLIsgxhGGI4HCKKosOAHGVZWnZ/myLKpzMPbdtaMO5hoPF4jDRNd5/8o4tpaXIEKQRM8oZax6jLBtn8E8Lto6LLHY88zy3bbXYbGrIeg8EAk8kEKgqIhUQoQ8yeH3B/fIRMig3AhCTZFz8MWWzHceD7PhlUI6NLBS0R+CTDSivXdXF5cYbbfp+em+1neEhD/jZV1UDMR3juH5N+uTXs/ekEH+6QNPwio4LDDDunfwFhjYgTRe5/WiMU6cjSxHR+fHyy8vzrMgPyl/jbpNcAnNd1bXd2l9mxTJx3aweQi03TYLvGTPfVeLigX8FrsVjYAd98hFQvKTBdQgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/a43876d59d614815e920304bf9a4ea26/a6d36/image.png&quot;
        srcset=&quot;/static/a43876d59d614815e920304bf9a4ea26/222b7/image.png 163w,
/static/a43876d59d614815e920304bf9a4ea26/ff46a/image.png 325w,
/static/a43876d59d614815e920304bf9a4ea26/a6d36/image.png 650w,
/static/a43876d59d614815e920304bf9a4ea26/e548f/image.png 975w,
/static/a43876d59d614815e920304bf9a4ea26/3c492/image.png 1300w,
/static/a43876d59d614815e920304bf9a4ea26/d5f92/image.png 2150w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;통장에 잔고가 1000원 있는 상황이다. 트랜잭션1이 트랜잭션2 보다 먼저 시작되었다. 트랜잭션1은 잔액 1000원을 읽어오고, 근소한 차이로 다음에 트랜잭션2 도 잔액 1000원을 읽어온다. 이어서 트랜잭션1이 잔고에 3000원을 입금하여 총 잔액을 4000원으로 업데이트하고 커밋한다. 또 이어서 트랜잭션2가 잔고에 500원을 입금할여 잔액을 입금한다. 우리의 기대라면 잔액이 1000 + 3000 + 500 = 4500원이 되어야한다. 하지만, 트랜잭션1의 입금 내역은 분실된다.&lt;/p&gt;
&lt;p&gt;만약 이 문제를 트랜잭션 격리수준으로 해결하고 싶다면 어떻게 해야할까? 우선 MySQL 에서 기본적으로 제공하는 &lt;code class=&quot;language-text&quot;&gt;REPREATABLE READ&lt;/code&gt; 로는 해결될까? 이 격리수준은 트랜잭션이 완료될 떄 까지 SELECT 쿼리가 사용되는 모든 데이터에 &lt;code class=&quot;language-text&quot;&gt;s-lock(공유 락)&lt;/code&gt; 을 거는 수준이다. 즉, 조회한 데이터가 트랜잭션을 실행하는 동안 일관된 값을 읽어오는 정도의 수준을 제공한다. 하지만 이 격리수준에선 위 문제를 여전히 해결할 수 없다.&lt;/p&gt;
&lt;p&gt;이 이상의 격리수준으로 &lt;code class=&quot;language-text&quot;&gt;SERIALIZABLE&lt;/code&gt; 을 설정하면 해결될까? &lt;code class=&quot;language-text&quot;&gt;SERIALIZABLE&lt;/code&gt; 은 한 트랜잭션에서 사용되고 있는 데이터에 다른 트랜잭션에서 절대적으로 수정(WRITE) 할 수 없는 격리수준이다. MySQL 기준으로 &lt;code class=&quot;language-text&quot;&gt;SERIALIZABLE&lt;/code&gt; 은 읽기 작업을 하는 데이터에 대해서 &lt;code class=&quot;language-text&quot;&gt;s-lock&lt;/code&gt; 을 건다.&lt;/p&gt;
&lt;p&gt;위 상황의 경우 두 트랜잭션이 같은 데이터에 대해 &lt;code class=&quot;language-text&quot;&gt;s-lock&lt;/code&gt;을 걸고 그 직후 서로 &lt;code class=&quot;language-text&quot;&gt;x-lock&lt;/code&gt;을 거는 상황이 연출되는데, 이 경우 데드락이 발생한다. &lt;code class=&quot;language-text&quot;&gt;s-lock&lt;/code&gt;과 &lt;code class=&quot;language-text&quot;&gt;x-lock&lt;/code&gt;은 양립할 수 없기 때문이다. 두 트랜잭션은 &lt;code class=&quot;language-text&quot;&gt;x-lock&lt;/code&gt;을 걸기 위해 서로가 &lt;code class=&quot;language-text&quot;&gt;s-lock&lt;/code&gt;을 해제하는 시점을 무한히 대기하며 타임아웃 될 것이다. 결국 데드락으로 인해 동시성 이슈 해결은 불가능하다.&lt;/p&gt;
&lt;p&gt;또한 &lt;code class=&quot;language-text&quot;&gt;SERIALIZABLE&lt;/code&gt; 격리수준은 락을 획득하지 않아도 되는, 락 획득과 전혀 관련없는 &lt;code class=&quot;language-text&quot;&gt;SELECT&lt;/code&gt; 절을 포함한 다른 트랜잭션들 모두가 락을 획득하기 위해 대기하는 문제가 발생한다. 이 떄문에 락을 걸어 동시성 제어가 필요한 기능들 외의 다른 모든 기능들에 성능 저하가 발생한다.&lt;/p&gt;
&lt;p&gt;위와 같이 2개의 트랜잭션에서 동일한 데이터를 변경할 경우 마지막으로 커밋된 내용만이 인정되고, 먼저 커밋된 내용이 분실되는 문제를 &lt;strong&gt;두번의 갱신 분실 문제 (second lost updates problem)&lt;/strong&gt; 라고 부른다. 이와 같이 두번의 갱신 분실 문제와 같은 경우 트랜잭션으로 처리할 수 있는 범위를 넘어선다. 따라서 별도의 방법이 필요하다.&lt;/p&gt;
&lt;h2 id=&quot;낙관적-락-optimistic-lock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%82%99%EA%B4%80%EC%A0%81-%EB%9D%BD-optimistic-lock&quot; aria-label=&quot;낙관적 락 optimistic lock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;낙관적 락 (Optimistic Lock)&lt;/h2&gt;
&lt;p&gt;앞서 설명했듯이 &lt;strong&gt;JPA 락 메커니즘은 특정 엔티티에 대한 동시 접근을 제어하기 위해 사용한다.&lt;/strong&gt; 이 JPA 메커니즘에는 낙관적 락과 비관적 락이 존재한다. 그 중 낙관적 락은 &lt;strong&gt;대부분의 트랜잭션이 충돌(동시성) 이 발생하지 않을 것이라고 낙관적으로 가정하는 방법이다.&lt;/strong&gt; 따라서 데이터베이스가 제공하는 락 기능을 사용하지 않고, 엔티티의 버전 값을 관리하며 동시성을 제어한다. (앞서 이해를 돕기위해 JPA 락 메커니즘은 데이터베이스 특정 행에 락을 거는 것이라 설명했지만, 사실 낙관적 락은 그렇지 않다.)&lt;/p&gt;
&lt;h3 id=&quot;version&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#version&quot; aria-label=&quot;version permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@Version&lt;/h3&gt;
&lt;p&gt;엔티티의 버전을 관리하기 위해 특정 필드에 &lt;code class=&quot;language-text&quot;&gt;@Version&lt;/code&gt; 을 적용한다. 적용 가능한 타입은 숫자 타입, 즉 &lt;code class=&quot;language-text&quot;&gt;Long&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Integer&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Short&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Timestamp&lt;/code&gt; 등이 존재한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Amount&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
  &lt;span class=&quot;token annotation punctuation&quot;&gt;@GeneratedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenerationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; remain&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Version&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; version&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 버전 관리 필드&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Board 엔티티에 변경 사항이 발생할 때 마다 버전 필드의 값이 1씩 증가한다.&lt;/p&gt;
&lt;h3 id=&quot;변경-감지를-어떻게-할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B3%80%EA%B2%BD-%EA%B0%90%EC%A7%80%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;변경 감지를 어떻게 할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;변경 감지를 어떻게 할까?&lt;/h3&gt;
&lt;p&gt;엔티티에 변경사항을 반영하기 위해 트랜잭션을 커밋할 떄, 영속성 컨텍스트를 Flush 하면서 아래와 같이 version 값을 포함한 &lt;code class=&quot;language-text&quot;&gt;UPDATE&lt;/code&gt; 쿼리를 날린다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;UPDATE&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;AMOUNT&lt;/span&gt; 
&lt;span class=&quot;token class-name&quot;&gt;SET&lt;/span&gt;
  remain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  version &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; # 기존 버전값 에서 &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;증가 시킴
&lt;span class=&quot;token class-name&quot;&gt;WHERE&lt;/span&gt;
  id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  and version &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; # 기존 버전 값으로 엔티티를 찾음&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위처럼 엔티티의 버전 값을 1 증가시키고, &lt;code class=&quot;language-text&quot;&gt;WHERE&lt;/code&gt; 절에서 엔티티 조회 시점의 버전으로 데이터를 찾는다. 만약 엔티티가 그 사이에 다른 트랜잭션에 의해 수정되었다면, 그 다른 트랜잭션이 이미 기존 버전 값을 1증가시켰을 것이므로 &lt;code class=&quot;language-text&quot;&gt;WHERE&lt;/code&gt; 문을 통해 엔티티를 찾을 수 없다. 이로 인해 예외가 발생한다.&lt;/p&gt;
&lt;h3 id=&quot;최초-커밋만-인정하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B5%9C%EC%B4%88-%EC%BB%A4%EB%B0%8B%EB%A7%8C-%EC%9D%B8%EC%A0%95%ED%95%98%EA%B8%B0&quot; aria-label=&quot;최초 커밋만 인정하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;최초 커밋만 인정하기&lt;/h3&gt;
&lt;p&gt;위와 같이 엔티티의 변경감지를 &lt;code class=&quot;language-text&quot;&gt;version&lt;/code&gt; 필드를 통해 알아낸다. 다른 트랜잭션에 의해 버전 값이 증가했다면 &lt;code class=&quot;language-text&quot;&gt;WHERE&lt;/code&gt; 문으로 찾을 수 없다. 즉, 버전의 불일치가 발생하여 예외가 발생한다. 이는 다시말해, 먼저 커밋된 (버전 값을 먼저 증가시킨) 트랜잭션만을 성공시킨다. 낙관적 락을 사용하면 &lt;strong&gt;최초 커밋만 인정하기&lt;/strong&gt; 정책을 구현할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;낙관적-락의-lockmodetype&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%82%99%EA%B4%80%EC%A0%81-%EB%9D%BD%EC%9D%98-lockmodetype&quot; aria-label=&quot;낙관적 락의 lockmodetype permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;낙관적 락의 LockModeType&lt;/h3&gt;
&lt;p&gt;엔티티의 특정 필드에 version 어노테이션을 명시해줬다면, 아래처럼 추가적인 락 옵션을 통해 다양한 락을 적용시킬 수 있다. 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;@Lock&lt;/code&gt; 어노테이션으로 LockModeType 를 지정할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LockModeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OPTIMISTIC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; amountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;NONE&lt;/code&gt; : 별도 락 옵션 지정없이도 엔티티에 &lt;code class=&quot;language-text&quot;&gt;@Version&lt;/code&gt; 을 적용했을 때 수행되는 락 옵션이다. 조회한 엔티티를 수정하는 시점에 다른 트랜잭션에 의해 변경되지 않음을 보장한다. 엔티티를 수정하는 시점에 버전 값을 증가시켜는데, 이때 엔티티 버전이 조회 시점과 다르다면 예외가 발생한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;OPTIMISTIC&lt;/code&gt; : &lt;code class=&quot;language-text&quot;&gt;NONE&lt;/code&gt; 의 경우 엔티티를 수정하는 시점에 버전을 체크하지만, 이 옵션은 엔티티를 조회만 해도 버전을 체크한다. 이로써 엔티티의 조회 시점부터 트랜잭션이 끝날 때 까지 다른 트랜잭션에 의해 변경되지 않음을 보장한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;OPTIMISTIC_FORCE_INCREMENT&lt;/code&gt; : 낙관적 락을 사용하면서 버전 정보를 강제로 증가한다. 엔티티가 물리적으로 변경되지 않았지만, 논리적으로는 변경되었을 경우 버전을 증가하고 싶을 때 사용한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;비관적-락-pessimistic-lock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BD-pessimistic-lock&quot; aria-label=&quot;비관적 락 pessimistic lock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비관적 락 (Pessimistic Lock)&lt;/h2&gt;
&lt;p&gt;앞선 낙관적 락은 충돌이 발생하지 않을 것이라고 낙관적으로 가정했다면, &lt;strong&gt;비관적 락은 충돌이 잦을 것이라 발생하고 락을 거는 기법이다.&lt;/strong&gt; 낙관적 락은 단순히 Version 필드 값을 체크하는 방식이기에, DB 락을 사용하지 않는다. 반면 비관적 락을 &lt;strong&gt;실제로 데이터베이스 베타 락을 사용하여 엔티티에 대한 동시성을 제어&lt;/strong&gt;한다. 마찬가치로 &lt;code class=&quot;language-text&quot;&gt;@Lock&lt;/code&gt; 어노테이션을 사용하여 락을 획득할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LockModeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PESSIMISTIC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findByIdForUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; amountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;베타-락&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B2%A0%ED%83%80-%EB%9D%BD&quot; aria-label=&quot;베타 락 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;베타 락&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://haon.blog/database/shared-exclusive-lock/&quot;&gt;MySQL 8.0 공유 락(Shared Lock) 과 배타 락(Exclusive Lock)&lt;/a&gt; 에 설명했듯이, 베타 락은 &lt;code class=&quot;language-text&quot;&gt;FOR UPDATE&lt;/code&gt; 를 사용하여 락을 건다. 락을 획득한 현재 트랜잭션만이 읽기와 쓰기 연산이 가능하며, 다른 트랜잭션은 읽기 쓰기 모두 불가능하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; amount &lt;span class=&quot;token keyword&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;UPDATE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;데드락에-대한-대처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EB%93%9C%EB%9D%BD%EC%97%90-%EB%8C%80%ED%95%9C-%EB%8C%80%EC%B2%98&quot; aria-label=&quot;데드락에 대한 대처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데드락에 대한 대처&lt;/h3&gt;
&lt;p&gt;비관적 락은 베타 락을 사용하기 떄문에 데드락에 빠질 가능성이 높아진다. 만약 두 쓰레드가 서로가 공유 자원을 붙잡고 있고, 상대방이 붙잡고 있는 자원을 획득하길 원하는 경우 데드락에 빠질 위험이 있으니 유의해야한다. 이를 위해 락을 획득하고 있는 최대 시간을 &lt;code class=&quot;language-text&quot;&gt;@QueryHints&lt;/code&gt; 로 저장할 수 있다. 지정한 시간을 초과하면 타임아웃이 발생하고, &lt;code class=&quot;language-text&quot;&gt;PessimisticLockException&lt;/code&gt; 예외를 터뜨린다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LockModeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PESSIMISTIC_WRITE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@QueryHints&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@QueryHint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;javax.persistence.lock.timeout&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;SELECT a FROM amount a WHERE a.id IN :amountId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findByIdForUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; amountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;비관적-락의-lockmodetype&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BD%EC%9D%98-lockmodetype&quot; aria-label=&quot;비관적 락의 lockmodetype permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비관적 락의 LockModeType&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;PESSIMISTIC_WRITE&lt;/code&gt; : 비관적 락이라 함은 일반적으로 이 옵션을 뜻한다. &lt;code class=&quot;language-text&quot;&gt;FOR UPDATE&lt;/code&gt; 를 추가하여 베타 락을 건다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;PESSIMISTIC_READ&lt;/code&gt; : 데이터를 반복 읽기만 하고 수정하지 않을 떄 사용하며, 잘 사용하지 않는 방법이다. &lt;code class=&quot;language-text&quot;&gt;FOR SHARE&lt;/code&gt; 을 추가하여 락을 건다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;PESSIMISTIC_FORCE_INCREMENT&lt;/code&gt; : 비관적 락이지만 유일하게 버전 정보를 사용하며, 버전 정보를 강제로 증가시킨다. &lt;code class=&quot;language-text&quot;&gt;FOR UPDTE NOWAIT&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;FOR UPDATE&lt;/code&gt; 를 추가한다고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;자바 ORM 표준 JPA 프로그래밍, 김영한&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/jpa-concurrency-control-optimistic-lock-and-pessimistic-lock/&quot;&gt;https://hudi.blog/jpa-concurrency-control-optimistic-lock-and-pessimistic-lock/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://expitly.tistory.com/56&quot;&gt;https://expitly.tistory.com/56&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/299&quot;&gt;https://mangkyu.tistory.com/299&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL 8.0 공유 락(Shared Lock) 과 배타 락(Exclusive Lock)]]></title><description><![CDATA[스프링부트와 같이 멀티 쓰레드로 동작하는 애플리케이션에선 공유 자원에 대한  이 발생할 수 있다. 동시 접근이 발생할 경우 일관성과 무결성을 해칠 수 있기 떄문에, 해당 데이터에 대한  을 걸 수 있다. 이  을 거는 방식에는 크게  과…]]></description><link>https://haon.site/database/shared-exclusive-lock/</link><guid isPermaLink="false">https://haon.site/database/shared-exclusive-lock/</guid><pubDate>Fri, 20 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;스프링부트와 같이 멀티 쓰레드로 동작하는 애플리케이션에선 공유 자원에 대한 &lt;code class=&quot;language-text&quot;&gt;경쟁상태(Race Condition)&lt;/code&gt; 이 발생할 수 있다. 동시 접근이 발생할 경우 일관성과 무결성을 해칠 수 있기 떄문에, 해당 데이터에 대한 &lt;code class=&quot;language-text&quot;&gt;잠금(Lock)&lt;/code&gt; 을 걸 수 있다. 이 &lt;code class=&quot;language-text&quot;&gt;잠금(Lock)&lt;/code&gt; 을 거는 방식에는 크게 &lt;code class=&quot;language-text&quot;&gt;공유 락&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;배타 락&lt;/code&gt; 2가지로 나뉘는데, 이들에 대해 학습해보도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;공유-락shared-lock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%B5%EC%9C%A0-%EB%9D%BDshared-lock&quot; aria-label=&quot;공유 락shared lock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;공유 락(Shared Lock)&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;공유 락(Shared Lock)&lt;/code&gt; 은 &lt;code class=&quot;language-text&quot;&gt;읽기 락(Read Lock)&lt;/code&gt; 이라고도 불린다. &lt;strong&gt;공유 락이 걸린 공유 자원에 대해서는 다른 트랜잭션들이 읽기 연산(SELECT) 만 실행 가능하며, 쓰기 연산은 불가능하다.&lt;/strong&gt; 즉, 하나의 데이터를 읽는 것을 여러 사용자가 동시에 할 수 있는 비교적 개방적인 락이다.&lt;/p&gt;
&lt;p&gt;공유 락이 걸린 데이터에 대해서 다른 트랜잭션도 똑같이 공유 락을 획득할 수 있지만, 배타 락은 획득할 수 없다. 즉, 읽기 연산에 대해선 얼마든지 개방적이이지만, 쓰기 연산에 대해서만 엄밀히 제한을 거는 방식이라고 볼 수 있다. &lt;strong&gt;다른 트랜잭션에 의해 쓰기 연산이 절대 발생하지 않으므로, 현재 트랜잭션에서 조회한 데이터가 연산을 처리하는 동안 다른 트랜잭션에 의해 변경되지 않음을 보장한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;베타-락exclusive-lock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B2%A0%ED%83%80-%EB%9D%BDexclusive-lock&quot; aria-label=&quot;베타 락exclusive lock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;베타 락(Exclusive Lock)&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;배타 락(Exclusive Lock)&lt;/code&gt; 은 &lt;code class=&quot;language-text&quot;&gt;쓰기 락(Write Lock)&lt;/code&gt; 이라고도 불린다. 앞선 공유 락에 비해 공유자원에 대한 접근 가능 수준이 엄격한 락이라고 할 수 있다. 공유자원에 대해 베타 락을 획득한 트랜잭션은 읽기 연산과 쓰기 연산 모두 자유롭게 실행할 수 있다. 하지만, &lt;strong&gt;현재 베타 락을 걸은 현재 트랜잭션 이외의 다른 트랜잭션은 모두 읽기 작업도, 쓰기 작업도 수행할 수 없다.&lt;/strong&gt; 즉, 베타 락이 걸린 공유자원에 대해 접근을 시도하려고 하는 다른 트랜잭션들은 공유 락과 배타 락을 걸을 수 없다. 애당초 접근 조차 불가능하므로, 해당 공유 자원에 접근하여 락을 거는 것 조차 불가능하다. 결국 &lt;strong&gt;배타 락을 걸은(획득한) 트랜잭션은 해당 데이터에 대한 전적인 독점권을 갖게된다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;mysql-80-에서-공유-락-배타-락을-어떻게-획득할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mysql-80-%EC%97%90%EC%84%9C-%EA%B3%B5%EC%9C%A0-%EB%9D%BD-%EB%B0%B0%ED%83%80-%EB%9D%BD%EC%9D%84-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%9A%8D%EB%93%9D%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;mysql 80 에서 공유 락 배타 락을 어떻게 획득할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MySQL 8.0 에서 공유 락, 배타 락을 어떻게 획득할까?&lt;/h2&gt;
&lt;p&gt;공유 락과 배타락의 락 옵션은 Auto Commit 이 비활성화 되거나, &lt;code class=&quot;language-text&quot;&gt;BEGIN&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;START TRANSACTION&lt;/code&gt; 명령어를 통해 트랜잭션이 시작된 상태에서만 락이 유지된다. 즉, 일반 연산이 아닌 트랜잭션 단위의 연산에 대해서만 락을 적용할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;공유-락&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%B5%EC%9C%A0-%EB%9D%BD&quot; aria-label=&quot;공유 락 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;공유 락&lt;/h3&gt;
&lt;p&gt;MySQL 8.0 엔진에서 공유 락을 걸고 싶다면 &lt;code class=&quot;language-text&quot;&gt;SELECT&lt;/code&gt; 문 마지막에 &lt;code class=&quot;language-text&quot;&gt;FOR SHARE&lt;/code&gt; 를 사용하여 현재 트랜잭션이 특정 공유 자원에 공유 락을 걸 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; MY_TABLE_NAME &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;SHARE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;배타-락&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%ED%83%80-%EB%9D%BD&quot; aria-label=&quot;배타 락 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배타 락&lt;/h3&gt;
&lt;p&gt;반면 배타 락은 &lt;code class=&quot;language-text&quot;&gt;FOR UPDATE&lt;/code&gt; 를 사용하여 락을 걸 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; MY_TABLE_NAME &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;UPDATE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;락을-걸때-유의할-점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9D%BD%EC%9D%84-%EA%B1%B8%EB%95%8C-%EC%9C%A0%EC%9D%98%ED%95%A0-%EC%A0%90&quot; aria-label=&quot;락을 걸때 유의할 점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;락을 걸때 유의할 점&lt;/h2&gt;
&lt;h3 id=&quot;단순-select-쿼리는-락-획득을-시도하지-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%88%9C-select-%EC%BF%BC%EB%A6%AC%EB%8A%94-%EB%9D%BD-%ED%9A%8D%EB%93%9D%EC%9D%84-%EC%8B%9C%EB%8F%84%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;단순 select 쿼리는 락 획득을 시도하지 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단순 SELECT 쿼리는 락 획득을 시도하지 않는다.&lt;/h3&gt;
&lt;p&gt;MySQL InnoDB 엔진을 사용하는 테이블에선 &lt;code class=&quot;language-text&quot;&gt;FOR UPDATE&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;FOR SHARE&lt;/code&gt; 절을 가지지 않는 &lt;code class=&quot;language-text&quot;&gt;SELECT&lt;/code&gt; 쿼리는 잠금(Lock) 없는 읽기가 지원된다. 즉, 특정 자원이 &lt;code class=&quot;language-text&quot;&gt;FOR UPDATE&lt;/code&gt; 로 락이 걸린 상태일지라해도 &lt;code class=&quot;language-text&quot;&gt;FOR UPDATE&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;FOR SHARE&lt;/code&gt; 가 없는 단순 SELECT 쿼리절은 락 획득을 위한 대기 없이 곧 바로 해당 데이터를 조회할 수 있다. 다시 말하면, 락을 굳이 획득할 필요가 없는 로직이 대기상태에 빠질 일이 없기 떄문에 성능상 손해를 볼 일이 발생하지 않을 것이란 뜻이된다.&lt;/p&gt;
&lt;h3 id=&quot;데드락&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EB%93%9C%EB%9D%BD&quot; aria-label=&quot;데드락 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데드락&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d2ab8a6c2d31a4a0e1d1a9d57bf44640/f793b/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.533742331288344%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABV0lEQVR42oVS7YqCUBDt/V+lH71AtlBrEEHWZupW68cSdFVIV1Ez07MzF1yMahsYLnrPPXPmzPRA0TQNH0jTFJ7n4Xw+oyw5S/k9Gr3BNE3UdY1X0esShkGI9XoN63MLwzCxWn0Q2QiDwQCKouAnjhHHkSx4vV4fFrghzPMch8MBx+ORlH3Ls6KHfM8knutCVVXs93s4jo0oitB9f68wDCXhbrf7A7f3Lcb3fWiaJnFBEPyvkAG2bVOrK5xOp5vq7cm+bqmgEEJiq6q6J2yDfeFMkwQOgdmvpqmlV0zIlnC7PCAhfBRF8VhhV0FCZK7j4H0ywWKhYblcQtd1zGYzuOThZqPLlpnYsiy45PVThW1LWZbJ6rxGhmGg3+9jOp3K/+PxmHKC4VDBfD4n4q/XLbfrwKoLapO95SlX1YWm79L0BSnzIGhAXPQp4eVSSp84u9Fdi1eL/QtUpfxT/qomowAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d2ab8a6c2d31a4a0e1d1a9d57bf44640/a6d36/image.png&quot;
        srcset=&quot;/static/d2ab8a6c2d31a4a0e1d1a9d57bf44640/222b7/image.png 163w,
/static/d2ab8a6c2d31a4a0e1d1a9d57bf44640/ff46a/image.png 325w,
/static/d2ab8a6c2d31a4a0e1d1a9d57bf44640/a6d36/image.png 650w,
/static/d2ab8a6c2d31a4a0e1d1a9d57bf44640/e548f/image.png 975w,
/static/d2ab8a6c2d31a4a0e1d1a9d57bf44640/3c492/image.png 1300w,
/static/d2ab8a6c2d31a4a0e1d1a9d57bf44640/f793b/image.png 1404w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;배타 락을 사용하는 경우 데드락에 빠질 가능성이 높아진다. 만약 두 쓰레드가 서로가 공유 자원을 붙잡고 있고, 상대방이 붙잡고 있는 자원을 획득하길 원하는 경우 데드락에 빠질 위험이 있으니 유의해야한다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=ZXV6ZqMyJLg&quot;&gt;https://www.youtube.com/watch?v=ZXV6ZqMyJLg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/mysql-8.0-shared-lock-and-exclusive-lock/&quot;&gt;https://hudi.blog/mysql-8.0-shared-lock-and-exclusive-lock/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sabarada.tistory.com/121&quot;&gt;https://sabarada.tistory.com/121&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[경쟁 상태의 2가지 패턴 - Read-Modify-Write, Check-Then-Act]]></title><description><![CDATA[경쟁상태(Race Condition…]]></description><link>https://haon.site/database/race-condition-pattern/</link><guid isPermaLink="false">https://haon.site/database/race-condition-pattern/</guid><pubDate>Thu, 19 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;경쟁상태race-condition&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%BD%EC%9F%81%EC%83%81%ED%83%9Crace-condition&quot; aria-label=&quot;경쟁상태race condition permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;경쟁상태(Race Condition)&lt;/h2&gt;
&lt;p&gt;스프링부트 애플리케이션은 멀티 쓰레드로 동작한다. 멀티 쓰레드 환경에서 안전하게 다루어야 할 요소 중 하나는 바로 동시성 이슈이다. 동시성 이슈는 여러 쓰레드가 함께 다루는 데이터, 즉 공유 자원에 대해 발생한다. 이렇듯 &lt;strong&gt;공유 자원에 여러 쓰레드가 동시간대에 접근함으로인해 데이터 정합성 이슈가 발생할 수 있는 상황을 경쟁 상태(Race Condition) 이라고 한다.&lt;/strong&gt; 이번 포스팅에선 경쟁 상태가 발생하는 전형적인 패턴 2가지인 &lt;strong&gt;Read-Modify-Write&lt;/strong&gt;, &lt;strong&gt;Check-Then-Act&lt;/strong&gt; 에 대해 학습해보도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;read-modify-write&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#read-modify-write&quot; aria-label=&quot;read modify write permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Read-Modify-Write&lt;/h2&gt;
&lt;p&gt;Read-Modify-Write 패턴이란 &lt;strong&gt;이전 상태를 기준으로 객체의 현재 상태를 변경하면서 발생하는 문제&lt;/strong&gt;를 뜻한다. 코드로 이해해보자.&lt;/p&gt;
&lt;h3 id=&quot;아이템-관리-서비스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%84%EC%9D%B4%ED%85%9C-%EA%B4%80%EB%A6%AC-%EC%84%9C%EB%B9%84%EC%8A%A4&quot; aria-label=&quot;아이템 관리 서비스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;아이템 관리 서비스&lt;/h3&gt;
&lt;p&gt;아이템을 관리하는 서비스를 가정해보자. 컬렉션으로 아이템을 관리하며, 아이템 잔량을 관리하기 위한 간단히 &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; 변수를 만들었다. &lt;code class=&quot;language-text&quot;&gt;addItem()&lt;/code&gt; 을 호출하면 신규 아이템이 아이템 리스트에 추가되고, count 값이 1증가할 것이다. 여기서 count 변수는 여러 쓰레드가 동시에 접근 가능한 공유자원임을 알고 넘어가자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ItemManager&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; items &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Item&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        count&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; items&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이제 동시간대에 100명의 쓰레드가 아이템을 추가하기 위해 &lt;code class=&quot;language-text&quot;&gt;addItem()&lt;/code&gt; 을 호출한다고 해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; 쓰레드_100개가_동시에_아이템을_추가한다&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;ItemManager&lt;/span&gt; itemManager &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ItemManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Runnable&lt;/span&gt; runnable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            itemManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;신규 아이템&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;runnable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;itemManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드가 실행되면 count 변수 결과값은 얼마일까? 단순하게 생각해본다면 100개의 쓰레드가 100번의 +1 연산을 수행하니, 결과값은 10000이 될 것으로 예상된다. 하지만, 실제 결과값은 기대와 달리 10000이 되지 못한다.&lt;/p&gt;
&lt;p&gt;사실, 공유자원인 count 에 대한 &lt;code class=&quot;language-text&quot;&gt;count++&lt;/code&gt; 연산 내부 동작은 아래와 같은 세부 동작이 발생한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; count 변수의 현재 값을 가져온다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 값을 수정한다. (값을 1증가시킨다)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 수정한 값을 실제로 반영한다 (덮어씌운다)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;쓰레드1과 쓰레드2가 있고, 현재 count 값은 100 이라고 가정해보자. 만약 쓰레드1이 &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 번 까지의 과정 수행을 마치기 전에 쓰레드2 가 count 값을 읽어오면 어떻게 될까? 쓰레드 1이 아직 +1 증가시킨 연산을 아직 실제로 반영하지 못했기 떄문에, 쓰레드2는 반영하기 이전의 기존 값인 100을 읽어오게 된다. 결국 count 의 결과값은 102가 아닌 101이 되어서, 예기치못하게 데이터 정합성 이슈가 발생한다. 이는 &lt;code class=&quot;language-text&quot;&gt;원자성(atomic)&lt;/code&gt; 을 보장하지 못한 결과이다.&lt;/p&gt;
&lt;p&gt;이와 같이 &lt;strong&gt;이전 상태를 기준으로 객체의 현재 상태를 변경하면서 발생하는 데이터 정합성 문제&lt;/strong&gt; 를 &lt;strong&gt;Read-Modify-Write&lt;/strong&gt; 패턴이라고 한다.&lt;/p&gt;
&lt;h2 id=&quot;check-then-act-패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#check-then-act-%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;check then act 패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Check-Then-Act 패턴&lt;/h2&gt;
&lt;p&gt;Check-Then-Act 패턴은 &lt;strong&gt;이전에 검증(Check) 한 결과가 행동(Act) 을 취햐는 시점에는 더 이상 유효하지 않을 때 발생하는 문제&lt;/strong&gt;를 뜻환다. 쉽게말해, &lt;strong&gt;if 조건문(Check) 를 통과하기 이전에는 조건에 부합하는 값이었지만, if 문을 통과한 이후에는 조건에 부합하지 않음으로 인해 데이터 정합성 문제&lt;/strong&gt;가 발생하는 경우를 의미한다.&lt;/p&gt;
&lt;h3 id=&quot;수강신청-관리-서비스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%98%EA%B0%95%EC%8B%A0%EC%B2%AD-%EA%B4%80%EB%A6%AC-%EC%84%9C%EB%B9%84%EC%8A%A4&quot; aria-label=&quot;수강신청 관리 서비스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;수강신청 관리 서비스&lt;/h3&gt;
&lt;p&gt;이번에는 수강신청 관리 서비스를 가정해보자. 최대 100명의 수강신청 인원을 수용할 수 있으며, 한 사람의 수강신청 정보는 &lt;code class=&quot;language-text&quot;&gt;Register&lt;/code&gt; 타입으로 관리된다. 또한 현재까지 수강신청한 인원을 관리하기 위해 &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; 변수를 선언했다. 앞선 예제와 다르게 &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; 값을 최대 100까지 만들 수 있다는점을 인지하자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterManager&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;REGISTER_LIMIT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; registers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addRegister&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Register&lt;/span&gt; register&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;REGISTER_LIMIT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            registers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;register&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            count&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getRegisters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; registers&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 100개의 쓰레드로 &lt;code class=&quot;language-text&quot;&gt;addRegister()&lt;/code&gt; 를 호출한다면 어떻게 될까? 이번에는 과연 &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; 값이 100을 초과하지 않고 문제없이 실행될까? 아쉽게도, count 값은 최대 허용범위인 100을 훌쩍 초과하는 값이된다. &lt;code class=&quot;language-text&quot;&gt;if(count &amp;lt; 100)&lt;/code&gt; 으로 검증한 결과가 &lt;code class=&quot;language-text&quot;&gt;count++&lt;/code&gt; 을 실행하는 시점엔 더 이상 유효하지 않기 떄문에 발생한 문제이다.&lt;/p&gt;
&lt;p&gt;현재 &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; 값이 100 이고, 현재 쓰레드1 에서 아직 &lt;code class=&quot;language-text&quot;&gt;count++&lt;/code&gt; 을 수행한 결과를 반영하지 못했는데, 그 사이에 다른 여러 쓰레드2,3,4들에서 &lt;code class=&quot;language-text&quot;&gt;if(count &amp;lt; 100)&lt;/code&gt; 조건을 검증하면 어떻게 될까? 신기하게도 조건문을 모두 통과해버리게 되고, 결국 수행되선 안될 count++ 이 여러번 수행된다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=ktWcieiNzKs&quot;&gt;https://www.youtube.com/watch?v=ktWcieiNzKs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[데이터베이스 인덱스 B+ Tree 구조는 왜 조회 쿼리 성능이 빠를까?]]></title><description><![CDATA[…]]></description><link>https://haon.site/database/index-basic/</link><guid isPermaLink="false">https://haon.site/database/index-basic/</guid><pubDate>Tue, 17 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ca06cfcc2ad56d2ca96f7b49df9edfa7/8ae78/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.50920245398773%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACn0lEQVR42iVTyXLaQBTk/yvnHJNbKq6kKjGu2GY3BiQhIY1m0C60IBCrsfMHnZZymMNs/bpf9+vEqcDplCCObWwrH1G0gqYPIdUCRSm41/Dz7jO+fvkEz58jzwUOxwSHQ4wilxDuon1b7XzY9gyd5qJZWSZRH0Ks1xp8f0nwNYrCgRADXA+vkPY9PDXCYWvisLNxPUfYbCQcMUdZrnG55litCHi5ZARMkKZuW8VxXluwaqeQJAaScIJj1YM0vyNWvf+A1QpvpwBR7MD3ligLhdt7iaX5gs71usF+H6HceqzotuzqOuBeIAwoMZ2gyp4g7AG2W4X3W459HeNEEs3bPHNxPie4vuXQjTElHxt2gqAhgtCG8gx+9ChDQMoxdsUYkfcAxx7icgrxQcCK94e2PTo8rh2V3Xiu6yN09nWIhMzK0kXqD5AnC1ZdIS8E+zfCcTeBdO5hGH3UOw/nY2OGwpGAUmpIUptkPLzdCiy0ITrb7RphZFGugL9uJM7hiweEbhdKPLJnA9hWF9Kd4XSMcDlvkNGMI4mY1pTpsFBVqjVmvuijk5eKDruUazIuI8SJQJa7LVgkf2GXP0FfdJFEZgtS13SXBjZGGMsJgmDJNLhsQYTXWQ+dOHGQFRJKae1lltn8YMGyBoiDHrLoAZr2iKqUuNHAbem1PU+YW9OaEEy2JjaAswbQDy0GeE3LxwzpDOlmxTDrbPAfFGkPoXrAYv7M/vnM3qYNf5o0cTGgaEi99ynZozEhARvJrJCTvhAz9kG1DONYZ9+esS/6DPMjTBY7M/x/P0q+8TgtLotaPH9pwZpJa6KkGzSlGRtdH6DX/0WZY47PGNOXe4wG3+DLHxiP7jCfPSNujXM4DVOGf4o5Wff7v1ung8CEzzUcdvEPbutCoc6qzKIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/ca06cfcc2ad56d2ca96f7b49df9edfa7/a6d36/image-1.png&quot;
        srcset=&quot;/static/ca06cfcc2ad56d2ca96f7b49df9edfa7/222b7/image-1.png 163w,
/static/ca06cfcc2ad56d2ca96f7b49df9edfa7/ff46a/image-1.png 325w,
/static/ca06cfcc2ad56d2ca96f7b49df9edfa7/a6d36/image-1.png 650w,
/static/ca06cfcc2ad56d2ca96f7b49df9edfa7/e548f/image-1.png 975w,
/static/ca06cfcc2ad56d2ca96f7b49df9edfa7/8ae78/image-1.png 1096w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;구매한 책을 읽을때마다, 필요한 키워드를 빠르게 찾고 싶다면 책 뒷편의 부록 페이지에서 인덱스를 살펴볼 것이다. 만약 인덱스가 없더라면, 책에 실린 수많은 텍스트에서 내가 원하는 키워드를 찾아내기 위해 최악의 경우 책 맨 앞에서부터 끝까지를 살펴봐야 한다.&lt;/p&gt;
&lt;h2 id=&quot;데이터베이스-인덱스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%9D%B8%EB%8D%B1%EC%8A%A4&quot; aria-label=&quot;데이터베이스 인덱스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 인덱스&lt;/h2&gt;
&lt;p&gt;데이터베이스 인덱스는 책 뒷편의 인덱스와 유사한 특징을 가진다. 둘의 공통점은, &lt;strong&gt;내가 원하는 특정 데이터를 빠르게 탐색하기 위해 미리 정렬해놓은 인덱스 정보를 활용하는 것이다.&lt;/strong&gt; 인덱스를 통해 원하는 데이터가 저장소 내부 어디에 있는지의 정보를 빠르게 서칭하고, 해당 페이지로 이동하여 데이터를 조회할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;즉, 데이터베이스 인덱스란 빠른 조회(SELECT) 성능을 위해 미리 저장해놓은 추가적인 저장 공간이다.&lt;/strong&gt; 마치 책에서 특정 인덱스가 책 본문의 130번 페이지에 원하는 데이터가 존재함을 표현하듯이, 데이터베이스 인덱스는 key 가 20번인 데이터는 테이블 내의 35번째 행에 저장되어 있음을 표현한다.&lt;/p&gt;
&lt;h3 id=&quot;조회select-성능-최적화를-위한-b-tree-구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A1%B0%ED%9A%8Cselect-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94%EB%A5%BC-%EC%9C%84%ED%95%9C-b-tree-%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;조회select 성능 최적화를 위한 b tree 구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;조회(SELECT) 성능 최적화를 위한 B+ Tree 구조&lt;/h3&gt;
&lt;p&gt;인덱스는 다양한 방식의 인덱싱 자료구조 저장 형태를 취할 수 있다. 자료구조 종류로는 &lt;code class=&quot;language-text&quot;&gt;해싱(Hashing)&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;B- Tree&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;B+ Tree&lt;/code&gt; 등의 형태를 취하지만, 현재 대부분의 데이터베이스에서 취하는 구조는 &lt;code class=&quot;language-text&quot;&gt;B+ Tree&lt;/code&gt; 이다. 인덱싱 자료구조 및 알고리즘에 대한 내용은 이따가 자세히 학습해보도록 하자.&lt;/p&gt;
&lt;p&gt;인덱스는 &lt;code class=&quot;language-text&quot;&gt;B+ Tree&lt;/code&gt; 자료구조 형태의 저장방식을 취함으로써, 항상 데이터간의 정렬된 상태를 유지한다. &lt;strong&gt;이 구조를 통해 데이터의 저장(INSERT, DELETE, UPDATE) 쿼리 성능을 희생한 대신에, 조회(SELECT) 쿼리 성능을 최적화했다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;인덱스를-왜-사용하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;인덱스를 왜 사용하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스를 왜 사용하는가?&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d80c15b735e1f75050bc592c09e1edfb/1dbe8/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.65030674846626%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABOElEQVR42n1SyZaCQAzk/z9sbnPRG5tssik0oKwCZVfG9jlzmLwX0uRVqqoDFv7Evu/oxwnDNMn7tm1Y11Xy8Xi8z+ybZJ+VYZGAuW27NOqmxdf3Ea4fIoljFEWB2+2Gvu8xDIPUcRzRti1OpxOyLEMYhoITwk93y7JANQ2SJMHlUiJNM5TlRQ9EqKoKk3ZthIntug6NxlOApoTwer0iz3NEUahrBtt20L0cUbUsS6mO6yHQTkhMx5zzPA++78vZhEUAh4Ig+LEfxcjSVAtEAnYdR4C5Jj2fz1CqxjwvIkg89/cZFnfBYdd1JQ+Ho1b2ZYDXmedZE1EgFidKKbke+5xl/UXIB5u2bWuyA+q60qlkb7HeJSuXn2rXdHS/32VnFOOt+G7+jvdXZpivyMUTxLOp46tvsP/FExhoZINSgIHyAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d80c15b735e1f75050bc592c09e1edfb/a6d36/image-2.png&quot;
        srcset=&quot;/static/d80c15b735e1f75050bc592c09e1edfb/222b7/image-2.png 163w,
/static/d80c15b735e1f75050bc592c09e1edfb/ff46a/image-2.png 325w,
/static/d80c15b735e1f75050bc592c09e1edfb/a6d36/image-2.png 650w,
/static/d80c15b735e1f75050bc592c09e1edfb/e548f/image-2.png 975w,
/static/d80c15b735e1f75050bc592c09e1edfb/3c492/image-2.png 1300w,
/static/d80c15b735e1f75050bc592c09e1edfb/1dbe8/image-2.png 1374w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;만약 인덱스가 없다면, 우리가 원하는 데이터를 찾기 위해 &lt;code class=&quot;language-text&quot;&gt;테이블 풀 스캔(Table Full Scan)&lt;/code&gt; 이 발생한다. &lt;strong&gt;테이블 풀 스캔이란, 우리가 원하는 특정 데이터를 찾아내기 위해 데이터베이스의 테이블의 맨 앞부터 끝까지 차례대로 모든 행을 읽어내는 방식이다.&lt;/strong&gt; 테이블 풀 스캔은 결국 최악의 경우 처음부터 끝 까지 모든 데이터를 탐색해야 하므로, 디스크에서 데이터를 읽어 메모리로 적재하는 I/O 비용이 많이 발생하는 가장 느린 테이블 스캔 방식이다. 데이터베이스는 디스크내에 내장되어 있기 떄문에, 디스크에 직접 접근하여 스캔을 하는 방식대신에 디스크 접근 연산을 최소화하는 방식이 효율적이다.&lt;/p&gt;
&lt;p&gt;애당초에 디스크에 접근하여 수행하는 연산은 매우 느리다. 디스크 내부적으로 데이터를 쓰거나 읽기위해 매번 디스크 헤더를 이동시키고, 또 다시 다른 데이터에 대한 연산 처리를 위해 헤더를 이동시켜야 하므로, 비효율적인 연산 구조를 취할 수 밖에없다. 이와 관련한 자세한 내용은 &lt;strong&gt;순차 I/O 와 랜덤 I/O 의 차이점&lt;/strong&gt;에 관련한 내용인데, 향후 다른 포스트에서 자세히 다루도록 하겠다.&lt;/p&gt;
&lt;p&gt;반면 인덱스만을 메모리에 &lt;code class=&quot;language-text&quot;&gt;B+ Tree&lt;/code&gt; 형태로 적재하고, 원하는 데이터를 디스크에서 빠르게 찾아낼 수 있다. &lt;strong&gt;즉, 인덱스를 사용하면 디스크에 접근하여 수행하는 불필요한 I/O 를 최소화하여, 데이터 탐색 성능을 개선할 수 있다.&lt;/strong&gt; 시간 복잡도 측면에서 테이블 풀 스캔이 최악의 경우 &lt;code class=&quot;language-text&quot;&gt;O(N)&lt;/code&gt; 의 시간 복잡도를 가진다면, 인덱스는 B+ Tree 구조를 통해 검색 연산시 &lt;code class=&quot;language-text&quot;&gt;O(logN)&lt;/code&gt; 의 시간복잡도를 가진다. 게다가 인덱스는 자신이 가리키고 있는 실제 데이터에 크기에 비해 매우 작은 크기를 가진다. 따라서 실제 데이터베이스 테이블에 비해 적재하기 용이하다.&lt;/p&gt;
&lt;h2 id=&quot;인덱스-자료구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;인덱스 자료구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스 자료구조&lt;/h2&gt;
&lt;p&gt;인덱스를 저장하는 자료구조 종류로 &lt;code class=&quot;language-text&quot;&gt;해싱(Hashing)&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;B- Tree&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;B+ Tree&lt;/code&gt; 등의 형태를 취하지만, 현재 대부분의 데이터베이스에서 취하는 구조는 &lt;code class=&quot;language-text&quot;&gt;B+ Tree&lt;/code&gt; 이라고 설몀했다. 각 자료구조의 특징은 어떻게 되길래 현재 대부분의 데이터베이스에서 B+ Tree 구조를 취하게 됐을까?&lt;/p&gt;
&lt;h3 id=&quot;해시-테이블-hash-table&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B4%EC%8B%9C-%ED%85%8C%EC%9D%B4%EB%B8%94-hash-table&quot; aria-label=&quot;해시 테이블 hash table permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;해시 테이블 (Hash Table)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/42aeba2c5ab34119005cf5f7ed60acc6/f3c12/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.34969325153374%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACWklEQVR42m2S208TURDG+Y989kH/EF81PJnwygMmJpAAGpRA4iMYJMrF+GLC1aByKU0pbZfSlmvvt+1tt9tuS7vb/pxWaagwm2/3zNk538yZ+QZ4wNrtdvdbLJWIxmOUjXJv//ZfL/bfc2sDtwvbtkkmk6iq2j1Uq9W4Or/A7XRxfX6Jbdl/4yyLrJolnU6TSCYwa2aPuI+wIYRX0ShXkQhNIaxIoD92QSgTwRMJYtRN7HYTzSgQCARQFIUT/0mXtFPMvQr/N1sybtZCLBnHfK36yDR0riMJfhwd4kwf4kg6OIjvsxvelSRaryUD8uZanKe6zmO5xpNMhkfZLFO2xXbWzcf0PnPhX3hjCjtrBea/XTIWfc1IaIRhZZiJyASFWuEOoZguWKrX+Sx9WxIsCFwtmzX1mA+hDeaF0J8KsreTZvG7n5nwNO/OpngbfMPsxSxFs9hPmBYM3dzwUisxVCryQtNYabdYT7lYUB2s6h7ydpV8ScVz7iGUDBFIBAgmgpxGTzGqRj9hXvC+0WCqXGZaMF6tsi1X3ioqfNHcLBePUJtySKouV3S0ska+mO+is7Yktm8oOcG4aTImuhuvVHila6w3btjKe/mkHrJSdFNomWTzOVxBL76UD2/SizflxRPzSJJy/1CS4gwK0XMZyKCQPsvlWBTZbOY8zCV+s5DYE+ko7P3MsLxxxrT0b1KZZNQ5ykxo5n4PH5SN9HDLCLKqHbParbBKIq2y6zvCGXfiiDlwxAXhA3RD7yfsaNwS5y46UjWsOppVQ7drsteSJBb1polp9uOusP8AZx5wSFTrvycAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/42aeba2c5ab34119005cf5f7ed60acc6/a6d36/image-3.png&quot;
        srcset=&quot;/static/42aeba2c5ab34119005cf5f7ed60acc6/222b7/image-3.png 163w,
/static/42aeba2c5ab34119005cf5f7ed60acc6/ff46a/image-3.png 325w,
/static/42aeba2c5ab34119005cf5f7ed60acc6/a6d36/image-3.png 650w,
/static/42aeba2c5ab34119005cf5f7ed60acc6/f3c12/image-3.png 764w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;자료구조 전공 수업을 들었다면 한번쯤은 학습해봤을 자료구인 &lt;code class=&quot;language-text&quot;&gt;해시 테이블&lt;/code&gt; 이다. &lt;strong&gt;해시 테이블은 Key-Value 형태로 데이터르 저장하는 자료구조다.&lt;/strong&gt; 데이터의 key 값을 알고 있다면, 데이터에 &lt;code class=&quot;language-text&quot;&gt;O(1)&lt;/code&gt; 라는 빠른 시간 복잡도로 접근할 수 있다.&lt;/p&gt;
&lt;h4&gt;대소비교 연산으로 인해 느리다&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;하지만, 해시 테이블은 대소비교 연산에 부적합하기 때문에 DBMS 에서 잘 사용되지 않는 자료구조이다.&lt;/strong&gt; 해시 테이블의 데이터는 정렬되어 있지 않아서, Key 값이 어떤 기준값보다 크거나 또는 작은 데이터를 찾기 위해선 모든 데이터에 접근해야한다. 즉, N개의 데이터가 있다면 N번의 데이터 접근이 필요하다. 즉, 해시 테이블의 조회 연산은 최악의 경우 &lt;code class=&quot;language-text&quot;&gt;O(n)&lt;/code&gt; 의 시간이 걸린다. 데이터베이스에서는 대소비교 연산이 자주 발생하기 때문에 해시 테이블은 적합하지 않다.&lt;/p&gt;
&lt;h3 id=&quot;b--tree&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#b--tree&quot; aria-label=&quot;b  tree permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;B- Tree&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6b5e2a92c11bf42509e37c8b1ca7d64f/1dbe8/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.012269938650306%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABgUlEQVR42oWSW0/bQBCF8/9/R5/70AdUodKmLWqLQFULhBAbhL252CSOGyfrW7Lrj7Ed2aqExEqzY8/ZOTN7Zge8uqpmt4cMvbpGxy56PZGw4a01eJXOWsk1lLsV3ugj7t9PTJ1zbL6VInvBzduEVVUJyQGT78Q0psgkZtmXBWWqsTUuRKbMBEvljJDLd11ctp6wJqqXju4InTN2y9sOPJQbZqP3LCS+fBpK4r7D8m2A//sd6u6UlXdeC3Ts8EiYJ0vCuU+xi7okK92FakKgHomXczA94UFuEiiHhXJJoqDrctBWU2yfxzKAP/xb3FDqgHTjs/a+kIS3xNMLNuG1+CuS5xpfEKlLNrOfbFcPxOo78eyqJ0wiF8/5JWQXPIy+kiZT1uG9DOSE+eMPvPEpajIU7DPK+UaWKJybM9T4BN+9xB994Ol+2BPWOppGXLmVsd2Q0rRApzlp1not3tqqUanxYu1/6/spNzpW3ftrXUVZ5GRZRp63Vu7L/88e9e/ygBe5prM8bgOTRAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/6b5e2a92c11bf42509e37c8b1ca7d64f/a6d36/image-4.png&quot;
        srcset=&quot;/static/6b5e2a92c11bf42509e37c8b1ca7d64f/222b7/image-4.png 163w,
/static/6b5e2a92c11bf42509e37c8b1ca7d64f/ff46a/image-4.png 325w,
/static/6b5e2a92c11bf42509e37c8b1ca7d64f/a6d36/image-4.png 650w,
/static/6b5e2a92c11bf42509e37c8b1ca7d64f/e548f/image-4.png 975w,
/static/6b5e2a92c11bf42509e37c8b1ca7d64f/3c492/image-4.png 1300w,
/static/6b5e2a92c11bf42509e37c8b1ca7d64f/1dbe8/image-4.png 1374w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;B-Tree&lt;/code&gt; 자료구조는 이진트리와 달리 자식 노드를 여러개 가질 수 있는 &lt;code class=&quot;language-text&quot;&gt;균형 트리(Balanced Tree)&lt;/code&gt; 구조이다. 즉, 이진 탐색 트리의(BST) 에서 자식 노드 개수가 2개 이상으로 확장된 트리 구조다. 이진 탐색 트리의 자식 노드가 최대 2개라면, B-Tree 는 자식 노드가 2개 이상인 트리이다. &lt;strong&gt;이진 탐색 트리에서 노드의 개수를 늘리고 트리의 전체 높이를 줄임으로써 매우 빠른 탐색 성능을 얻을 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;노드(node)의 종류&lt;/h4&gt;
&lt;p&gt;트리 내부 노드들의 종류로는, 우선 가장 최상위에는 루트 노드가 존재하며, 맨 하위 계층에는 리프 노드가 존재한다. 또한 이 중간 계층에는 브랜치 노드가 존재한다.&lt;/p&gt;
&lt;h4&gt;Key-Value 구성&lt;/h4&gt;
&lt;p&gt;일반적인 이진 트리와 달리, 한 노드에 여러 데이터를 가질 수 있다. &lt;strong&gt;B- Tree 구조의 각 노드(node) 는 테이블의 특정 데이터 레코드 행(ex. PK  - 기본 키)을 타킷으로 해서, 헤당 행의 값을 key 로, 해당 레코드가 저장된 주소(포인터) 를 value 로 저장하는 일련의 key-value 쌍으로 구성된다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;특히 Key 값의 경우 아무 값이 저장되는 것이 아니라 인덱스가 적용된 컬럼의 값이 저장된다.&lt;/strong&gt; 예를들어, 테이블의 기본 키(PK) 또는 기타적으로 인덱스를 생성한 컬럼의 값이 Key 값으로 저장된다. 이러한 B-Tree 트리내의 노드들은 마구잡이로 생성되는 것이 아니라, 항상 key 값을 기준으로 오름차순으로 정렬된 상태를 유지한다. 이는 다시말해, &lt;strong&gt;새로운 인덱스가 생성될 때 마다 오름차순 정렬 상태를 유지하기위해 트리내의 노드의 재정렬이 필요함을 뜻한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;반면 Value 값의 경우 Key 에 해당하는 실제 데이터의 물리적 위치 정보를 저장한다. &lt;strong&gt;즉, Value 값은 Key 에 해당하는 실제 데이터 레코드를 가리키는 포인터를 저장한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;각 노드는 좌우로 다른 노드를 가리키는 포인터를 가지고있다. 좌측 포인터는 Key 보다 작은 데이터를 가진 노드를 가리키고, 우측 포인터는 Key 보다 큰 데이터를 가진 노드를 가리킨다.&lt;/p&gt;
&lt;h4&gt;O(logN) 으로 빠르다&lt;/h4&gt;
&lt;p&gt;B-Tree 는 정렬된 트리 구조이므로 특정 노드를 찾는데 &lt;code class=&quot;language-text&quot;&gt;O(logN)&lt;/code&gt; 의 시간이 걸린다.&lt;/p&gt;
&lt;h3 id=&quot;b-tree&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#b-tree&quot; aria-label=&quot;b tree permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;B+ Tree&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f66b675646ddd40c6f96a06d0d774e0d/d4e7f/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 35.58282208588957%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABbUlEQVR42mWQW2/TQBCF/f//BRJCPMBD+4Dal0ogJCiISq5UipUmjlMnvt+yznrjtf0xcbmocDQjzc6eOXNxmDGJ/fJTLDAqJvEv2K4+UITXTLbjP/xTM0rsPCOMA2PfMeg9VjeYNkPXMb1RTL2Z8yef7JFpsMIfn5f/FuxNjSpW6DIQQSELcRwMqlyJB7S5z353J4OMYgOD0YzmgMqWNIlHlS+wRzXPOQu2hUf2eI3KvT/djm1K/HBJGn5l554Rfn4pgsPfVQXl9oYk+ERwd45uHuecM0nX03q2U7JOzTRP0dOrjOF4kL+n9UdrsCpnkJNMIjy/D/UTpztxJJZTOKq4J/I/knpnbLwrVHhLHX5j7b4gWlyRPFywdF9R+O8Jvp/j319Sbr6Qyt/SfUP44x2++5rFzVuK6BbH9pqqzDDSoSwS6dihD3vqKqbIY9I4JNqtqYqYcLNku10Ld49ua+oyoakzmioVbkSnFT8B1mcUGSghCiYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/f66b675646ddd40c6f96a06d0d774e0d/a6d36/image-6.png&quot;
        srcset=&quot;/static/f66b675646ddd40c6f96a06d0d774e0d/222b7/image-6.png 163w,
/static/f66b675646ddd40c6f96a06d0d774e0d/ff46a/image-6.png 325w,
/static/f66b675646ddd40c6f96a06d0d774e0d/a6d36/image-6.png 650w,
/static/f66b675646ddd40c6f96a06d0d774e0d/e548f/image-6.png 975w,
/static/f66b675646ddd40c6f96a06d0d774e0d/3c492/image-6.png 1300w,
/static/f66b675646ddd40c6f96a06d0d774e0d/d4e7f/image-6.png 1416w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;B+ Tree 는 B- Tree 에서 개선된 구조로, 대부분의 DBMS 에서 채택중인 자료구조이다.&lt;/p&gt;
&lt;p&gt;앞선 B- Tree 와 달리, 같은 깊이(depth) 에 존재하는 노드들은 서로 링크드리스트 구조로 연결된 형태를 취한다. 이에따라, 리프 노드끼리도 더블리 링크드리스트로 연결된 구조를 지닌다. &lt;strong&gt;즉, B+ Tree 는 리프 노드끼리 서로를 포인터로 연결하는(가리키는) 링크드 리스트 구조를 취한다. 그리고 이 링크드리스트 구조로 인해 범위 탐색(Range Scan) 의 성능이 매우 빠르다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;리프-노드끼리-링크드-리스트-구조로-연결된다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%ED%94%84-%EB%85%B8%EB%93%9C%EB%81%BC%EB%A6%AC-%EB%A7%81%ED%81%AC%EB%93%9C-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EA%B5%AC%EC%A1%B0%EB%A1%9C-%EC%97%B0%EA%B2%B0%EB%90%9C%EB%8B%A4&quot; aria-label=&quot;리프 노드끼리 링크드 리스트 구조로 연결된다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리프 노드끼리 링크드 리스트 구조로 연결된다&lt;/h3&gt;
&lt;p&gt;B+ Tree 는 B-Tree 와 달리 리프 노드끼리 링크드 리스트를 취하기 때문에, 1개가 아닌 여러 데이터를 조회하는 &lt;code class=&quot;language-text&quot;&gt;레인지 스캔(Range Scan)&lt;/code&gt;, 즉 범위 스캔이 유리하다. 데이터배이스 특성상 대소비교 연산이 다소 오래걸리기에, B-Tree 구조 또한 원하는 데이터를 탐색하기 위해 발생하는 여러번의 대소비교 연산 오버헤드를 무시할 수 없었다. 이를 해결하기 위해, B+ Tree 에선 리프 노드를 &lt;code class=&quot;language-text&quot;&gt;링크드 리스트(Linked List)&lt;/code&gt; 로 연결했다. 즉, 정렬된 상태의 리프 노드 중에서 원하는 몇몇 데이터(노드)들을 맨 앞에서부터 차례대로 빠르게 &lt;code class=&quot;language-text&quot;&gt;순차 탐색(Sequential Search)&lt;/code&gt; 으로 찾아낼 수 있게된다.&lt;/p&gt;
&lt;p&gt;만약 B- Tree 를 레인지 스캔하면 어떤 과정이 일어날까? 원하는 데이터 N개를 찾아내기 위해, 각 데이터에 대해 매번 루트 노드부터 시작하여 리프 노드까지 대소 비교를 하는 연산이 N번 발생할 것이다. 반면, B+ Tree 의 경우 루트 노드에서 특정 리프 노드에 한 번 도달한 뒤, 해당 리프 노드에서부터 시작해서 링크드리스트를 따라 넘어가면서 여러 리프노드를 따라 순차 탐색하면 된다.&lt;/p&gt;
&lt;h2 id=&quot;인덱스를-모든-컬럼에-적용해도-될까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%EB%AA%A8%EB%93%A0-%EC%BB%AC%EB%9F%BC%EC%97%90-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%8F%84-%EB%90%A0%EA%B9%8C&quot; aria-label=&quot;인덱스를 모든 컬럼에 적용해도 될까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스를 모든 컬럼에 적용해도 될까?&lt;/h2&gt;
&lt;p&gt;궁금증이 하나 생긴다. 이런 장점을 지닌 인덱스를 데이터베이스 테이블내에 적용하면 조회 쿼리 성능이 매우 좋아지는게 아닐까? 인덱스를 항상 모든 테이블에 적용하면 안될까? 하지만, 이런 인덱스도 이곳저곳에 모두 적용하고 남발하면 되려 역효과를 볼 수 있다.&lt;/p&gt;
&lt;p&gt;인덱스를 생성하면, 아무곳에나 생성되는 것이 아니라 데이터베이스내에서 인덱스가 생성되는 특정 공간이 고정되어있다. 이 저장 공간은 데이터베이스 전체 공간에서 최대 약 10% 만을 허용한다. &lt;strong&gt;즉, 인덱스를 생성할 수 있는 저장공간의 크기는 매우 작기 때문에, 잘못 사용하면 불필요한 저장 공간을 낭비할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;카디널리티cardinality-를-고려하여-정말-필요한-컬럼만-인덱스를-적용할-것&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B9%B4%EB%94%94%EB%84%90%EB%A6%AC%ED%8B%B0cardinality-%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EC%97%AC-%EC%A0%95%EB%A7%90-%ED%95%84%EC%9A%94%ED%95%9C-%EC%BB%AC%EB%9F%BC%EB%A7%8C-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%EC%A0%81%EC%9A%A9%ED%95%A0-%EA%B2%83&quot; aria-label=&quot;카디널리티cardinality 를 고려하여 정말 필요한 컬럼만 인덱스를 적용할 것 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;카디널리티(Cardinality) 를 고려하여 정말 필요한 컬럼만 인덱스를 적용할 것&lt;/h3&gt;
&lt;p&gt;따라서 인덱스는 정말 필요한 컬럼에 대해서만 적용하는 것이 바람직하다. 그렇다면, 인덱스를 효율적으로 적용하기 위한 기준은 무엇이 있을까?&lt;/p&gt;
&lt;p&gt;바로 &lt;code class=&quot;language-text&quot;&gt;카디널리티(Cardinality)&lt;/code&gt; 이다. 카디널리티란 데이터 집합에서 유일한 데이터 개수를 뜻한다. 즉, 데이터 특성상 유일성이 높은 컬럼일수록 인덱싱해주는 것이 좋다. 예를들어, 성별 컬럼보다 주민등록번호 컬럼의 카디널리티가 매우 높을 것이다. 성별은 모든 사람이 남/여 2가지 값 중 하나를 가지기 때문에, 유일성 수치가 매우 낮다. 반면 주민등록번호는 단 1명의 사람에게 보장되는 유일성을 가지기 떄문에 카디널리티가 높다.&lt;/p&gt;
&lt;h3 id=&quot;삽입-수정-삭제가-자주-발생하는-컬럼에는-적용하지-말-것&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%82%BD%EC%9E%85-%EC%88%98%EC%A0%95-%EC%82%AD%EC%A0%9C%EA%B0%80-%EC%9E%90%EC%A3%BC-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EC%BB%AC%EB%9F%BC%EC%97%90%EB%8A%94-%EC%A0%81%EC%9A%A9%ED%95%98%EC%A7%80-%EB%A7%90-%EA%B2%83&quot; aria-label=&quot;삽입 수정 삭제가 자주 발생하는 컬럼에는 적용하지 말 것 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;삽입, 수정, 삭제가 자주 발생하는 컬럼에는 적용하지 말 것&lt;/h3&gt;
&lt;p&gt;INSERT, UPDATE, DELETE 등의 데이터 구성에 변동을 줄 쿼리가 자주 사용되는 컬럼에 대해선 인덱스를 적용하지 않는 것이 좋다. &lt;strong&gt;즉, 조회 연산만 자주 발생하는 컬럼에 대해서 인덱싱하는 것이 좋다.&lt;/strong&gt; 앞서 설명했듯이, B+ Tree 구조는 새로운 인덱스가 생성 및 수정, 삭제될 때 마다 오름차순 정렬 상태를 유지하기위해 트리내의 노드의 재정렬이 필요하다. 재정렬 시간은 꽤 오래걸리며, 인덱스는 조회 성능을 최적화하는데 초점이 맞춰져있음을 기억하자.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=aTOFBD52060&quot;&gt;https://www.youtube.com/watch?v=aTOFBD52060&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=G4QdllKNGzk&quot;&gt;https://www.youtube.com/watch?v=G4QdllKNGzk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/db-index-and-indexing-algorithms/&quot;&gt;https://hudi.blog/db-index-and-indexing-algorithms/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/96&quot;&gt;https://mangkyu.tistory.com/96&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rebro.kr/169&quot;&gt;https://rebro.kr/169&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[자바 불변 클래스]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/java/immutable-class/</link><guid isPermaLink="false">https://haon.site/haon/java/immutable-class/</guid><pubDate>Tue, 17 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;불변-클래스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%88%EB%B3%80-%ED%81%B4%EB%9E%98%EC%8A%A4&quot; aria-label=&quot;불변 클래스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;불변 클래스&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;불변 클래스&lt;/code&gt;란 해당 인스턴스의 내부 값을 수정할 수 없는 클래스입니다. 불변 인스턴스에 간직된 정보는 고정되어 객체가 파괴되는 순간까지 절대 달라지지 않습니다. 자바 라이브러리에는 &lt;code class=&quot;language-text&quot;&gt;String&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;기본 타입의 박싱된 클래스들&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;BigInteger&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;BigDecimal&lt;/code&gt; 등이 이에 해당합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;불변-클래스로-만들기-위한-규칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%88%EB%B3%80-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%9C%84%ED%95%9C-%EA%B7%9C%EC%B9%99&quot; aria-label=&quot;불변 클래스로 만들기 위한 규칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;불변 클래스로 만들기 위한 규칙&lt;/h3&gt;
&lt;p&gt;클래스를 불변으로 만들려면 아래 5가지 규칙을 따르면됩니다.&lt;/p&gt;
&lt;h4&gt;1. 객체의 상태를 변경하는 메소드(변경자)를 제공하지 않는다.&lt;/h4&gt;
&lt;h4&gt;2. 클래스를 확장할 수 없도록 한다.&lt;/h4&gt;
&lt;p&gt;하위 클래스에서 부주의하게 객체의 상태를 변하게 만드는 사태를 막아줍니다. 상속을 막는 대표적인 방법은 클래스를 final 로 선언하는 것이 있습니다.&lt;/p&gt;
&lt;h4&gt;3. 모든 필드를 final 로 선언한다.&lt;/h4&gt;
&lt;p&gt;개발자가 설계의도를 가장 명확하게 표현할 수 있는 방법입니다.&lt;/p&gt;
&lt;h4&gt;4. 모든 필드를 private 으로 선언한다.&lt;/h4&gt;
&lt;p&gt;필드가 참조하는 가변 객체를 클라이언트에서 직접 접근해 수정하는 일을 막아줍니다. 기술적으로는 기본 타입 필드나 불변 객체를 참조하는 필드를 &lt;code class=&quot;language-text&quot;&gt;public final&lt;/code&gt; 로만 선언해도 불변 객체가 되지만, 이러면 다음 릴리스에서 내부 표현을 바꾸지 않으므로 권장되는 방법은 아닙니다.&lt;/p&gt;
&lt;h4&gt;5. 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.&lt;/h4&gt;
&lt;p&gt;클래스에 가변 객체를 참조하는 필드가 하나라도 있다면 클라이언트에서 그 객체의 참조를 얻을 수 없도록 해야합니다.&lt;/p&gt;
&lt;h3 id=&quot;불변-클래스-예시&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%88%EB%B3%80-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%98%88%EC%8B%9C&quot; aria-label=&quot;불변 클래스 예시 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;불변 클래스 예시&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; re&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; im&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; re&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; im&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;re &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; re&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;im &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; im&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;realPart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; re&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;imaginaryPart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; im&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;plus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;re &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;re&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; im &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;im&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;minus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;re &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;re&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; im &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;im&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;times&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;re &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;re &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; im &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;im&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; re &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;im &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; im &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;re&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;divideBy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; tmp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;re &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;re &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;im &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;im&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;re &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;re &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; im &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;im&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; tmp&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;re &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;im &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; im &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;re&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; tmp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; o&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; o&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;o &lt;span class=&quot;token keyword&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt; complex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Complex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; o&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;compare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;complex&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;re&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; re&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;compare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;complex&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;im&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; im&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hashCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;31&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hashCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;re&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hashCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;im&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;(&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; re &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; + &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; im &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;i)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 클래스는 실수와 허수를 가지는 복소수 불변클래스입니다. 실수와 허수 필드에 대한 getter 메소드는 있지만, setter 메소드는 존재하지 않습니다. 또 클래스를 final 로 정의하여 상속등의 방법으로 확장할 수 없도록 했습니다. 또 클래스의 모든 필드를 final 로 선언하여 초기화 이후 그 값이 바뀔 수 없도록 했습니다. 또한 모든 필드가 private 으로 선언되어 있어서, 클래스 외부에서는 직접 필드를 볼 수 없도록 했습니다. 마지막으로 클래스에 가변 컴포넌트가 존재하지 않습니다. 따라서 이 클래스는 불변 클래스의 5가지 규칙들을 모두 준수하고 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;불변-객체의-특징&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%88%EB%B3%80-%EA%B0%9D%EC%B2%B4%EC%9D%98-%ED%8A%B9%EC%A7%95&quot; aria-label=&quot;불변 객체의 특징 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;불변 객체의 특징&lt;/h2&gt;
&lt;h3 id=&quot;불변-객체는-근본적으로-thread-safe-하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%88%EB%B3%80-%EA%B0%9D%EC%B2%B4%EB%8A%94-%EA%B7%BC%EB%B3%B8%EC%A0%81%EC%9C%BC%EB%A1%9C-thread-safe-%ED%95%98%EB%8B%A4&quot; aria-label=&quot;불변 객체는 근본적으로 thread safe 하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;불변 객체는 근본적으로 thread-safe 하다.&lt;/h3&gt;
&lt;p&gt;불변 객체는 쓰레드 안전하기 때문에, 별도의 동기화 처리가 필요없습니다. 여러 쓰레드가 동시에 사용해도 절대 훼손되지 않으며, 그 어떤 쓰레드도 다른 쓰레드에게 영향을 줄 수 없으므로 불변 객체는 안심하고 공유할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;불변-객체끼리-내부-데이터를-공유할-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%88%EB%B3%80-%EA%B0%9D%EC%B2%B4%EB%81%BC%EB%A6%AC-%EB%82%B4%EB%B6%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EA%B3%B5%EC%9C%A0%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;불변 객체끼리 내부 데이터를 공유할 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;불변 객체끼리 내부 데이터를 공유할 수 있다.&lt;/h3&gt;
&lt;p&gt;불변 객체는 자유롭게 공유할 수 있음은 몰론, 불변 객체끼리는 내부 데이터를 공유할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;객체를-만들때-다른-불변-객체들을-구성요소로-사용하면-이점이-많다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%9D%EC%B2%B4%EB%A5%BC-%EB%A7%8C%EB%93%A4%EB%95%8C-%EB%8B%A4%EB%A5%B8-%EB%B6%88%EB%B3%80-%EA%B0%9D%EC%B2%B4%EB%93%A4%EC%9D%84-%EA%B5%AC%EC%84%B1%EC%9A%94%EC%86%8C%EB%A1%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%A9%B4-%EC%9D%B4%EC%A0%90%EC%9D%B4-%EB%A7%8E%EB%8B%A4&quot; aria-label=&quot;객체를 만들때 다른 불변 객체들을 구성요소로 사용하면 이점이 많다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;객체를 만들때 다른 불변 객체들을 구성요소로 사용하면 이점이 많다.&lt;/h3&gt;
&lt;p&gt;값이 바뀌지 않는 구성요소들로 이루어진 객체라면, 그 구조가 복잡해도 불변식을 유지하기 훨씬 수월합니다. 예를들어, 불변객체는 Map 의 키와 집합(set) 의 원소르 쓰기에 안성맞춤입니다. Map 이나 Set 은 안에 담긴값이 바뀌면 불변식이 허물어지는데, 불변 객체를 사용하면 그런 걱정은 하지 않아도됩니다.&lt;/p&gt;
&lt;h3 id=&quot;단점--값이-다르면-반드시-독립된-객체로-만들어야한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90--%EA%B0%92%EC%9D%B4-%EB%8B%A4%EB%A5%B4%EB%A9%B4-%EB%B0%98%EB%93%9C%EC%8B%9C-%EB%8F%85%EB%A6%BD%EB%90%9C-%EA%B0%9D%EC%B2%B4%EB%A1%9C-%EB%A7%8C%EB%93%A4%EC%96%B4%EC%95%BC%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;단점  값이 다르면 반드시 독립된 객체로 만들어야한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점 : 값이 다르면 반드시 독립된 객체로 만들어야한다.&lt;/h3&gt;
&lt;p&gt;불변 클래스에도 단점은 있습니다. 값이 다르면 반드시 다른 독립된 객체로 만들어야 한다는 것입니다. 값에 아주 조금이라도 변경이 발생한다면, 기존 인스턴스 내부에서 변동이 발생하는 것이 아니라, 새로운 인스턴스를 생성해야 하는 것입니다. 대표적으로 &lt;code class=&quot;language-text&quot;&gt;String&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;StringBuilder&lt;/code&gt; 의 차이를 예로 들 수 있는데, String 과 달리 StringBuffer 는 &quot;가변&quot; 의 특징을 지니고 있죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;활용방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%99%9C%EC%9A%A9%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;활용방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;활용방식&lt;/h2&gt;
&lt;h3 id=&quot;1-클래스는-꼭-필요한-경우가-아니라면-불변이여야-한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%8A%94-%EA%BC%AD-%ED%95%84%EC%9A%94%ED%95%9C-%EA%B2%BD%EC%9A%B0%EA%B0%80-%EC%95%84%EB%8B%88%EB%9D%BC%EB%A9%B4-%EB%B6%88%EB%B3%80%EC%9D%B4%EC%97%AC%EC%95%BC-%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;1 클래스는 꼭 필요한 경우가 아니라면 불변이여야 한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 클래스는 꼭 필요한 경우가 아니라면 불변이여야 한다.&lt;/h3&gt;
&lt;p&gt;getter 가 있다고해서 무조건 setter 를 만들지는 맙시다. 또 불변 클래스는 장점이 꽤 많으며, 단점이라곤 특정 상황에서의 성능 저하 뿐입니다. (ex. String 과 StringBuilder 처럼)&lt;/p&gt;
&lt;h3 id=&quot;2-불변으로-만들-수-없다면-변경-가능한-부분을-최소화하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EB%B6%88%EB%B3%80%EC%9C%BC%EB%A1%9C-%EB%A7%8C%EB%93%A4-%EC%88%98-%EC%97%86%EB%8B%A4%EB%A9%B4-%EB%B3%80%EA%B2%BD-%EA%B0%80%EB%8A%A5%ED%95%9C-%EB%B6%80%EB%B6%84%EC%9D%84-%EC%B5%9C%EC%86%8C%ED%99%94%ED%95%98%EC%9E%90&quot; aria-label=&quot;2 불변으로 만들 수 없다면 변경 가능한 부분을 최소화하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 불변으로 만들 수 없다면, 변경 가능한 부분을 최소화하자.&lt;/h3&gt;
&lt;p&gt;그렇다고 모든 클래스를 불변으로 만들수는 없습니다. 불변으로 만들 수 없는 클래스이더라도, 변경할 수 있는 부분을 최소한으로 줄입시다. 떄문에 꼭 변경해야할 필드를 제외한 나머지 모두를 final 으로 선언합시다. &lt;strong&gt;합당한 이유가 없다면, 모든 필드를 private final 이여야 합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Effective Java (Joshua Bloch 지음)&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Redis 를 활용하여 Refresh 토큰 접근속도 개선하기]]></title><description><![CDATA[시작에 앞서 지난 [Spring Security] Refresh Token, Access Token 을 활용한 로그인 관련 코드구현 에서도 설명드렸듯이, JWT 기반 인증/인가를 구현할때 DB 에 RefreshToken…]]></description><link>https://haon.site/haon/redis/refresh-token/</link><guid isPermaLink="false">https://haon.site/haon/redis/refresh-token/</guid><pubDate>Sat, 14 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;시작에-앞서&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%9E%91%EC%97%90-%EC%95%9E%EC%84%9C&quot; aria-label=&quot;시작에 앞서 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시작에 앞서&lt;/h2&gt;
&lt;p&gt;지난 &lt;a href=&quot;https://velog.io/@msung99/Spring-Security-Refresh-Token-Access-Token-%EC%9D%84-%EC%BD%94%EB%93%9C%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%B4%EB%B3%B4%EC%9E%90&quot;&gt;[Spring Security] Refresh Token, Access Token 을 활용한 로그인 관련 코드구현&lt;/a&gt; 에서도 설명드렸듯이, JWT 기반 인증/인가를 구현할때 DB 에 RefreshToken 을 저장할 수 있습니다.&lt;/p&gt;
&lt;p&gt;이에 대한 실제 코드구현으로 &lt;a href=&quot;https://velog.io/@msung99/Spring-Security-Refresh-token-Access-token-%EB%9E%80-%EA%B5%AC%ED%98%84%EB%B0%A9%EB%B2%95%EC%9D%80&quot;&gt;[Spring Security] Refresh token, Access token란? 기존 JWT 보다 탈취 위험성을 낮춰보자!&lt;/a&gt; 이라는 포스팅을 따로 다루었죠. 그런데 아쉬운 문제점이 존재했습니다. &lt;strong&gt;RefreshToken 을 발급한 이후, 별도의 로그아웃 API 호출이 없는경우 깔끔하게 토큰이 DB에서 삭제되지 못한다는 점입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;redis-의-특징&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-%EC%9D%98-%ED%8A%B9%EC%A7%95&quot; aria-label=&quot;redis 의 특징 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis 의 특징&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Redis-CS-%EA%B4%80%EB%A0%A8-%EC%A7%80%EC%8B%9D%EB%B6%80%ED%84%B0-%EB%9C%AF%EC%96%B4%EB%B3%B4%EB%A9%B0-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-Redis-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0%EC%99%80-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98%EC%9D%84-%EC%82%B4%ED%8E%B4%EB%B3%B4%EC%9E%90&quot;&gt;[Redis] CS 와 함께 뜯어보며 이해하는 Redis : 내부 구조와 동작원리에 대해&lt;/a&gt; 에서도 설명했듯이, Redis 는 인메모리 데이터베이스로써 접근속도가 MySQL 과 같은 일반 데이터베이스에 비해 굉장히 빠르다는 장점이 있습니다.&lt;/p&gt;
&lt;p&gt;다시 지난 내용을 정리해봅시다. Redis 는 key-value 데이터 쌍으로 관리할 수 있는 인메모리 데이터 스토리지입니다. In-memory 라는 특성때문에 Redis 는 저장된 데이터가 영속적이지 않고, 휘발성이라는 특징을 지닙니다.&lt;/p&gt;
&lt;p&gt;보통 데이터베이스에 저장되는 데이터들은 HDD, SDD 같은 디스크에 저장되지만, Redis 는 RAM 에 저장하므로 데이터를 영구적으로 저장이 불가능한것이죠. 대신 빠른 접근속도를 보장받을 수 있는 것입니다. &lt;strong&gt;빠른 접근속도, 휘발성이라는 특징으로 보통 캐시(Cache)의 용도로 Redis를 사용합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;왜-redis인가--redis-vs-rdb&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-redis%EC%9D%B8%EA%B0%80--redis-vs-rdb&quot; aria-label=&quot;왜 redis인가  redis vs rdb permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 Redis인가? : Redis vs RDB&lt;/h2&gt;
&lt;p&gt;Redis 는 리스트, 배열 형식의 데이터 처리에 특화되어있죠. 리스트 형 데이터의 입력과 삭제가 MySQL보다 10배 정도 빠릅니다.&lt;/p&gt;
&lt;p&gt;이런 Redis 를 RefreshToken 의 저장소로 선택하면 정말 좋을겁니다. 빠른 접근 속도로 사용자가 로그인시(리프레시 토큰 발급시) 병목이 되지 않기 때문이죠.&lt;/p&gt;
&lt;p&gt;또 Refresh Token 은 발급된 이후 일정시간 이후 만료가 되어야합니다.(보통 2주의 간격이죠?) 리프레시 토큰을 RDB 등에 저장하면, 스케줄러등을 사용해서 만료된 토큰을 주기적으로 DB 에서 제거해줘야합니다.&lt;/p&gt;
&lt;p&gt;그러나 &lt;strong&gt;Redis 는 기본적으로 데이터의 유효기간(time to live) 을 지정할 수 있습니다.&lt;/strong&gt; 이런 특징들이 바로 Refresh Token 을 저장하기에 적합한 특징들이죠.&lt;/p&gt;
&lt;p&gt;또한 Redis 는 휘발성이라는 특징으로 인해 데이터가 손실될수도 있는 위험이 있으나, Redis 에 저장될 리프레시 토큰은 손실되더라도 그리 손해가 큰 편은 아닙니다. 기껏해봤자 RefreshToken 이 없어져서 다시 로그인을 시도해야 하는 정도이겠죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;gradle&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#gradle&quot; aria-label=&quot;gradle permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;gradle&lt;/h2&gt;
&lt;p&gt;build.gradle 파일을 통한 의존성 주입은 아래와 같이 진행해줬습니다. 유심히 살펴볼 부분은 redis, 토큰을 사용하기 위한 부분입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;dependencies &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	implementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;data&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;jpa&apos;
	implementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;data&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;redis&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.3&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RELEASE&lt;/span&gt;&apos;

	implementation group&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jsonwebtoken&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;jjwt&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;api&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; version&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;0.11.2&apos;&lt;/span&gt;
	implementation group&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jsonwebtoken&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;jjwt&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;impl&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; version&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;0.11.2&apos;&lt;/span&gt;
	implementation group&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jsonwebtoken&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &apos;jjwt&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;jackson&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; version&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;0.11.2&apos;&lt;/span&gt;

	implementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;data&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;jdbc&apos;
	implementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;data&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;jpa&apos;
	implementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;data&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;redis&apos;
	implementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;jdbc&apos;
	implementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;web&apos;
	compileOnly &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;projectlombok&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;lombok&apos;
	runtimeOnly &apos;com&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mysql&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;mysql&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;connector&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;j&apos;
	annotationProcessor &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;projectlombok&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;lombok&apos;
	testImplementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;test&apos;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;redis-환경-구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;redis 환경 구성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis 환경 구성&lt;/h2&gt;
&lt;h3 id=&quot;redis-설치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-%EC%84%A4%EC%B9%98&quot; aria-label=&quot;redis 설치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis 설치&lt;/h3&gt;
&lt;p&gt;Redis 를 스프링부트 애플리케이션에서 활용하려면, 우선 Redis 가 설치되어 있어야합니다. 저는 Docker 로 Redis 이미지를 내려받고, 컨테이너로 Redis 를 실행해주었습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ docker pull redis  &lt;span class=&quot;token comment&quot;&gt;// 이미지 다운 (docker images 로 확인가능)&lt;/span&gt;
$ docker run &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;name my&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;redis &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token number&quot;&gt;6379&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6379&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;d redis  &lt;span class=&quot;token comment&quot;&gt;// 컨테이너로 Redis 실행&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;추가적으로 Redis-cli 에 접속하고 싶다면 아래와 같이 진행해주세요.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;docker exec &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;it my&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;redis redis&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;cli&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;yml-파일-host-port-구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#yml-%ED%8C%8C%EC%9D%BC-host-port-%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;yml 파일 host port 구성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;yml 파일 host, port 구성&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;spring&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  redis&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    host&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; localhost
    port&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6379&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;application.yml 에 host 와 port 를 설정해줍시다. localhost:6379 는 기본값이기 떄문에 만일 Redis 는 localhost:6379 로 띄웠다면 별도로 설정하지 않아도 연결이 되긴합니다.&lt;/p&gt;
&lt;p&gt;하지만 일반적으로 운영 서버에서는 qufehdml host 포트번호를 사용하기 때문에, 위처럼 값을 별도로 셋팅하고 Configuration 에서 Bean 으로 등록해줍시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;유저-서비스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9C%A0%EC%A0%80-%EC%84%9C%EB%B9%84%EC%8A%A4&quot; aria-label=&quot;유저 서비스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;유저 서비스&lt;/h2&gt;
&lt;h3 id=&quot;userentity&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#userentity&quot; aria-label=&quot;userentity permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UserEntity&lt;/h3&gt;
&lt;p&gt;우선 유저에 대한 JPA 엔티티입니다. 간단하게 아이디(identification) 과 비밀번호(password) 로만 구성해줬습니다. 실무에서는 보안상 비밀번호를 base64 해싱을 진행하는등의 복호화 과정이 필요하겠지만, 이번에는 간단히 이런 세부 과정은 생략하겠습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;User&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@AllArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@NoArgsConstructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;access &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AccessLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PROTECTED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Setter&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Builder&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserEntity&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GeneratedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenerationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; userIdx&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;unique &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; identification&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;usercontroller&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#usercontroller&quot; aria-label=&quot;usercontroller permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UserController&lt;/h3&gt;
&lt;p&gt;사용자(유저) 에 대한 컨트롤러입니다. 실습은 위한 필수적인 API 만을 구성하도록, 회원가입과 로그인에 대한 API 만 개발해놓았습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ResponseBody&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@PostMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/signUp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseResponse&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestBody&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignUserReq&lt;/span&gt; signUserReq&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            userService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;signUserReq&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseResponse&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BaseException&lt;/span&gt; baseException&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;baseException&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ResponseBody&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@PostMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/login&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseResponse&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LoginUserRes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestBody&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoginUserReq&lt;/span&gt; loginUserReq&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;LoginUserRes&lt;/span&gt; loginUserRes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;loginUserReq&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;loginUserRes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BaseException&lt;/span&gt; baseException&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;baseException&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;userservice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#userservice&quot; aria-label=&quot;userservice permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UserService&lt;/h3&gt;
&lt;p&gt;다음으로 User 에 대한 서비스 계층 코드입니다. UserRepository, JwtService 서비스 계층에 대한 의존성을 주입받고 앞서 살펴본 컨트롤러에게 기능을 제공해줍니다.&lt;/p&gt;
&lt;p&gt;특히 눈여겨 볼 부분은 login 메소드입니다. 엑세스, 리프레시 토큰을 생성하고 응답값으로 넘겨주는 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRepository&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JwtService&lt;/span&gt; jwtService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserRepository&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JwtService&lt;/span&gt; jwtService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jwtService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; jwtService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SignUserReq&lt;/span&gt; signUserReq&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;UserEntity&lt;/span&gt; userEntity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; signUserReq&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            userRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userEntity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BaseResponseStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SERVER_ERROR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoginUserRes&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LoginUserReq&lt;/span&gt; loginUserReq&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;UserEntity&lt;/span&gt; userEntity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;loginUserReq&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getIdentification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loginUserReq&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; accessToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; jwtService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createAccessToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; refreshToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jwtService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createRefreshToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoginUserRes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;accessToken&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; refreshToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BaseResponseStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SERVER_ERROR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;userrepository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#userrepository&quot; aria-label=&quot;userrepository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UserRepository&lt;/h2&gt;
&lt;p&gt;다음으로 유저 레포지토리입니다. Spring Data JPA 에서 제공해주는 레포지토리 인터페이스를 활용해 기본적인 레포지토티를 구성해습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@EnableJpaRepositories&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JpaRepository&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;select m from UserEntity m where m.identification = :identification and m.password = :password&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;UserEntity&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Param&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;identification&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; identification&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Param&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;password&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;redis-client--lettuce-vs-jedis&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-client--lettuce-vs-jedis&quot; aria-label=&quot;redis client  lettuce vs jedis permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis Client : Lettuce vs Jedis&lt;/h2&gt;
&lt;p&gt;스프링의 Redis Client 를 사용하는 방식에는 크게 Lettuce 와 Jedis 가 있습니다. 저희는 이번에 Lettuce 를 사용해보겠습니다.&lt;/p&gt;
&lt;p&gt;원래는 Jedis 를 많이 사용해왔으나, 여러가지 단점 (멀티쓰레드 불안정, Pool 한계 등) 과 Lettuce 의 장점(Netty 기반이라 비동기 지원가능) 때문에 Lettuce 로 추세가 넘어가고 있습니다.&lt;/p&gt;
&lt;p&gt;결국 Spring Boot 2.0 부터 Jedis 가 기본 클라이언트에서 deprecated 되고 Lettuce 가 탑재되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;spinrg-boot-starter-data-redi&lt;/strong&gt;s 를 사용하면 별도의 의존성주입 없이 Lettuce 를 사용할 수 있습니다(아까 진행한 내용). 반면 Jedis 는 별도의 설정이 필요하죠.
저희는 고민할 필요가 없습니다. Lettuce 를 사용해봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;redis-repository-vs-redis-template&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-repository-vs-redis-template&quot; aria-label=&quot;redis repository vs redis template permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis Repository VS Redis Template&lt;/h2&gt;
&lt;p&gt;스프링에서 Redis 를 사용하는 방법은 2가지가 있습니다. 바로 Repository 인터페이스를 정의하는 방법과, Redis Template 을 사용하는 방법입니다.
저희는 이번에 2가지 방법 모두 사용해볼겁니다.&lt;/p&gt;
&lt;h3 id=&quot;repository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#repository&quot; aria-label=&quot;repository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Repository&lt;/h3&gt;
&lt;p&gt;Repository 인터페이스를 정의하는 방법은 Spring Data JPA 와 비슷합니다. Redis 는 많은 자료구조를 지원하는데, Repository 를 정의하는 이 방법은 Hash 자료구조로 한정하여 사용할 수 있습니다.
Repository 를 사용하면 객체를 Redis 의 Hash 자료구조로 직렬화하여 스토리지에 저장할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;redistemplate&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redistemplate&quot; aria-label=&quot;redistemplate permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedisTemplate&lt;/h3&gt;
&lt;p&gt;RedisTemplate 은 Redis 서버에 명령어를 수행하기 위한 기능을 제공해줍니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;redishash-객체-클래스-정의&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redishash-%EA%B0%9D%EC%B2%B4-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%A0%95%EC%9D%98&quot; aria-label=&quot;redishash 객체 클래스 정의 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedisHash 객체 클래스 정의&lt;/h2&gt;
&lt;p&gt;두 방법(Repository, RedisTemplate) 을 본격적으로 사용하기 전에, Redis 스토리지에 저장될 객체 클래스를 정의해야합니다. 아래와 같이 &lt;strong&gt;@RedisHash 어노테이션&lt;/strong&gt;을 붙인 클래스가 레디스에 저장될 객체의 포맷이됩니다.&lt;/p&gt;
&lt;p&gt;각 옵션에 대한 기능을 먼저 요약해보자면 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;@RedisHash 어노테이션 : Redis 에 저장할 자료구조인 객체를 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;value : Redis 의 keyspace 값으로 사용됩니다.&lt;/li&gt;
&lt;li&gt;timeToLive : 데이터의 생명주기 초 단위로 설정. 디폴트는 만료시간이 없는 -1L 입니다.&lt;/li&gt;
&lt;li&gt;@Id : 이 어노테이션이 붙은 필드가 Redis Key 값이 되며, Null 로 셋팅시 랜덤값이 설정됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;keyspace 와 합쳐져서 Redis 저장되는 최종 key 값은 &lt;strong&gt;keyspace:id&lt;/strong&gt; 형태가 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;timeToLive&lt;/strong&gt; 옵션, 즉 Redis 에 저장된 RefreshToken 데이터가 유지되는 시간(생명주기)은 1시간으로 정의해주었습니다. 실무에서는 1시간이 아닌 2주정도의 긴 시간으로 설정하겠지만, 이번에는 토큰이 스토리지에서 삭제되는 모습을 확인할 수 있도록 아래처럼 정의해 주었습니다.&lt;/p&gt;
&lt;p&gt;또 &lt;strong&gt;value&lt;/strong&gt; 옵션, 즉 Redis 의 keyspace 값은 &quot;refreshToken&quot; 으로 설정해주었습니다. 앞서 말씀드렸듯이, 이 value 에 지정한 값과 @Id 어노테이션을 붙인 refreshToken 필드의 값을 합쳐서 Redis 의 key 값으로 사용합니다.&lt;/p&gt;
&lt;p&gt;예를들어 @Id 어노테이션이 붙은 refreshToken 필드의 값이 hi이면, 해당 데이터에 대한 Redis 의 key 값은 &lt;strong&gt;refreshToken:hi&lt;/strong&gt; 가 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RedisHash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;refreshToken&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; timeToLive &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RefreshToken&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; refreshToken&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; userIdx&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RefreshToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; refreshToken&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; userIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;refreshToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; refreshToken&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userIdx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userIdx&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getRefreshToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; refreshToken&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getUserIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; userIdx&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1-redis-repository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-redis-repository&quot; aria-label=&quot;1 redis repository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Redis Repository&lt;/h2&gt;
&lt;p&gt;이제 본격적으로 Redis 를 활용해봅시다. 우선 2가지 방법중에 Redis Repository 를 사용하는 방법먼저 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;아래와 같이 CrudRepository 를 상속하고, 첫번째 타입에는 데이터를 저장할 객체의 클래스를, 두번째는 객체의 ID 값(@Id 어노테이션이 붙은) 타입 클래스를 넣어주면 됩니다. Spring Data JPA 와 비슷하죠?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RefreshTokenRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CrudRepository&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RefreshToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;CrudRepository 가 제공하는 메소드만을 사용해도 충분하므로, 별도의 메소드를 추가로 정의하지 않겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;redis-repository-의-특징&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-repository-%EC%9D%98-%ED%8A%B9%EC%A7%95&quot; aria-label=&quot;redis repository 의 특징 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis Repository 의 특징&lt;/h3&gt;
&lt;p&gt;이렇게 Spring Data Redis 의 Redis Repository 를 활용하면 간단하게 &lt;strong&gt;Domain Entity 를 Redis Hash 로 만들 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;다만 트랜잭션을 이용하지 않기 때문에, &lt;strong&gt;만일 트랜잭션을 적용하고 싶다면 RedisTemplate 을 사용해야합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;사용방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;사용방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;사용방법&lt;/h3&gt;
&lt;p&gt;사용방법은 Spring Data JPA 를 사용할때와 동일합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@SpringBootTest&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisRepositoryTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RefreshTokenRepository&lt;/span&gt; refreshTokenRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;UserEntity&lt;/span&gt; user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;msung99&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1234&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    refreshTokenRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 저장&lt;/span&gt;

    refreshTokenRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Redis 의 key 값을 기준으로 데이터 탐색&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// =&gt; 즉, &quot;keyspace:Id&quot; 값에 해당하는 데이터를 가져옴&lt;/span&gt;

    refreshTokenRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// UserEntity 의 @RedisHash 에&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 정의되어있는 keysapce (refreshToken) 에 속한 key 의 개수를 구함&lt;/span&gt;

    refreshTokenRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 삭제&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2-redistemplate&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-redistemplate&quot; aria-label=&quot;2 redistemplate permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. RedisTemplate&lt;/h2&gt;
&lt;p&gt;다음으로 RedisTemplate 으로 Redis 에 대한 Repository 를 정의하는 방법에 대해 알아보겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;redisconfig&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redisconfig&quot; aria-label=&quot;redisconfig permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedisConfig&lt;/h3&gt;
&lt;p&gt;RedisTemplate을 사용하기 위해서는 아래와 같이 &lt;strong&gt;@Configuration&lt;/strong&gt;을 통해서 redisTemplate을 &lt;strong&gt;스프링 Bean 으로 등록&lt;/strong&gt; 해야 사용가능합니다.&lt;/p&gt;
&lt;p&gt;Redis 사용하기 위한 기본 Configuration 으로, application.yml 에 설정한 값을 @Value 어노테이션으로 주입해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; redisHost&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; redisPort&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;${spring.redis.host}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; redisHost&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                       &lt;span class=&quot;token annotation punctuation&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;${spring.redis.port}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; redisPort&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisHost &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisHost&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisPort &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisPort&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisConnectionFactory&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;redisConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LettuceConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;redisHost&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; redisPort&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;redisTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; redisTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;redisConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;refreshtokenrepository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#refreshtokenrepository&quot; aria-label=&quot;refreshtokenrepository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RefreshTokenRepository&lt;/h3&gt;
&lt;p&gt;RedisTemplate 에서는 앞서 살펴본 Redis Repository 방법처럼 인터페이스를 정의하지 않고, 직접 아래처럼 구현해야합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ValueOperations&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stereotype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Repository&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;redis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;model&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RefreshToken&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Objects&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Optional&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;concurrent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TimeUnit&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;


&lt;span class=&quot;token annotation punctuation&quot;&gt;@Repository&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RefreshTokenRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RefreshTokenRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RefreshToken&lt;/span&gt; refreshToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ValueOperations&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; valueOperations &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opsForValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        valueOperations&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;refreshToken&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRefreshToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; refreshToken&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMemberId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expire&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;refreshToken&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRefreshToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TimeUnit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SECONDS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RefreshToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; refreshToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ValueOperations&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; valueOperations &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opsForValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; userIdx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; valueOperations&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;refreshToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RefreshToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;refreshToken&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; userIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;jwtservice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jwtservice&quot; aria-label=&quot;jwtservice permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JwtService&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;createAccessToken(), createRefreshToken()&lt;/strong&gt; 을 통해 토큰을 생성하도록 했습니다. 각 토큰의 만룍기간은 30분, 60분으로 설정했는데 실무였다면 refreshToken 의 경우 약 2주가량의 긴 시간이으로 설정했겠죠?&lt;/p&gt;
&lt;p&gt;이 부분은 초반부에 언급한 UserService 서비스 부분에서 활용되는 부분이므로, 다시 되돌아가서 현재 코드와 함께 학습하시면 도움이 되실겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JwtService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RefreshTokenRepository&lt;/span&gt; refreshTokenRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JwtService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RefreshTokenRepository&lt;/span&gt; refreshTokenRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;refreshTokenRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; refreshTokenRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createTokenWhenLogin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; userIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; refreshToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createRefreshToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; accessToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createAccessToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; tokenList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;refreshToken&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; accessToken&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; tokenList&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createAccessToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; userIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; keyBytes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Decoders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BASE64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Secret&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ACCESS_TOKEN_SECRET_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Key&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hmacShaKeyFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keyBytes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt; now &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Jwts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHeaderParam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;jwt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;claim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;userIdx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;userIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setIssuedAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;now&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setExpiration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 만료기간은 30분으로 설정&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;signWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignatureAlgorithm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HS256&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;compact&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createRefreshToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; userIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; keyBytes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Decoders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BASE64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Secret&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ACCESS_TOKEN_SECRET_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Key&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hmacShaKeyFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keyBytes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt; now &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; jwtToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;token class-name&quot;&gt;Jwts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHeaderParam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;jwt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;claim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;userIdx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;userIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setIssuedAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;now&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setExpiration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 만료기간은 1시간으로 설정&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;signWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignatureAlgorithm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HS256&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;compact&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RefreshToken&lt;/span&gt; refreshToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RefreshToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jwtToken&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; userIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// RefreshToken refreshToken = new RefreshToken(UUID.randomUUID().toString(), userIdx);&lt;/span&gt;
        refreshTokenRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;refreshToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; refreshToken&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRefreshToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getAccessToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ServletRequestAttributes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestContextHolder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentRequestAttributes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Authorization&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getRefreshToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ServletRequestAttributes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestContextHolder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentRequestAttributes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;RefreshToken&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금까지 인증/인가를 위한 JWT 토큰를 어떻게 더 성능을 개선할지에 대해 자세히 다루어봤습니다. 몰론 Redis 를 사용하지 않고도 RDB 만으로 충분히 인증 및 인가 시스템을 충분히 구현은 가능하지만, 저희의 목적은 말씀 드렸듯이 성능 개선입니다.&lt;/p&gt;
&lt;p&gt;또 Redis 를 학습하시는 모든 분들에게 이 포스팅 내용이 큰 도움이 되셨으리라고 생각합니다. 최대한 열심히 적어본 포스팅이였네요!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://redis.io/&quot;&gt;Redis Document&lt;/a&gt;
&lt;a href=&quot;https://aws.amazon.com/ko/elasticache/what-is-redis/&quot;&gt;[AWS]Redis란 무엇입니까?&lt;/a&gt;
&lt;a href=&quot;https://yjam.tistory.com/50&quot;&gt;Redis는 왜 사용하는가?&lt;/a&gt;
&lt;a href=&quot;https://bcp0109.tistory.com/328&quot;&gt;Spring Boot 에서 Redis 사용하기&lt;/a&gt;
&lt;a href=&quot;https://wildeveloperetrain.tistory.com/32&quot;&gt;Spring Boot Redis 두 가지 사용 방법&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[자바 객체의 동일성(identity)와 동등성(equality)]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/java/equality-identity/</link><guid isPermaLink="false">https://haon.site/haon/java/equality-identity/</guid><pubDate>Fri, 13 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;동일성(identity)&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;동등성(equality)&lt;/code&gt; 라는 키워드에 대해 처음 접하게 되었습니다. 이들에 대한 이해도가 부족하다고 생각이 들었기때문에, 이번 기회에 학습해보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;원활한 설명을 위해, 우선 아래와 같은 사용자 정의 클래스를 정의했다고 가정하겠습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; currentPosition&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token class-name&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; currentPosition&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentPosition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; currentPosition&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;동일성-identity&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%9D%BC%EC%84%B1-identity&quot; aria-label=&quot;동일성 identity permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동일성 (Identity)&lt;/h2&gt;
&lt;p&gt;동일성은 &lt;strong&gt;두 비교대상이 바라보고 있는 실제 인스턴스 객체의&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;메모리 주소&lt;/code&gt; &lt;strong&gt;가 같음을 의미하는 것 입니다.&lt;/strong&gt; 동일성에 대한 비교는 &lt;code class=&quot;language-text&quot;&gt;==&lt;/code&gt; 비교연산자로 확인할 수 있게됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Position&lt;/span&gt; pos1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Position&lt;/span&gt; pos2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pos1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pos1 &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; pos2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;예를들어 위 코드에서 pos2 를 생성시에 새로운 인스턴스를 생성하는 것이 아니라, 기존 객체인 pos1 를 대입받았습니다. 이 결과로 pos1 과 pos2 이 같은 &lt;strong&gt;메모리 주소에 위치한 같은 인스턴스를 바라보게 됩니다.&lt;/strong&gt; 객체는 각자의 고유한 식별자를 가지고 있는데, 이 식별자가 같으면 동일하다고 판단하게 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;동등성equality&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EB%93%B1%EC%84%B1equality&quot; aria-label=&quot;동등성equality permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동등성(equality)&lt;/h2&gt;
&lt;p&gt;반면 동등성은 비교 대상의 두 객체가 &lt;strong&gt;논리적으로 동일한 값을 저장 및 표현하고 있는지를 의미하는 것&lt;/strong&gt;입니다. 쉽게말해, 메모리 주소값을 비교하는 것이 아니라 &lt;strong&gt;&quot;논리적으로 동일한지&quot;&lt;/strong&gt; 를 비교하는 것입니다. 동등성 비교를위해, 자바에선 &lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;hashCode()&lt;/code&gt; 를 재정의해야합니다.&lt;/p&gt;
&lt;p&gt;예를들어 지갑에 100원이 있고 집에 100원이 있다고 해봅시다. 분명히 둘은 엄연히 다른 객체(물체) 이기 떄문에 &lt;code class=&quot;language-text&quot;&gt;동일성&lt;/code&gt; 에 있어선 서로 다른 객체라는 결과를 얻게됩니다. 반명 둘은 논리적으론 100원이라는 면에선 같은 동전이므로, &lt;code class=&quot;language-text&quot;&gt;동등성&lt;/code&gt;의 결과로는 같은 객체라는 결과를 얻게됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bridge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... (생략)&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Bridge&lt;/span&gt; otherPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bridge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;otherPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hashCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 2개의 Bridge 라는 VO 가 있을때, 이 둘을 &lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;hashCode()&lt;/code&gt; 를 재정의후 둘의 path 필드가 동일하다면 동일하다는 결과를 도출해내는 것은 &lt;code class=&quot;language-text&quot;&gt;동등성(equality)&lt;/code&gt; 이 보장된 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;hashcode-는-왜-재정의-해야하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hashcode-%EB%8A%94-%EC%99%9C-%EC%9E%AC%EC%A0%95%EC%9D%98-%ED%95%B4%EC%95%BC%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;hashcode 는 왜 재정의 해야하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;hashCode 는 왜 재정의 해야하는가?&lt;/h2&gt;
&lt;h3 id=&quot;hashcode&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hashcode&quot; aria-label=&quot;hashcode permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;hashCode&lt;/h3&gt;
&lt;p&gt;우선 hashCode 에 대해 이해할 필요가 있습니다. 객체의 &lt;code class=&quot;language-text&quot;&gt;hashCode&lt;/code&gt; 란 &lt;strong&gt;객체를 식별하는 하나의 고유 정수값&lt;/strong&gt;을 말합니다. &lt;code class=&quot;language-text&quot;&gt;Object.hashCode()&lt;/code&gt; 메소드가 객체의 hashCode 를 반환하는 것이며, 이 메소드는 객체의 메모리 버전을 이용해서 hashCode 를 만들어 반환하기 때문에, &lt;strong&gt;각 객체마다 고유한 다른 해시코드 값을 가집니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;따라서 객체의 &lt;code class=&quot;language-text&quot;&gt;동일성&lt;/code&gt; 이 아니라 논리적인 &lt;code class=&quot;language-text&quot;&gt;동등성&lt;/code&gt; 을 비교할시엔 &lt;code class=&quot;language-text&quot;&gt;hashCode()&lt;/code&gt; 를 오버라이딩할 필요가 있습니다. 자바의 컬렉션에서 &lt;strong&gt;HashSet, HashMap, HashTable 같은 경우도 두 객체가 동등한지 논리적 동등성을 비교하기 떄문입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;equals-와-hashcode-를-함께-재정의해야-하는-이유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#equals-%EC%99%80-hashcode-%EB%A5%BC-%ED%95%A8%EA%BB%98-%EC%9E%AC%EC%A0%95%EC%9D%98%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0&quot; aria-label=&quot;equals 와 hashcode 를 함께 재정의해야 하는 이유 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;equals() 와 hashCode() 를 함께 재정의해야 하는 이유&lt;/h3&gt;
&lt;p&gt;본론으로 돌아가서, 왜 &lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;hashCode()&lt;/code&gt; 중에 하나만 재정의하면 어떻게될까요?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/69219e7d-a257-423f-b9a7-b1c33c22e86e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;우선 논리적 동등성을 비교시, hash 값을 사용하는 컬렉션(HashSet, HashMap, HashTable) 은 객체가 논리적으로 동일한지 비교시 &lt;code class=&quot;language-text&quot;&gt;hashCode()&lt;/code&gt; 값을 먼저 비교하고, 이후에 &lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 를 비교하게 됩니다. 이 둘의 결과가 모두 true 라면 논리적으로 동일하다고 판별하게 됩니다.&lt;/p&gt;
&lt;p&gt;또 &lt;code class=&quot;language-text&quot;&gt;Object.hashCode()&lt;/code&gt;는 객체의 고유한 주소값을 int 값으로 해싱하여 변환하므로, 객체마다 다른 값을 리턴합니다. 따라서 해싱 기반 컬렉션에 저장된 서로 다른 두 객체를 비교시, 해싱된 값이 다르므로 논리적으로 다르다고 판단되는 방식입니다.&lt;/p&gt;
&lt;h4&gt;hashCode() 만 재정의하고, equals() 는 재정의 안한 경우&lt;/h4&gt;
&lt;p&gt;만약 &lt;code class=&quot;language-text&quot;&gt;hashCode()&lt;/code&gt; 만 재정의한다면 &lt;code class=&quot;language-text&quot;&gt;hashCode()&lt;/code&gt; 가 만든 해시값을 이용해 컬렉션에 객체가 저장된 버킷을 찾을순 있습니다. 그러나 해싱된 값이 동일해도 &lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 로 비교시에 논리적으로 서로 같은 객체인지 판별할 수 없기 때문에 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; 이 반환됩니다. 즉, &lt;strong&gt;논리적 동일성을 비교할 수 없으므로&lt;/strong&gt; 원하는 객체를 찾을 수 없게됩니다.&lt;/p&gt;
&lt;h4&gt;equals() 만 재정의하고, hashCode() 는 재정의 안한 경우&lt;/h4&gt;
&lt;p&gt;반대로 &lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 만 재정의한다면 같은 VO 라도 컬렉션에 저장된 각 객체마다 해시코드 값이 달라지므로, &lt;code class=&quot;language-text&quot;&gt;hashCode()&lt;/code&gt; 반환값이 다르기때문에 동일한 VO 의 동일성이 false 가 나오게됩니다.&lt;/p&gt;
&lt;h3 id=&quot;hashcode-재정의-방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hashcode-%EC%9E%AC%EC%A0%95%EC%9D%98-%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;hashcode 재정의 방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;hashCode 재정의 방법&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hashCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;인텔리제이와 같은 IDEA 에서 제공하는 기능을 사용하면, &lt;code class=&quot;language-text&quot;&gt;Objects.hash&lt;/code&gt; 메소드를 호출하는 로직으로 hashCode 메소드를 재정의해줍니다. Objects.hash 메서드는 hashCode 메서드를 재정의하기 위해 간편히 사용할 수 있는 메서드이지만 속도가 느립니다. 인자를 담기 위한 배열이 만들어지고 인자 중 기본 타입이 있다면 박싱과 언박싱도 거쳐야 하기 때문이죠.&lt;/p&gt;
&lt;p&gt;따라서 성능에 민감하지 않은 경우라면 &lt;code class=&quot;language-text&quot;&gt;Object.hash&lt;/code&gt; 로 간편하게 재정의하되, 민감한 경우만 직접 재정의해줍시다. 참고로 대부분의 프로그램은 &lt;code class=&quot;language-text&quot;&gt;Objects.hash&lt;/code&gt; 를 활용하여 재정의해도 문제 없습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;직접 오버라이딩을 하는 경우는, 벨덤의 가이드라인 &lt;a href=&quot;https://www.baeldung.com/java-hashcode&quot;&gt;Guide to hashCode() in Java&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/identity-vs-equality/&quot;&gt;https://hudi.blog/identity-vs-equality/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@mooh2jj/equals%EC%99%80-hashCode%EB%8A%94-%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80&quot;&gt;https://velog.io/@mooh2jj/equals와-hashCode는-언제-사용하는가&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://steady-coding.tistory.com/534&quot;&gt;https://steady-coding.tistory.com/534&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[자바의 EnumMap은 무엇이고, 왜 HashMap 보다 성능이 더 빠른가?]]></title><description><![CDATA[EnumMap  EnumMap 이란 키(key) 를 특정 Enum 타입만을 사용하도록 하는 Map 인터페이스의 구현체입니다. Map 에 대한 구현체이므로, 당연히 Map 에서 제공하는 메소드들을 모두 사용 가능합니다. 그래서 EnumMap…]]></description><link>https://haon.site/haon/java/enum-map/</link><guid isPermaLink="false">https://haon.site/haon/java/enum-map/</guid><pubDate>Wed, 11 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;enummap&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#enummap&quot; aria-label=&quot;enummap permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EnumMap&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/f8e84400-5a01-451d-857d-d2c4586b0645/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;EnumMap 이란 키(key) 를 특정 Enum 타입만을 사용하도록 하는 Map 인터페이스의 구현체입니다. Map 에 대한 구현체이므로, 당연히 Map 에서 제공하는 메소드들을 모두 사용 가능합니다.&lt;/p&gt;
&lt;h3 id=&quot;그래서-enummap-이-왜-빠른건데&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%9E%98%EC%84%9C-enummap-%EC%9D%B4-%EC%99%9C-%EB%B9%A0%EB%A5%B8%EA%B1%B4%EB%8D%B0&quot; aria-label=&quot;그래서 enummap 이 왜 빠른건데 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그래서 EnumMap 이 왜 빠른건데?&lt;/h3&gt;
&lt;p&gt;EnumMap 에 대해서 찾아보길, &lt;code class=&quot;language-text&quot;&gt;HashMap&lt;/code&gt; 과 같은 Map 구현체들에 비해서 빠른 성능을 낼 수 있다고 알려져있습니다. 왜 EnumMap 이 다른 구현체들보다 더 빠른 성능을 낼 수 있을까요?&lt;/p&gt;
&lt;h3 id=&quot;enummap-vs-hashmap&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#enummap-vs-hashmap&quot; aria-label=&quot;enummap vs hashmap permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EnumMap VS HashMap&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/620e5906-779c-449b-9923-611d26e0224c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;우선 &lt;code class=&quot;language-text&quot;&gt;HashMap&lt;/code&gt; 의 동작방식에 대해 이해할 필요가 있습니다. Hash 는 어떤 방식보다도 검색에 빠르지만, 이를 위해서는 &lt;strong&gt;별도의 해싱 작업이 필요합니다.&lt;/strong&gt; 즉, HashMap 은 해시 값을 만들고, &lt;code class=&quot;language-text&quot;&gt;해시 충돌(Hash Collision)&lt;/code&gt; 에 대응하기 위한 작업 처리가 필요합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Enum 에 대한 자세한 설명은 &lt;a href=&quot;https://velog.io/@msung99/JAVA-%EC%97%B4%EA%B1%B0Enum-%ED%83%80%EC%9E%85-%EA%B7%B8%EA%B1%B0-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B1%B4%EB%8D%B0&quot;&gt;[JAVA] 열거(Enum) 타입? 그거 어떻게 사용하는건데? 🤷‍♂️&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/289328a8-0bdd-43b7-b32a-5797dcef94c6/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;반면 &lt;code class=&quot;language-text&quot;&gt;EnumMap&lt;/code&gt; 은 어떨까요? 우선 &lt;code class=&quot;language-text&quot;&gt;Enum&lt;/code&gt; 의 특징을 다시 되짚어보면 상수의 선언 순서대로 일정한 순번을 가집니다. 때문에 Enum 객체의 &lt;code class=&quot;language-text&quot;&gt;ordinal()&lt;/code&gt; 을 활용해서 열거타입 객체의 순번을 가져올 수 있는것이라고 했었습니다.&lt;/p&gt;
&lt;p&gt;이렇게 &quot;순번&quot; 정보를 가지고있다는 특징으로 인해, EnumMap 은 내부적으로 배열에다 값을 저장할 수 있고, 실제로 내부적으로 배열에 열겨헝 데이터들을 저장하는 구조합니다. 결국 배열의 인덱싱 및 연산을 떠올려보면, HashMap 과 달리 해싱 및 해싱 충돌작업에 대한 별도의 작업을 수행하지 않습니다.&lt;/p&gt;
&lt;p&gt;또한 HashMap 의 경우 데이터의 개수가 많아지고 일정 크기에 도달하면 구조 재조정 작업이 필요하지만, EnumMap 의 경우 전달된 Enum 타입의 상수 개수만큼만 저장공간을 확보하면 되므로 이 작업이 필요하지 않습니다.&lt;/p&gt;
&lt;h3 id=&quot;enummap-의-특징&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#enummap-%EC%9D%98-%ED%8A%B9%EC%A7%95&quot; aria-label=&quot;enummap 의 특징 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EnumMap 의 특징&lt;/h3&gt;
&lt;p&gt;EnumMpa 의 특징을 정리해보자면, 다음과 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EnumMap 은 내부저긍로 데이터를 저장할 때 배열을 사용한다.&lt;/li&gt;
&lt;li&gt;기본 연산들이 상수시간 안에 수행된다.&lt;/li&gt;
&lt;li&gt;캐싱을 통한 성능 향상을 위해 key 배열 또한 내부적으로 가지고있다.&lt;/li&gt;
&lt;li&gt;equals 연산을 수행시, 내부의 key 와 value 가 모두 일치하는지 전체 순회를 통해 확인한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;enummap-연산&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#enummap-%EC%97%B0%EC%82%B0&quot; aria-label=&quot;enummap 연산 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EnumMap 연산&lt;/h2&gt;
&lt;h3 id=&quot;enummap-선언방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#enummap-%EC%84%A0%EC%96%B8%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;enummap 선언방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EnumMap 선언방법&lt;/h3&gt;
&lt;p&gt;생성자에 Enum 클래스 타입을 직접 인자로 전달해야 한다는 것을 유의합시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;EnumMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ineger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; myMap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EnumMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;get&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#get&quot; aria-label=&quot;get permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;get&lt;/h3&gt;
&lt;p&gt;이 외에는 기본적으로 Map 의 구현체이므로, Map 에서 제공하는 모든 기능을 사용할 수 있습니다. 예를들어 &lt;code class=&quot;language-text&quot;&gt;get()&lt;/code&gt; 을 활용하여 key 에 대한 value 값을 조회할때는, 열거타입 객체를 인자로 넘겨주면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; directionIntegerMap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EnumMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; eastValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; directionIntegerMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EAST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;remove&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#remove&quot; aria-label=&quot;remove permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;remove&lt;/h3&gt;
&lt;p&gt;특정 날짜의 매핑을 해제하려면 간단히 remove() 를 호출하면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;directionIntegerMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EAST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/enummap-class-java-example/&quot;&gt;https://www.geeksforgeeks.org/enummap-class-java-example/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/java-enum-map/&quot;&gt;https://hudi.blog/java-enum-map/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.manty.co.kr/bbs/detail/develop?id=61&quot;&gt;https://www.manty.co.kr/bbs/detail/develop?id=61&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://siyoon210.tistory.com/142&quot;&gt;https://siyoon210.tistory.com/142&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@kasania/Java-EnumMap&quot;&gt;https://velog.io/@kasania/Java-EnumMap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://recordsoflife.tistory.com/890&quot;&gt;https://recordsoflife.tistory.com/890&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[자바 Enum 타입, 그거 어떻게 사용하는건데 🤷‍♂️]]></title><description><![CDATA[학습배경 열겨형을 활용하여 어떻게 상수관리 및 메소드 처리를 깔끔하게 처리할 수 있는지를 잘 모른다고 느꼈기때문에, 이를위해 이번 포스팅에서 열거 타입에 대해 자세히 다루어보고자 합니다. 열겨 타입 (Enum…]]></description><link>https://haon.site/haon/java/enum/</link><guid isPermaLink="false">https://haon.site/haon/java/enum/</guid><pubDate>Mon, 09 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;열겨형을 활용하여 어떻게 상수관리 및 메소드 처리를 깔끔하게 처리할 수 있는지를 잘 모른다고 느꼈기때문에, 이를위해 이번 포스팅에서 열거 타입에 대해 자세히 다루어보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;열겨-타입-enum&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%B4%EA%B2%A8-%ED%83%80%EC%9E%85-enum&quot; aria-label=&quot;열겨 타입 enum permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;열겨 타입 (Enum)&lt;/h2&gt;
&lt;p&gt;열겨형이란 서로 관련된 상수들을 편리하게 관리하기 위해 선언하는 것으로, 여러 상수를 정의할때 활용하면 유용합니다.&lt;/p&gt;
&lt;h3 id=&quot;타입에-안전한-열겨형-typesafe-enum&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%83%80%EC%9E%85%EC%97%90-%EC%95%88%EC%A0%84%ED%95%9C-%EC%97%B4%EA%B2%A8%ED%98%95-typesafe-enum&quot; aria-label=&quot;타입에 안전한 열겨형 typesafe enum permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;타입에 안전한 열겨형 (typesafe enum)&lt;/h3&gt;
&lt;p&gt;자바의 열겨형은 &quot;타입에 안전한 열겨형&quot; 이라서 &lt;strong&gt;실제 값이 같아도 타입이 다르면 컴파일 에러가 발생합니다.&lt;/strong&gt; 이처럼 값뿐만 아니라 타입까지 체크하기 때문에 타입에 안전하다고 하는 것입니다&lt;/p&gt;
&lt;h3 id=&quot;열거-타입-선언방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%B4%EA%B1%B0-%ED%83%80%EC%9E%85-%EC%84%A0%EC%96%B8%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;열거 타입 선언방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;열거 타입 선언방법&lt;/h3&gt;
&lt;p&gt;정의는 간단합니다. enum 타입의 괄호 { } 안에 상수의 이름을 나열하기만 하면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;EAST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;SOUTH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;WEST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;NORTH&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;열거-타입-사용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%B4%EA%B1%B0-%ED%83%80%EC%9E%85-%EC%82%AC%EC%9A%A9&quot; aria-label=&quot;열거 타입 사용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;열거 타입 사용&lt;/h3&gt;
&lt;p&gt;열거 타입도 데이터 타입의 일종으로, 변수를 선언하고 대입할 수 있습니다. 또 열거타입은 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; 을 지정할 수 있습니다. 열거타입 또한 &quot;참조 타입&quot; 이기 떄문입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt; direction1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EAST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt; direction2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;열거타입-상수간의-비교&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%B4%EA%B1%B0%ED%83%80%EC%9E%85-%EC%83%81%EC%88%98%EA%B0%84%EC%9D%98-%EB%B9%84%EA%B5%90&quot; aria-label=&quot;열거타입 상수간의 비교 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;열거타입 상수간의 비교&lt;/h3&gt;
&lt;p&gt;열겨형 상수간의 비교는 &lt;code class=&quot;language-text&quot;&gt;==&lt;/code&gt; 를 사용할 수 있습니다. equals() 가 아닌 &quot;==&quot; 로 비교 가능하다는 것은 그만큼 빠른 성능을 제공하는 얘기입니다. 그러나 &quot;&amp;#x3C;&quot;, &quot;&gt;&quot; 와 같은 비교연산자는 사용할 수 없고 &lt;code class=&quot;language-text&quot;&gt;compareTo()&lt;/code&gt; 는 사용 가능합니다. compareTo() 는 두 비교대상이 같으면 0, 왼쪽이 크다면 양수, 오른쪽이 크다면 음수를 반환합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt; direction &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EAST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;direction &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EAST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;direction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;compareTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;WEST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;jvm-메모리에-올라간-열거타입&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jvm-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%97%90-%EC%98%AC%EB%9D%BC%EA%B0%84-%EC%97%B4%EA%B1%B0%ED%83%80%EC%9E%85&quot; aria-label=&quot;jvm 메모리에 올라간 열거타입 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JVM 메모리에 올라간 열거타입&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/266d6b69-71e2-4c09-b331-ddfb79063488/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;자바에서의 열거 타입은 일종의 클래스이며, 상수 하나당 인스턴스 객체를 각각 하나씩 만들어서 &lt;code class=&quot;language-text&quot;&gt;public static final&lt;/code&gt; 필드로 공개합니다. 또한 열거 타입의 인스턴스는 런타임에 단 한번만 실행됩니다. 이런 특징으로 &lt;a href=&quot;https://velog.io/@msung99/JAVA-%EC%9E%90%EC%9B%90%EC%9D%84-%EC%A7%81%EC%A0%91-%EB%AA%85%EC%8B%9C%ED%95%98%EC%A7%80-%EB%A7%90%EA%B3%A0-%EC%9D%98%EC%A1%B4-%EA%B0%9D%EC%B2%B4-%EC%A3%BC%EC%9E%85%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC-feat.-%EC%8B%B1%EA%B8%80%ED%86%A4&quot;&gt;싱글톤&lt;/a&gt; 을 보장할 때 사용되기도 합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt; myDir &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EAST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;JVM 의 메모리 영역을 크게 구분하면, &lt;strong&gt;메소드 영역, 힙 영역, 스택 영역&lt;/strong&gt; 3가지로 나뉩니다. 이때, 메소드 영역에는 클래스와 변수 (static variable) 가 저장됩니다. 따라서 열거 타입 클래스가 이 메소드 영역에 올라가게 되고, 힙 영역에는 실제 객체(인스턴스) 가 올라갑니다. 또 스택 영역에는 열거 타입 변수가 생성되죠. 위처럼 열거 타입 변수 myDir 를 생성한다면 해당 변수는 스택 영역에 생성되며, 힙 영역의 EAST 객체를 참조하게 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;enum-메소드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#enum-%EB%A9%94%EC%86%8C%EB%93%9C&quot; aria-label=&quot;enum 메소드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Enum 메소드&lt;/h2&gt;
&lt;p&gt;모든 열겨형 타입은 컴파일 타임에 &lt;code class=&quot;language-text&quot;&gt;Enum&lt;/code&gt; 이라는 최상위 클래스를 상속합니다. 우리가 지금껏 사용할 수 있었던 Enum 타입의 메소드들은 Enum 의 메소드라고 할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;name&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#name&quot; aria-label=&quot;name permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;name&lt;/h3&gt;
&lt;p&gt;열거 객체(열겨형 상수)의 이름을 문자열로 반환합니다. 이때 name() 을 별도로 호출안해도 기본적으로 열거 객체를 출력하면 문자열 이름이 반환된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EAST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;WEST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;SOUTH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;NORTH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt; direction &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EAST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;direction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// EAST&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;direction&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// EAST&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;values&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#values&quot; aria-label=&quot;values permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;values&lt;/h3&gt;
&lt;p&gt;열겨형의 모든 열거 객체(상수)를 순서대로 배열에 담아 반환합니다.열거 타입의 모든 열거 객체를 순회할 떄 유용하게 사용됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; dArr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt; d &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dArr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;d&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;valueof&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#valueof&quot; aria-label=&quot;valueof permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;valueOf&lt;/h3&gt;
&lt;p&gt;지정된 열겨형에서 name 과 일치하는 열거형 상수를 반환합니다. 외부에서 문자열을 입력받아서 열거 객체로 변환할 때 유용하게 사용됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt; direction &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;EAST&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;direction&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// EAST&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EAST&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;EAST&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;ordinal&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ordinal&quot; aria-label=&quot;ordinal permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ordinal&lt;/h3&gt;
&lt;p&gt;열거 객체의 순번을 반환합니다. 이때 순번이란 열거 타입을 정의할때 명시된 순서로써 0부터 시작합니다. ordinal() 이 반환하는 숫자는 열겨형 상수의 값으로 사용하지 않는것디 좋다고합니다. 이 값은 내부적인 용도로만 사용되기 위한 것이기 떄문입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// EAST:0, WEST:1, SOUTH:2, NORTH:3&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt; direction &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;WEST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;direction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ordinal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NORTH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;compareto&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#compareto&quot; aria-label=&quot;compareto permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;compareTo&lt;/h3&gt;
&lt;p&gt;앞서 설명드린 compareTo 메소드로, 두 열거 객체간의 순번을 비교하여 상대적 순번 차이를 반환하는 메소드입니다. 비교대상 열거 객체보다 순번이 빠르다면 음수가, 느리다면 양수가 반환됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NORTH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;compareTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EAST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 양수&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EAST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;compareTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NORTH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 음수&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;열겨형에-멤버-추가하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%B4%EA%B2%A8%ED%98%95%EC%97%90-%EB%A9%A4%EB%B2%84-%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0&quot; aria-label=&quot;열겨형에 멤버 추가하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;열겨형에 멤버 추가하기&lt;/h2&gt;
&lt;p&gt;열거형 상수의 값이 불연속적인 경우에는 아래처럼 열겨형 상수의 이름 옆에 원하는 값을 괄호 &quot;( )&quot; 와 함께 적어주면 됩니다. 또한 &lt;strong&gt;열거형의 생성자는 제어자가 암묵적으로 private 입니다. 때문에 외부에서 열겨형 객체를 생성할 수 없습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;EAST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;E&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;WEST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;W&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;SOUTH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;324&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;S&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NORTH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;N&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; symbol&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// private 이 생략됨&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; symbol&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;자바의 정석 (남궁성 지음)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/java-enum/&quot;&gt;https://hudi.blog/java-enum/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hyunndyblog.tistory.com/23&quot;&gt;https://hyunndyblog.tistory.com/23&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[JDBC 에서 데이터베이스 커넥션 풀 다루기 (feat. JDBC Driver, DataSource, HikariCP)]]></title><description><![CDATA[지난 HikariCP 와 데이터베이스 커넥션 풀(Connection Pool) 에 이어서, 이번 포스트에선 스프링부트에서 제공하는 JDBC 로 DB…]]></description><link>https://haon.site/database/jdbc-connection-pool/</link><guid isPermaLink="false">https://haon.site/database/jdbc-connection-pool/</guid><pubDate>Sat, 07 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;지난 &lt;a href=&quot;https://haon.blog/database/hikaricp-theory-1/&quot;&gt;HikariCP 와 데이터베이스 커넥션 풀(Connection Pool)&lt;/a&gt; 에 이어서, 이번 포스트에선 스프링부트에서 제공하는 JDBC 로 DB 커넥션 풀을 간단한 실습과 함께 직접 다루어보고자 한다.&lt;/p&gt;
&lt;h2 id=&quot;각-데이터베이스-벤더마다-각기-다른-스팩-불일치-문제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%81-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B2%A4%EB%8D%94%EB%A7%88%EB%8B%A4-%EA%B0%81%EA%B8%B0-%EB%8B%A4%EB%A5%B8-%EC%8A%A4%ED%8C%A9-%EB%B6%88%EC%9D%BC%EC%B9%98-%EB%AC%B8%EC%A0%9C&quot; aria-label=&quot;각 데이터베이스 벤더마다 각기 다른 스팩 불일치 문제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;각 데이터베이스 벤더마다 각기 다른 스팩 불일치 문제&lt;/h2&gt;
&lt;p&gt;일반적으로 스프링부트 애플리케이션에서 데이터베이스에 커넥션을 맺고 접근하기 위한 과정은 아래와 같이 수행된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; TCP/IP 소켓을 활용하여 DB 와 커넥션 연결&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; DB 에게 원하는 연산을 위해 SQL 을 전송&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; SQL 수행 결과를 응답받음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;하지만, 문제는 각 데이터베이스 벤더마다 커넥션을 연결하는 방법과, SQL 을 전달하는 방법, 그리고 SQL 수행 결과를 응답받는 방법이 각기 다르다는 점이다. 이로 인해 발생하는 문제점은 아래와 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 데이터베이스를 다른 종류의 데이터베이스로 변경하면 애플리케이션 서버에 개발된 데이터베이스 사용 코드도 함께 변경해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 개발자가 각각의 데이터베이스마다 커넥션 연결하는 방법, SQL 전달 방법, 그리고 그 결과를 응답받는 방법을 일일이 새롭게 학습해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;jdbc-driver-표준-인터페이스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jdbc-driver-%ED%91%9C%EC%A4%80-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4&quot; aria-label=&quot;jdbc driver 표준 인터페이스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JDBC Driver 표준 인터페이스&lt;/h2&gt;
&lt;p&gt;각 DB 벤더마다 스팩이 다른 문제를 해결하기 위해, &lt;strong&gt;자바 애플리케이션과 데이터베이스 시스템 사이에서 각기 다른 DB 벤더 연산 문제를 해결하며 둘을 상호 연결하기 위한 어댑터가 필요함을 느낄 수 있다.&lt;/strong&gt; 실제로 자바 진영에선 어댑터로 JDBC Driver 를 제공한다. 즉, &lt;strong&gt;JDBC Driver 란 다양한 DB 벤더를 사용할 수 있도록 자바 애플리케이션 요청(SQL 포함한 요청)을 각 DBMS 가 이해할 수 있는 프로토콜로 그때그때 알맞게 변환해주는 어댑터이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/83a1bbd7b3de6d4e1064259bb0d938cd/da893/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.69325153374233%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABwUlEQVR42oWSX2sTQRTF90P6AQQVH6uCj76J4oOI8V0QX4t+AVuoKVYfJCoSSho1iTXRbrJ/Znd2NzOz+/POmiatUhy4DHPuvYd7zp2Ai07dQGEhPxOl438n+BswyyVxmpBMQ5I3RxIDkv0Bv173mfWOiKMIXeh/mZrmPGGzAiJpqKoKay2mdlTGUAs+e3vIePsA19Rtjc+ve72g1R1QSSKTJuPWhM5tpHm88sXPP8PWqxaL41gIzYauFLpRAbYhMLOU6P1XAe26eClTHf+YMhpPyHONdZb59IThoI+uc9JEYTzhStXw3SeeXL/D4cEHgm+jETdu3yLXOUmS8GU4QJXgeluU+5dIlZEJa9x2n+jyU1QdcTz/zqIIicp5S/iw84ggCLh7/x7Bz9mMzuMOTV23PobhiXjnTXsJk2domdw2juVkQb47aAkW8UK8FZ/rP6q63S7Xrlxld2eHwPullFp75iWf9dAaWY5MmPfGhDdfYCNRkinBzcXfxk92umVPmKYKnSvyLCVTGarSZB8nVA/2KOYp82ghihrKslzVp+0ysyzbEK63KtJ94bkoCkqzpMBRyPt0Ov91Cp8TTGvd3r8BYOD1+Wd5BpEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/83a1bbd7b3de6d4e1064259bb0d938cd/a6d36/image.png&quot;
        srcset=&quot;/static/83a1bbd7b3de6d4e1064259bb0d938cd/222b7/image.png 163w,
/static/83a1bbd7b3de6d4e1064259bb0d938cd/ff46a/image.png 325w,
/static/83a1bbd7b3de6d4e1064259bb0d938cd/a6d36/image.png 650w,
/static/83a1bbd7b3de6d4e1064259bb0d938cd/e548f/image.png 975w,
/static/83a1bbd7b3de6d4e1064259bb0d938cd/3c492/image.png 1300w,
/static/83a1bbd7b3de6d4e1064259bb0d938cd/da893/image.png 2130w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;개발자는 JDBC Driver 에만 의존하여, 각 벤더마다 다른 사용법과 스팩을 학습하고 다루지않아도 된다. 각 벤더에서 제공하는 Driver 는 MySQLDriver, OracleDriver 와 같이 구체타입이 정의되어 있는데, 어떤 벤더의 Driver 를 선택할지는 간단한 설정만 변경해주면 문제없이 동작한다. 설정과 관련한 내용은 이따가 자세히 다루도록 한다.&lt;/p&gt;
&lt;p&gt;JDBC Driver 는 크게 다음 3가지 기능을 수행하는 표준 인터페이스 객체를 제공한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Connection&lt;/code&gt; : DB 와 커넥션 연결을 추상화한 객체&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Statement&lt;/code&gt; : DB 에 SQL 전달하는 방법을 추상화한 객체&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;ResultSet&lt;/code&gt; : DB 로 부터 응답을 전달받는 방법을 추상화한 객체&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;JDBC Driver 에서 제공하는 위 3가지 객체를 사용하여 데이터베이스와 커넥션을 맺고, SQL 을 전달하고, DB 로 부터 응답을 전달받을 수 있다. 모두 추상화된 표준 인터페이스로, 그 어떠한 데이터베이스 벤더로 변경되더라도 코드 변경사항 없이 커넥션을 맺고 연산을 수행할 수 있다. 만약 MySQL 벤더를 사용한다면 위 Connection 객체의 구체 타입으로 MySQLConnection 구체 타입 객체가 자동으로 주입되며, Statement 의 경우 MySQLStatement, ResultSet 은 MySQLResultSet 구현체가 주입된다.&lt;/p&gt;
&lt;h3 id=&quot;drivermanager&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#drivermanager&quot; aria-label=&quot;drivermanager permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DriverManager&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DriverManager&lt;/code&gt; 는 자바 애플리케잇녀에서 사용할 수 있는 JDBC Driver 집합을 관리하는 클래스다. 즉, MySQLDriver, OracleDriver, ... 등 다양한 벤더의 Driver 를 그떄그떄 알맞게 제공해주는 클래스이다. 이 클래스를 통해 앞선 3가지 객체 중 &lt;code class=&quot;language-text&quot;&gt;Connection&lt;/code&gt; 객체로 데이터베이스 커넥션을 얻어오는 방법을 학습해보자.&lt;/p&gt;
&lt;p&gt;JDBC API 4.0 부터 &lt;code class=&quot;language-text&quot;&gt;DriverManager.getConnection()&lt;/code&gt; 메소드로 JDBC Driver 구체 타입을 자동으로 로드할 수 있다. 만약 우리 애플리케이션이 MySQL 을 사용한다면 Driver 는 MySQL 드라이버가 선택될 것이며, 아래의 경우 MySQL 벤더의 커넥션이 얻어와 질 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; connection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DriverManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PASSWORD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 &lt;code class=&quot;language-text&quot;&gt;DriverManager&lt;/code&gt; 는 아쉽게도 커넥션 풀, 분산 트랜잭션을 지원하지 않아 잘 사용되지 않는다고 한다. 이 외에도 &lt;code class=&quot;language-text&quot;&gt;Statement&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;ResultSet&lt;/code&gt; 이 존재하나, 이번 포스팅의 가장 큰 목적인 JDBC 를 이해하고 커넥션 풀을 다루는 것이 초점을 맞췄으므로 다루지 않도록 한다.&lt;/p&gt;
&lt;h3 id=&quot;커넥션을-얻어오는-방법이-각기-다름으로-인해-발생한-문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EB%84%A5%EC%85%98%EC%9D%84-%EC%96%BB%EC%96%B4%EC%98%A4%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%9D%B4-%EA%B0%81%EA%B8%B0-%EB%8B%A4%EB%A6%84%EC%9C%BC%EB%A1%9C-%EC%9D%B8%ED%95%B4-%EB%B0%9C%EC%83%9D%ED%95%9C-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;커넥션을 얻어오는 방법이 각기 다름으로 인해 발생한 문제점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커넥션을 얻어오는 방법이 각기 다름으로 인해 발생한 문제점&lt;/h3&gt;
&lt;p&gt;커넥션을 얻기 위해 앞선 JDBC DriverManager 로 매번 새로운 커넥션을 얻어오는 방법도 존재하나, 커넥션 풀에서 이미 생성된 커넥션을 얻어오는 방법이 존재한다. 만약 우리 애플리케이션에서 DriverManager 로 신규 커넥션을 생성하는 방식에서 커넥션 풀을 활용하는 방식으로 마이그레이션 하고 싶다면 어떻게할까? 반대로 DBCP 종류도 HikariCP 외에 다양한 커넥션 풀이 존재하는데, 기존에 사용하던 DBCP 벤더에서 다른 벤더로 마이그레이션 하고 싶다면? 아쉽게도 자바 애플리케이션내의 커넥션을 얻어오는 코드를 모두 변경해야한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;자바 애플리케이션에서 커넥션을 얻어오는 방법은 매우 다양하기 떄문에, 커넥션을 얻어오는 방법을 추상화했다.&lt;/strong&gt; 우리는 DataSource 라는 커넥션을 얻어오는 방법을 추상화한 인터페이스로 자바 애플리케이션 코드 변경없이도 커넥션을 얻는 코드를 유지할 수 있다. (이 떄문에 앞선 DriverManager 는 사실상 현재 거의 사용하지 않을 방법이며, DataSource 를 사용하여 커넥션 관련 연산을 편하게 처리할 것이다.)&lt;/p&gt;
&lt;h3 id=&quot;datasource&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#datasource&quot; aria-label=&quot;datasource permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DataSource&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 인터페이스는 데이터베이스의 커넥션을 얻어오기 위해 사용된다. 각 벤더는 자신의 DBMS 에 알맞는 &lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 구현체를 생성한다. 구현체라 함은, DriverManager, HikariCP, 기타 DBCP 등이 해당된다. &lt;strong&gt;DataSource 는  단일 커넥션, 커넥션 풀링, 분산 트랜잭션 등 다양한 커넥션 기능을 제공한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;실제로 아패와 같이 HikariCP 커넥션 풀에 대한 &lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 를 제공해준다. 아래처럼 &lt;code class=&quot;language-text&quot;&gt;setJdbcUrl()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;setUser()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;setPassword()&lt;/code&gt; 로 커넥션을 얻는 벤더에 접근하기 위한 설정을 할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;HikariDatasource&lt;/span&gt; dataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HikariDatasource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
dataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setJdbcUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DBCP_URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
dataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
dataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PASSWORD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;커넥션은 아래처럼 &lt;code class=&quot;language-text&quot;&gt;getConnection()&lt;/code&gt; 을 통해 가져올 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; connection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 HikariCP 외에도 다른 벤더를 사용하고 싶다면 그에 맞는 &lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 를 사용할 수 있다. 아래는 H2 데이터베이스에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 구현체인 &lt;code class=&quot;language-text&quot;&gt;JdbcDataSource&lt;/code&gt; 이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Datasource&lt;/span&gt; dataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;커넥션-풀링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80%EB%A7%81&quot; aria-label=&quot;커넥션 풀링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커넥션 풀링&lt;/h2&gt;
&lt;p&gt;이번 포스트의 목적인 JDBC 를 이해하고, 커넥션 풀을 어떻게 직접 다룰 수 있는지 자바 애플리케이션 코드를 직접 작성하며 학습하는 것 이었다. 이를위해, 지금부터 커넥션 풀링에 대해 학습해보도록 하자.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;커넥션 풀링이란 미리 다수의 커넥션 객체를 DBCP 에 생성해둔 뒤, 해당 커넥션을 재사용하는 방식을 뜻한다.&lt;/strong&gt; DBCP 와 관련한 내용은 &lt;a href=&quot;https://haon.blog/database/hikaricp-theory-1/&quot;&gt;HikariCP 와 데이터베이스 커넥션 풀(Connection Pool)&lt;/a&gt; 에서 다루었다. 앞서 학습한 DriverManager 로 매번 새로운 커넥션을 생성하는 것은 매우 비효율적이므로, 커넥션 풀링을 통해 애플리케이션 성능을 최적화할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;hikaricp-커넥션-풀링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hikaricp-%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80%EB%A7%81&quot; aria-label=&quot;hikaricp 커넥션 풀링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HikariCP 커넥션 풀링&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/943e87f335a205604e769e5b05735e1f/46115/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.49079754601227%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABrUlEQVR42l2RzWsTURTF5w/KQkTETe1CqiD0D3Ch4N6tIAVxU3BX3biwlmIFQQsFA4pfxbRQP0jLxFKTMaQfKUyKTYbJzCSdmeS9Nz/fTJIae+Fw3+Weezj3PiNJElpuC7th0/baBEGA29a5EzAKIQSO4+B5Hn7g47ouvu+f9hMNNXwbKlF0wy5xFJOKZ4Rh9jod5gtPsf6UsjrljkciZZaby6+oP5xFxTFGOjyCUiqDkCIjbtUq5B6cp2ivZnVP9JBaZAQlBrz6vbtY164gtAFjXOisoLlrcXH2MmZjfbC67I/xEvrhCWE/pjYzw/b0NKLb+d/hCFINVjH3UsEJTHsoqMRQbNB3q9tYb5fYun2Lzakp+vr+pw7Prp7Gz/0ql1KH9j+H4zdOBX+/e8HGzRusTU7S0x9mpMNSKk6ikFAj1odteQ4bla+sfMtz7v4Ffhx+Ht4w4kv5A8Vf77GLixwUXlLJL5G/fpXlXI7I9zCOnSZHzSN29svUGlUqdUuvarJQeM5q+SNPPs1je3tEodS8Y56tz7Gy9pjdN3c4/P6ag1KR0uICm3OPkHHMX6yTQwhOyUgaAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/943e87f335a205604e769e5b05735e1f/a6d36/image-1.png&quot;
        srcset=&quot;/static/943e87f335a205604e769e5b05735e1f/222b7/image-1.png 163w,
/static/943e87f335a205604e769e5b05735e1f/ff46a/image-1.png 325w,
/static/943e87f335a205604e769e5b05735e1f/a6d36/image-1.png 650w,
/static/943e87f335a205604e769e5b05735e1f/e548f/image-1.png 975w,
/static/943e87f335a205604e769e5b05735e1f/46115/image-1.png 1290w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;기본적으로 별다른 설정이 없다면 스프링부트 애플리케이션은 &lt;code class=&quot;language-text&quot;&gt;HikariCP&lt;/code&gt; 을 디폴트 &lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 로 채택하고 있다. 이전에 다루었듯이, HikariCP 는 바이트코드 레벨 수준까지 극단적으로 최적화 되어있기에, 다른 DBCP 풀링 프레임워크에 비해 압도적으로 빠른 성능을 보이기 떄문이다. 지금부터 &lt;code class=&quot;language-text&quot;&gt;HikariCP&lt;/code&gt; 로 커넥션 풀링을 하는 방법을 학습해본다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;HikariCP&lt;/code&gt; 를 DataSource 로 사용하려면 기본적으롷 &lt;code class=&quot;language-text&quot;&gt;datSourceClassName&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;jdbcUrl&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;username&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;password&lt;/code&gt; 3가지 설정이 필수라고 한다. 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;HikariConfig&lt;/code&gt; 로 HikariCP 설정값을 부여할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;HikariConfig&lt;/span&gt; dataSourceConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HikariConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
dataSourceConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setJdbcUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DB_URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
dataSourceConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setUsername&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
dataSourceConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PASSWORD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또한 아래처럼 최대 커넥션 풀 사이즈로 설정 가능하며, 커넥션 풀 이름도 설정할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;dataSourceConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setMaximumPoolSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
dataSourceConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setPoolName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hikari Pool&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 &lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 의 구현체로 &lt;code class=&quot;language-text&quot;&gt;HikariDataSource&lt;/code&gt; 를 획득할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;HikariDataSource&lt;/span&gt; dataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HikariDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataSourceConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또한 직접 &lt;code class=&quot;language-text&quot;&gt;Connection&lt;/code&gt; 객체, 즉 커넥션을 직접 꺼내오고 싶다면, HikariCP 커넥션 풀로부터 이미 생성된 커넥션을 &lt;code class=&quot;language-text&quot;&gt;getConnection()&lt;/code&gt; 으로 꺼내올 수 있을 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; connection1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; connection2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;hikaricp-설정하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hikaricp-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0&quot; aria-label=&quot;hikaricp 설정하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HikariCP 설정하기&lt;/h3&gt;
&lt;p&gt;HikariCP 커넥션 풀링 세부 설정값은 &lt;code class=&quot;language-text&quot;&gt;application.yml&lt;/code&gt; 에서 간단히 설정할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;spring&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  datasource&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    hikari&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      pool&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; haon&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;pool 
      jdbc&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; jdbc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;h2&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;/test&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DB_CLOSE_DELAY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
      username&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; devhaon
      password&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; moheng
      maximum&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;pool&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;size&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; 
      minimum&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;idle&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
      connection&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;timeout&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;눈 여겨 볼만한 곳은 웃헌 &lt;code class=&quot;language-text&quot;&gt;maximum-pool-size&lt;/code&gt; 이다. 이는 HikariCP 풀 내에 미리 생성해 둘 커넥션 갯수를 설정하는 것이다. 다음은 &lt;code class=&quot;language-text&quot;&gt;connectio-timeout&lt;/code&gt; 인데, 이는 커넥션을 구하기 위해 대기하는 최대 시간이다. 만약 풀에서 모든 커넥션을 사용중이라면, 지정된 시간만큼 최대로 계속 대기하게된다. 기본값은 30초이다.&lt;/p&gt;
&lt;p&gt;우리 하모니 팀의 최적의 HikariCP 사이즈 값은 다음 포스트에서 다루어볼까 한다.&lt;/p&gt;
&lt;p&gt;추가적으로 만약 DB Replication 등 여러 &lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 에 대한 다중 설정이나 세부적인 설정을 하고싶다면 직접 DataSource 설정 관련 빈을 생성하고 설정해주도록 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MAXIMUM_POOL_SIZE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DB_URL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;jdbc:h2:./test;DB_CLOSE_DELAY=-1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;USER&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;devhaon&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PASSWORD&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;moheng&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;


    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hikariDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; dataSourceConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HikariConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        dataSourceConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setPoolName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;haon-pool&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        dataSourceConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setJdbcUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DB_URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        dataSourceConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setUsername&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        dataSourceConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PASSWORD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        dataSourceConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setMaximumPoolSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MAXIMUM_POOL_SIZE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HikariDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hikariConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;스프링 DB 1편 - 데이터 접근 핵심 원리, 김영한&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@miot2j/Spring-DB%EC%BB%A4%EB%84%A5%EC%85%98%ED%92%80%EA%B3%BC-Hikari-CP-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0&quot;&gt;https://velog.io/@miot2j/Spring-DB커넥션풀과-Hikari-CP-알아보기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/java-db-connection-pooling/&quot;&gt;https://hudi.blog/java-db-connection-pooling/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://escapefromcoding.tistory.com/712&quot;&gt;https://escapefromcoding.tistory.com/712&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[HikariCP 와 데이터베이스 커넥션 풀(DBCP) 최적화 고민하기 - 이론편]]></title><description><![CDATA[…]]></description><link>https://haon.site/database/hikaricp-theory/</link><guid isPermaLink="false">https://haon.site/database/hikaricp-theory/</guid><pubDate>Thu, 05 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/hikaricp-theory/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;데이터베이스-커넥션&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%BB%A4%EB%84%A5%EC%85%98&quot; aria-label=&quot;데이터베이스 커넥션 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 커넥션&lt;/h2&gt;
&lt;p&gt;스프링부트 애플리케이션을 사용하면 데이터베이스와의 통신으로 원하는 연산을 수행해야한다. 그런데 궁금한 점이 있다. 기본적으로 특별한 설정없이도 데이터베이스와 연결을 맺고 데이터를 통신하는데, 이는 내부적으로 어떠한 동작원리로 인해 수행되는 것일까? 이는 바로 데이터베이스와의 TCP 소켓을 통한 커넥션을 통해 가능한 것이었다.&lt;/p&gt;
&lt;p&gt;일반적으로 데이터베이스 연결의 생명주기는 아래와 같은 절차로 수행된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 클라이언트 요청에 따라, 스프링부트 애플리케이션 로직을 수행하기 시작한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 데이터베이스 드라이버를 사용하여 데이터베이스와 연결을 맺기위한 TCP 커넥션을 생성한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 데이터베이스와 연결된 TCP 커넥션으로 데이터를 통신한다. (이 과정에서 Statement 를 생성하고, SQL 문을 전송하고, ResultSet 을 통해 결과를 확인하는 작업이 발생한다.)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 데이터베이스와의 연결을 닫는다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; TCP 커넥션을 닫는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그런데 위와 같이 데이터베이스와 연결을 맺고, 해제하는 작업은 비용이 꽤나 많이 들어가는 작업이다. &lt;strong&gt;커넥션 객체를 생성하는 비용 자체도 문제가 될 뿐더러, 매번 SQL 쿼리 자체를 전송하기 위해 객체를 생성하고 해제하는 과정이 매우 비효율적으로 느껴진다.&lt;/strong&gt; 또한 커넥션 연결도 &lt;code class=&quot;language-text&quot;&gt;TCP/IP&lt;/code&gt; 연결을 통해 이루어진다. 즉, 3-way 헨드쉐이킹 과정을 통해 통신을 준비하는데, 이 과정이 쿼리를 요청할 떄 마다 반복되면 네트워크 구간에서 병목의 원인이 될 수 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;커넥션을 생성하는 과정은 전체 과정 중 대략 50%를 차지한다고 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;dbcp-데이터베아스-커넥션-풀&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dbcp-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%95%84%EC%8A%A4-%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80&quot; aria-label=&quot;dbcp 데이터베아스 커넥션 풀 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DBCP (데이터베아스 커넥션 풀)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/846610325cf910230590b53998ed9b57/09262/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.987730061349694%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACCUlEQVR42qWTv24TQRCHIyRehQfgDXiDvEHyAkCRKogmEoUlKBAlDUWAggYFV4kIQqYjmBSOLOJIjogT3/n2/u/Zt3e7dx/rtQKiAAmxutNIM3PfzP1mdo3vCZxEtEMBFylUhv85a0jN4PV7FiMfWvuYBm0MplSYbzP0WUDjrKCZZTamMdr8BWjP3fv3ODruO0dVVYShIAgXiMuE8CrGu8iJpwLPmxMlOVEU0jTtb6DGdtNeAw/29y0kIs9zfN8jTiTRcJd08pFCDPGPn6FKyfToCfNwRCZLtK5/0WqLKlpnHfD09JSiKFwszzO8wGr5ZZ121KH13kLvNm1lNT68BeIAkVQs5pJKVZjGEJxf8ebxc876Jyvg1tYWvV6Puq6ZzWZMPR9fpMRpQVYoLr2IRVlzMQ2R84rA5sRxTJLawm3L7quXrN28wfbDBytgt9tlMpm4DufzwgIF+vMG9fgF+nKP6tM6RqWoD3dogh6pXA6mtqyVjl/7fTY3Num+666ASikXNHaCvucxEzGcP6UVh5AcweiRVV3BcNtqNSTNFbquHGz53TX455R3dnYYDAaUZYmU0v1OZXPKWrNQNelCU9qipd0WZX1BENji5s9r0+l0GI/HzrFMTK02MrevzKzNyLPEFXI+uwl5Ll3usoEwDEmSBCEEWZatgMtBaK3/+VY0TeP2djnMa/sDFG446zg0WokAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/846610325cf910230590b53998ed9b57/a6d36/image.png&quot;
        srcset=&quot;/static/846610325cf910230590b53998ed9b57/222b7/image.png 163w,
/static/846610325cf910230590b53998ed9b57/ff46a/image.png 325w,
/static/846610325cf910230590b53998ed9b57/a6d36/image.png 650w,
/static/846610325cf910230590b53998ed9b57/e548f/image.png 975w,
/static/846610325cf910230590b53998ed9b57/3c492/image.png 1300w,
/static/846610325cf910230590b53998ed9b57/09262/image.png 1896w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;한번 요청이 들어올때마다 매번 데이터베이스 커넥션을 생성하고 해제하는 비용은 꽤 비효율적이다. 이를 해결하기 위해, &lt;strong&gt;여러개의 데이터베아스 커넥션을 미리 생성해두고 요청이 들어올때마다 꺼내다 쓰는 방안을 고안학게 됐다. 이 방식을 바로 데이터베이스 커넥션 풀(DBCP) 이라고 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;다시 정리하면, &lt;strong&gt;DBCP 는 데이터베이스 커넥션울 알정량 미리 생성해두고 필요할 때 마다 언제든 가져다 쓸 수 있는 일종의 저장소 개념이다.&lt;/strong&gt; 데이터베이스 커넥션 풀 내에는 이미 데이터베이스와 연결이 맺어진 일정량의 커넥션들이 존재한다. 매 요청이 들어올 때 마다 새롭게 커넥션을 맺고 닫는 대신에, 맺어놓은 커넥션을 닫지않고 유지한다. 클라이언트 요청이 유입되면 데이터베이스 커넥션을 DBCP 로 부터 꺼내다 쓰고, 사용을 마쳤다면 해당 커넥션을 DBCP 에 다시 반납한다.&lt;/p&gt;
&lt;h2 id=&quot;hikaricp&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hikaricp&quot; aria-label=&quot;hikaricp permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HikariCP&lt;/h2&gt;
&lt;p&gt;데이터베이스 커넥션 풀 프레입워크의 종류로는 &lt;code class=&quot;language-text&quot;&gt;HikariCP&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Tomcat Polling DataSource&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Commons DBCP2&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Oracle UCP&lt;/code&gt; 등 다양한 DBCP 종류가 존재한다. 그 중 성능이 압도적으로 뛰어나 대부분의 회사에서 사용중인 커넥션 풀인 HickriCP 를 중점적으로 학습해보자.&lt;/p&gt;
&lt;h3 id=&quot;datasource&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#datasource&quot; aria-label=&quot;datasource permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DataSource&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 란 데이터베이스와 커넥션을 맺는데 필욯란 정보를 제공하는 표준 인터페이스이다. DataSource 는 데이터베이스에 접근하기 위한 Driver, URL, 사용자 이름, 비밀번호등을 포함한다. 개발자는 DataSrouce 를 사용하여 데이터베이스와의 연결을 설정하고, DBCP 에서 커넥션을 가져올 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0c33fc6cb0f7fc7bff5644a9bc08328e/e5ca1/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.49079754601227%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABr0lEQVR42l2RP2/TQBiH83WQWKrAAmJAApZITEXqwIdAwNKBge7ACkOxRBEVFFQVlT+CIiBAgaKkNEJJXaQEpzVExDhxYjuxffaD75LUhVf66e53977P3b2XS5IEq/MH0zTpdDr0er39cRIiEliWRbfbxXEcbNv+Zz9JFY/nuTiJcX0X3x8Qx6NleYiMvu8z/3aeaqusvMw9GMk4v31/kb0b10iEICeLJwAJlIpEpHy1WefQlSnWjefKB1GISPcnilOAjMbFC2yfLSCCYAScgDLgKLG22+DI3HE2dl8pH4owy0vrQs/F8/voly9RmZ7OgP9LxBkwf/UYn5trykdxNIKNX2DXSlRXNL6cn6FUKBANhylw/FTZWQUk2e+lbhocnTvBRjO74cEW2bWvbK/e4d3MOYqnzxAFKVD2KwwDHLen5Hp9WnaLF5trLH9a4fDsFO8bTxVgEHisbi5RLD/AKF6n/lLj27LGo1MnWcrnCYcDclbX5udvk5qxw86ejt78TuXHFovrDynqr9HeLGA6dQZezK92m7sfb/Lkwy0az2YxS48xKmUq9xbYuq2pT/oLNkhD3S+WP+gAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/0c33fc6cb0f7fc7bff5644a9bc08328e/a6d36/image-1.png&quot;
        srcset=&quot;/static/0c33fc6cb0f7fc7bff5644a9bc08328e/222b7/image-1.png 163w,
/static/0c33fc6cb0f7fc7bff5644a9bc08328e/ff46a/image-1.png 325w,
/static/0c33fc6cb0f7fc7bff5644a9bc08328e/a6d36/image-1.png 650w,
/static/0c33fc6cb0f7fc7bff5644a9bc08328e/e548f/image-1.png 975w,
/static/0c33fc6cb0f7fc7bff5644a9bc08328e/3c492/image-1.png 1300w,
/static/0c33fc6cb0f7fc7bff5644a9bc08328e/e5ca1/image-1.png 1332w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;HickriCP 는 DBCP 를 제공하는 JDBC DataSource 의 구현체 중 하나이다.  스프링부트에 별다른 DataSource 설정이 없다면 기본적으로 내장되어 사용하게 되는 JDBC 커넥션 풀링 프레임워크이다.&lt;/p&gt;
&lt;p&gt;HikariCP 는 바이트코드 레벨 수준까지 극단적으로 최적화 되어있기에, 다른 DBCP 풀링 프레임워크에 비해 압도적으로 빠른 성능을 보인다. 이 외에도 &lt;a href=&quot;https://escapefromcoding.tistory.com/712&quot;&gt;HikariCP란?&lt;/a&gt; 의 내용을 빌리자면, 미세한 단위 까지 고려한 세부 최적화, 컬렉션 프레임워크를 직접 구현하여 영리하게 사용하기에 좋은 성능을 낼 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;적절한-dbcp-사이즈를-찾기위해-무엇을-고려해야-할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%81%EC%A0%88%ED%95%9C-dbcp-%EC%82%AC%EC%9D%B4%EC%A6%88%EB%A5%BC-%EC%B0%BE%EA%B8%B0%EC%9C%84%ED%95%B4-%EB%AC%B4%EC%97%87%EC%9D%84-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC-%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;적절한 dbcp 사이즈를 찾기위해 무엇을 고려해야 할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;적절한 DBCP 사이즈를 찾기위해 무엇을 고려해야 할까?&lt;/h2&gt;
&lt;p&gt;DBCP 사이즈를 구성할 때 사전 이해해야하는 몇가지 원칙과 고려사항이 존재한다.&lt;/p&gt;
&lt;h3 id=&quot;cpu-코어&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cpu-%EC%BD%94%EC%96%B4&quot; aria-label=&quot;cpu 코어 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CPU 코어&lt;/h3&gt;
&lt;p&gt;하나의 CPU 코어가 있는 컴퓨터에서도 수십 또는 수백개의 쓰레드를 동시에 지원할 수 있다. 하지만 이는 운영체제의 속임수일 뿐이다. 운영체제에서 컨텍스트 스위칭을 통해 동시에 진행하는 것 처럼 보이게 하는 속임수일뿐, 실제로는 단일 코어는 한 번에 하나의 쓰레드만 지원할 수 있다.&lt;/p&gt;
&lt;p&gt;단일 CPU 가 주어지면 여러 쓰레드를 순차적으로 실행하는 것이 시분할을 통해 여러 쓰레드를 동시에 실행하는 것보다 항상 빠르다. &lt;strong&gt;쓰레드 수가 CPU 코어 수를 초과하면 단순히 쓰레드 수가 더 많아질 뿐이지 더 빠른 속도를 보장하는 것은 아니다.&lt;/strong&gt; 즉, 단순하게 DBCP 사이즈를 늘린다고해서 더 빠른 속도로 처리하는 것이 아니다.&lt;/p&gt;
&lt;h3 id=&quot;디스크&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%94%94%EC%8A%A4%ED%81%AC&quot; aria-label=&quot;디스크 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;디스크&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e19bd038f765f697f4ea85b9bb129a74/307e7/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.263803680981596%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABLElEQVR42m1RCWrEMAzM/x9XuqQsuCXHxrmT5iD3qWpEXbJLBUNsazIaSRb9E/tx0LwsdPAX2PddsG3b3xk4z1P45ouwXsUg8KFcerMVFWVJeZ5T13XU9z2N4ygYhoEWLqi1piAIKI4icl2X38dnQYhN00RxkggxDEOKmKx1SFmWiRg4xhGEm6aRgvM8S86CQMlOqqqiNE3J83wW07Sum7wB5XdJjuuR47jCadtWHMOh7/tSHDrSMqrACSzDjf94cNWaiZ6Q7/c7FUUhrtTnl5zhdF1XSrgTjOQ6R8sk8DNElVL0fruJs7quBRDwOJ/nhTjEQsx4kLuGZZTh0rZtEW24pZDdYm5wgDcUjONY7ugKM0NHwNOWzQWVMEuQMWTAzHD43TA2+xpXMQj+AMPYZDonFAcrAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e19bd038f765f697f4ea85b9bb129a74/a6d36/image-2.png&quot;
        srcset=&quot;/static/e19bd038f765f697f4ea85b9bb129a74/222b7/image-2.png 163w,
/static/e19bd038f765f697f4ea85b9bb129a74/ff46a/image-2.png 325w,
/static/e19bd038f765f697f4ea85b9bb129a74/a6d36/image-2.png 650w,
/static/e19bd038f765f697f4ea85b9bb129a74/e548f/image-2.png 975w,
/static/e19bd038f765f697f4ea85b9bb129a74/3c492/image-2.png 1300w,
/static/e19bd038f765f697f4ea85b9bb129a74/307e7/image-2.png 1354w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;데이터베이스의 주요 병목 현상은 CPU 외에도 디스크, 네트워크 이렇게 3가지 요소가 영향을 미친다.&lt;/strong&gt; 데이터베이스는 일반적으로 디스크에 저장하는데,  디스크는 전통적인 모터 구동 암에 Read/Write 헤드가 장착된 회전 금속 를레이트로 구성된다. Read/Write 헤드는 한 번에 한 곳에만 읽을 수 있으며, 다른 쿼리에 대한 데이터를 읽기 위해선 새 위치를 검색해야한다. 즉, 플래터(원판) 을 돌려서 읽어야 할 데이터가 저장된 위치까지 디스크 헤더를 이동시킨 다음 데이터에 대한 읽기/쓰기 작업을 수행해야한다. &lt;strong&gt;따라서 탐색 시간 비용과 플래터의 데이터가 다시 돌아오기 까지 디스크를 기다려야 하는 회전 비용이 추가적으로 발생한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;디스크에서 위 과정이 발생하는동안 쓰레드는 &lt;code class=&quot;language-text&quot;&gt;Block&lt;/code&gt; 된다. 이 시간동안 다른 쓰레드의 작업을 처리할 수 있는 여유가 생기게된다. 이러한 여유 덕분에 실제로 더 많은 작업을 수행할 수 있게된다.&lt;/p&gt;
&lt;h3 id=&quot;네트워크&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC&quot; aria-label=&quot;네트워크 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;네트워크&lt;/h3&gt;
&lt;p&gt;네트워크도 디스크와 유사하다. 이더넷 인터페이스를 통해 유선으로 데이터를 작성하면 송/수신 버퍼가 가득차거나 멈출 때 block이 발생할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;적절한-dbcp-사이즈-공식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%81%EC%A0%88%ED%95%9C-dbcp-%EC%82%AC%EC%9D%B4%EC%A6%88-%EA%B3%B5%EC%8B%9D&quot; aria-label=&quot;적절한 dbcp 사이즈 공식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;적절한 DBCP 사이즈 공식&lt;/h2&gt;
&lt;p&gt;위와 같은 CPU, 디스크, 네트워크 요소의 다양한 상황을 고려했을 때, HikariCP 공식문서에선 아래와 같은 커넥션 풀 사이즈를 권장한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 connections = (core_count * 2) + effective_spindle_count&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;core_count&lt;/code&gt; 는 CPU 의 코어수를 의미하고, &lt;code class=&quot;language-text&quot;&gt;effective_spindle_count&lt;/code&gt; 는 데이터베이스 서버가 동시 관리할 수 있는 I/O 개수이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;core_count * 2&lt;/code&gt; : 코어 수에 근접할 수록 좋지만, 디스크 및 네트워크와 CPU 의 속도차이로 인한 여유시간을 활용하기 위해 계수 2를 곱해준다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;effective_spindle_count&lt;/code&gt; : 하드디스크는 하나의 spindle 을 가진다. Spindle 은 데이터베이스 서버가 관리할 수 있는 동시 I/O 요청 수를 뜻한다. 디스크가 n개 존재하면 spindle_count 는 n이 될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예를들어 하드디스크가 있는 8-core i7 CPU 를 가진 서버에서는 DBCP 사이즈를 대략 &lt;code class=&quot;language-text&quot;&gt;(8 * 2) + 1 = 17&lt;/code&gt; 으로 설정해야한다. 위 공식은 모든 상황에서 절대적으로 최적화됨을 보장하진 않기에, 맹신하진 말고 DBCP 크기를 선정할 때 대략적인 지표로만 참고하자.&lt;/p&gt;
&lt;p&gt;또한 &lt;code class=&quot;language-text&quot;&gt;core_count * 2&lt;/code&gt; 계산 값보다 커넥션이 더 필요한 경우는 거의 없다. 가령 사용자가 1000명일 경우 DBCP 크기를 1000개로 설정한 것은 커넥션을 과도하게 생성하여 낭비한 셈이다. 100개의 커넥션도 많다.&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;다음 포스트에선 JDBC Driver 와 HikariCP 를 실제로 스프링부트 애플리케이션에서 적용하는 방법에 대해 다루어보도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;스프링 DB 1편 - 데이터 접근 핵심 원리 (김영한)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/dbcp-and-hikaricp/&quot;&gt;https://hudi.blog/dbcp-and-hikaricp/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://escapefromcoding.tistory.com/712&quot;&gt;https://escapefromcoding.tistory.com/712&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://engineerinsight.tistory.com/238&quot;&gt;https://engineerinsight.tistory.com/238&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[JPA Auditing 으로 엔티티의 생성/수정 시간 자동 추적하기]]></title><description><![CDATA[Auditing JPA 에서는 Audit 라는 기능을 제공하고 있다. Audit 는 사전적으로  라는 뜻을 내포하는데, JPA 제공하는 Auditing 은 무엇이고, 어떻게 제공할까? JPA 의 Auditing…]]></description><link>https://haon.site/spring/jpa-auditing/</link><guid isPermaLink="false">https://haon.site/spring/jpa-auditing/</guid><pubDate>Tue, 03 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;auditing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#auditing&quot; aria-label=&quot;auditing permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Auditing&lt;/h2&gt;
&lt;p&gt;JPA 에서는 Audit 라는 기능을 제공하고 있다. Audit 는 사전적으로 &lt;code class=&quot;language-text&quot;&gt;심사하다, 감사하다&lt;/code&gt; 라는 뜻을 내포하는데, JPA 제공하는 Auditing 은 무엇이고, 어떻게 제공할까?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JPA 의 Auditing 기능은 엔티티가 생성되고, 변경되는 그 순간의 시점을 감지하여 생성시각, 수정시각, 생성한 사람, 수정한 사람을 기록할 수 있게 해준다.&lt;/strong&gt; 서비스를 운영시 생성된 데이터의 시점, 수정 일자를 기록한 뒤 추적할 때 유용하게 사용되는 기능이다.&lt;/p&gt;
&lt;p&gt;이번 포스트에선 JPA Auditing 기능을 간단한 실습과 함께 엔티티의 로그를 자동으로 기록하는 방법에 대해 학습하도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;auditing-활성화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#auditing-%ED%99%9C%EC%84%B1%ED%99%94&quot; aria-label=&quot;auditing 활성화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Auditing 활성화&lt;/h2&gt;
&lt;p&gt;가정먼저 &lt;code class=&quot;language-text&quot;&gt;@EnableJpaAuditing&lt;/code&gt; 어노테이션을 사용하여 Auditing 을 활성화해야 한다. 스프링부트내의 Application 클래스에 명시해도 되며, &lt;code class=&quot;language-text&quot;&gt;@Configuration&lt;/code&gt; 이 명시된 클래스를 별도로 생성하고 활성화시켜도 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@EnableJpaAuditing&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JpaAuditConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;baseentity-생성하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#baseentity-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0&quot; aria-label=&quot;baseentity 생성하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BaseEntity 생성하기&lt;/h2&gt;
&lt;p&gt;생성시각(createdAt) 과 수정시각(updatedAt) 은 특정 엔티티에서만 사용되는 것이 아닌, 프로덕션 내의 대부분의 엔티티에서 사용되는 필드일 것이다. 모든 엔티티의 생성 및 수정 시점을 기록하고 트래킹하는 것은 유용하게 사용되기 떄문이다. 따라서 Auditing 를 적용한 클래스를 추상 클래스로 분리하고, 각 엔티티가 이 클래스를 상속받아서 사용할 수 있도록하여 중복 코드를 최소화시켜보자.&lt;/p&gt;
&lt;h3 id=&quot;baseentity&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#baseentity&quot; aria-label=&quot;baseentity permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BaseEntity&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@MappedSuperclass&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@EntityListeners&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AuditingEntityListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseEntity&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@CreatedDate&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;created_at&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nullable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; updatable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt; createdAt&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@LastModifiedDate&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;updated_at&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nullable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt; updatedAt&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getCreatedAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; createdAt&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getUpdatedAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; updatedAt&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 신기한 점들이 보인다. &lt;code class=&quot;language-text&quot;&gt;@MappedSuperclass&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@EntityListeners&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@CreatedDate&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@LastModifiedDate&lt;/code&gt; 어노테이션의 기능은 무엇이고, 어떻게 사용되는 것일까?&lt;/p&gt;
&lt;h3 id=&quot;mappedsuperclass&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mappedsuperclass&quot; aria-label=&quot;mappedsuperclass permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@MappedSuperClass&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@MappedSuperClass&lt;/code&gt; 어노테이션은 공통 매핑 정보가 필요할 때 부모 클래스에 선언된 필드를 상속받는 클래스에서 그대로 사용할 때 사용한다. 즉, BaseEntity 클래스를 상속한 모든 엔티티 클래스에는 &lt;code class=&quot;language-text&quot;&gt;createdAt&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;updatedAt&lt;/code&gt; 필드가 생성된다. 이때, 부모 클래스에서 대한 테이블은 별도로 생성되지 않는다.&lt;/p&gt;
&lt;h3 id=&quot;entitylisteners&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#entitylisteners&quot; aria-label=&quot;entitylisteners permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@EntityListeners&lt;/h3&gt;
&lt;p&gt;Auditing 을 적용할 엔티티 클래스에 적용해야하는 어노테이션이다. &lt;strong&gt;이 어노테이션은 엔티티의 변화(이벤트)를 감지하여 엔티티와 매핑된 테이블의 데이터를 조작한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이 어노테이션의 파라미터로 이벤트 리스너를 넣어줘야한다. 그래야지 어떤 이벤트가 발생했을 때 엔티티와 매핑된 테이블의 데이터를 조작할지 결정할 수 있기 때문이다. 필자의 경우 &lt;code class=&quot;language-text&quot;&gt;AuditingEntityListener&lt;/code&gt; 클래스를 넣어줬다. &lt;code class=&quot;language-text&quot;&gt;AuditingEntityListener&lt;/code&gt; 클래스는 엔티티의 영속, 수정 이벤트를 감지하는 이벤트 리스터 클래스다.&lt;/p&gt;
&lt;h3 id=&quot;createddate&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#createddate&quot; aria-label=&quot;createddate permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@CreatedDate&lt;/h3&gt;
&lt;p&gt;생성날짜를 기록하려면 &lt;code class=&quot;language-text&quot;&gt;LocalDateTime&lt;/code&gt; 타입의 필드에 &lt;code class=&quot;language-text&quot;&gt;@CreatedAt&lt;/code&gt; 어노테이션을 명시한다. 그리고 생성날짜는 수정날짜와 달리 한번 생성되면 절대 수정되면 안되기 떄문에 &lt;code class=&quot;language-text&quot;&gt;updatable = false&lt;/code&gt; 을 적용해준다. 또한 null 값이여서도 안되므로 &lt;code class=&quot;language-text&quot;&gt;nullable = false&lt;/code&gt; 를 적용해준다.&lt;/p&gt;
&lt;h3 id=&quot;lastmodifieddate&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lastmodifieddate&quot; aria-label=&quot;lastmodifieddate permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@LastModifiedDate&lt;/h3&gt;
&lt;p&gt;수정날짜에 대해선 &lt;code class=&quot;language-text&quot;&gt;@LastModifiedDate&lt;/code&gt; 를 적용해준다. 엔티티가 수정될 때 마다 이벤트를 감지하고 그 수정날짜 시점을 필드에 최신화시킬 것이다.&lt;/p&gt;
&lt;h2 id=&quot;테스트-환경에서-auditing-활성화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-auditing-%ED%99%9C%EC%84%B1%ED%99%94&quot; aria-label=&quot;테스트 환경에서 auditing 활성화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;테스트 환경에서 Auditing 활성화&lt;/h2&gt;
&lt;p&gt;만약 테스트 환경내의 레포지토리 레이어를 테스트할 때도 동일하게 생성 및 수정날짜를 자동 생성되도록 만들고 싶다면, 앞서 생성한 &lt;code class=&quot;language-text&quot;&gt;@EnableJpaAuditing&lt;/code&gt; 을 명시한 클래스를 Import 해주도록한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@DataJpaTest&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;JpaAuditConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RepositoryTestConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 까지 모두 마쳤다면, 여러 엔티티에서 생성날짜와 수정날짜를 쉽게 기록하고 추적할 수 있게된다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[JPA 의 등장배경과 SQL 중심적인 개발의 문제점]]></title><description><![CDATA[💡 현재 포스트는 하모니 팀 기술 블로그 에 게시된 글 입니다. 우리 팀은 왜 ORM 을 사용할까? 스프링부트 생태계에서 제공되는 ORM 표준 기술인 JPA…]]></description><link>https://haon.site/spring/sql-problem/</link><guid isPermaLink="false">https://haon.site/spring/sql-problem/</guid><pubDate>Sun, 01 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/jpa-sql-problem/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;우리 팀은 왜 ORM 을 사용할까? 스프링부트 생태계에서 제공되는 ORM 표준 기술인 JPA 는 어떤 근거로 도입해야지 정말 타당한것일까? &lt;strong&gt;나는 과연 진정한 의미의 객체지향 프로그래밍을 위한 ORM 을 제대로 이해하고 개발해왔을까?&lt;/strong&gt; 그간 JPA 는 SQL 중심적인 개발의 문제점, 데이터베이스와의 패러다임 불일치 이슈로 인해 등장했음을 알고 있지만, 아쉽게도 명확하고 구체적인 이유를 자세히 풀어 설명하지 못하고 있는 상태다.&lt;/p&gt;
&lt;p&gt;JPA 는 이미 우리 팀에서 적극 활용되고 있는 ORM 이다. 하지만, ORM 을 왜 써야하는지에 대한 자세한 근거, 명확하게 설명을 풀어 설명하기까지에는 다소 무리인 상태이다. 이 때문에 이번 포스트에서 JPA 을 왜 사용해야하는지에 대한 높은 이해도를 확보하고, ORM 내에 담겨있는 숨겨진 의미를 깊게 이해하고자 한다.&lt;/p&gt;
&lt;h2 id=&quot;jpa-의-등장배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jpa-%EC%9D%98-%EB%93%B1%EC%9E%A5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;jpa 의 등장배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JPA 의 등장배경&lt;/h2&gt;
&lt;p&gt;1990년대 인터넷이 보급됨과 동시에 온라인 비즈니스가 활성화되었다. 자연스레, 온&lt;strong&gt;라인 비즈니스에서 데이터베이스에 데이터를 저장하고 가져올 때 사용할&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;Connection Connector&lt;/code&gt; &lt;strong&gt;에 대한 필요성이 높아졌다.&lt;/strong&gt; 이 때문에 각 프로그래밍 언어에서 DB Connection 을 지원하는 API 기술들이 등장했다. 스프링 생태계 또한 DB Connection 을 더 쉽개 관리할 수 있는 API 인 &lt;code class=&quot;language-text&quot;&gt;Spring JDBC API&lt;/code&gt; 를 만들고 지원했다. (이 외에도 쿼리문을 XML 파일을 통해 관리하게끔 도와주는 MyBatis 도 등장했다.)&lt;/p&gt;
&lt;p&gt;하지만, 여전히 쿼리문을 개발자가 직접 작성해야하는 등 다양한 문제점을 가지고 있었다. 따라서 스프링 생태계에선 개발자가 쿼리문을 직접 작성하지 않아도 프레임워크 내부에서 자동으로 쿼리문을 작성해주는 &lt;code class=&quot;language-text&quot;&gt;ORM(Object Relational Model)&lt;/code&gt; 기술인 JPA 가 등장했다.&lt;/p&gt;
&lt;h2 id=&quot;sql-중심적인-개발의-문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sql-%EC%A4%91%EC%8B%AC%EC%A0%81%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;sql 중심적인 개발의 문제점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SQL 중심적인 개발의 문제점&lt;/h2&gt;
&lt;p&gt;그렇다면 JPA 가 등장하기 이전까지 SQL 중심적인 개발의 문제점은 무엇이 있을까? 개발자가 쿼리문을 직접 작성하고 관리함으로 인해 어떤 불편함을 야기했을까? Spring JDBC API 를 활용하던 시절의 단점은 무엇이 있을지 학습해보도록 한다.&lt;/p&gt;
&lt;h3 id=&quot;반복적인-객체-테이블-매핑-노동-작업&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%98%EB%B3%B5%EC%A0%81%EC%9D%B8-%EA%B0%9D%EC%B2%B4-%ED%85%8C%EC%9D%B4%EB%B8%94-%EB%A7%A4%ED%95%91-%EB%85%B8%EB%8F%99-%EC%9E%91%EC%97%85&quot; aria-label=&quot;반복적인 객체 테이블 매핑 노동 작업 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;반복적인 객체-테이블 매핑 노동 작업&lt;/h3&gt;
&lt;p&gt;JDBC API 는 쿼리문을 개발자들이 직접 작성해야한다. 이 떄문에 개발자들은 쿼리문을 작성하는 지루한 작업을 무한 반복해야했다. 쿼리문을 직접 작성해야했던 이유는, &lt;strong&gt;객체지향, 관게형 데이터베이스 패러다임 불일치 문제로 부터 발생했다. 객체를 관계형 데이터베이스에 저장하고 가져오는 작업은 곧 객체를 테이블로, 테이블을 객체로 매핑하는 작업과 같다.&lt;/strong&gt; 객체라함은 객체지향 프로그래밍을 준수하며 작성된 자바 코드 객체를 뜻한다.&lt;/p&gt;
&lt;p&gt;이런 매핑 작업을 개발자가 일일이 수행함으로써 얻는 이점은 있지도 않고, 시간만 잡아먹으며 노동 작업이다. 또한 테이블 구조가 조금이라도 변경된다면, 코드의 SQL 문을 변경된 테이블 구조에 알맞게 수정해야한다. 결국 개발자는 비즈니스 로직, 테스트, 객체지향에 집중하기 보다 SQL 매핑 작업에 더 몰두할 수 있게된다. 효율적인 로직 고민이 아닌, 일명 SQL 매핑 노가다 개발자가 될 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;각-데이터베이스의-방언을-지원하지-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%81-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EB%B0%A9%EC%96%B8%EC%9D%84-%EC%A7%80%EC%9B%90%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;각 데이터베이스의 방언을 지원하지 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;각 데이터베이스의 방언을 지원하지 않는다&lt;/h3&gt;
&lt;p&gt;비슷한 이유로, 객체를 데이터베이스에 저장하기 위해 각 DB 별로 알맞게 종속적인 SQL 쿼리문을 작성해야한다. 만약 MySQL 을 사용하는 팀에서 Oracle DB 로 마이그레이션 해야한다면? 빠른 기간내에 NoSQL 로 마이그레이션 해야한다면?&lt;/p&gt;
&lt;p&gt;아쉽게도 JPA 가 없는 환경에선 쿼리문을 Dao 에 일일이 작성해야 하므로, 각 데이터베이스에 알맞은 쿼리문을 Dao 내에 일일이 새롭게 작성해줘야한다. 데이터베이스에 의존적인 문제로 인해 모든 쿼리문을 일일이 작성해야하고, 이는 유지보수를 어렵게 만든다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;데이터베이스를 언제든 쉽게 갈아끠울 수 있는 하는 것을 &quot;방언&quot; 이라고 한다. 하지만 각 DB 에 알맞은 전용 Dao 과 SQL 을 작성함으로 인해 방언을 지원받지 못한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;객체-그래프-탐색의-어려움-객체와-관계형-db의-패러다임-차이&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%9D%EC%B2%B4-%EA%B7%B8%EB%9E%98%ED%94%84-%ED%83%90%EC%83%89%EC%9D%98-%EC%96%B4%EB%A0%A4%EC%9B%80-%EA%B0%9D%EC%B2%B4%EC%99%80-%EA%B4%80%EA%B3%84%ED%98%95-db%EC%9D%98-%ED%8C%A8%EB%9F%AC%EB%8B%A4%EC%9E%84-%EC%B0%A8%EC%9D%B4&quot; aria-label=&quot;객체 그래프 탐색의 어려움 객체와 관계형 db의 패러다임 차이 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;객체 그래프 탐색의 어려움 (객체와 관계형 DB의 패러다임 차이)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3052e7f5ddc0b1eb0b88b3da23bd6612/d7e70/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.012269938650306%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABKUlEQVR42qVRa4uDQAz0//+molWQfmihFaGgfVCptD6wPvuund4EdpE7ODguEDebZMfJxMCX3e93bLdbrNdr8aIo8Hg8xKuqQp7nWK1WSJIEXdfxCfq+x+FwQBiG8uZ4PEre4Od2u8HzPIzHY0wmE8RxjPP5LJ6mKTabjeSXyyVOp5MGJJDjOBiNRhJrwPf7LZe6roXVd1N1nipWxumaptF1Dfh8PpFlmbB6vV7CgM4fXC4XyV+vV+ljP2uMCUaJeNeAvFAj0zQxn89Fm7ZtRa/dbgfbtmFZFlzXldEUSBRFmE6nWCwW2O/3gmMo6tSR2pVlKQVlZEgpCMKTfcrImjk64x8akhXH+I/pLXP9s9kMvu/rTQ6XMvTf8oYai1oFQSDOsYfM/8LwA8Xls07HlsASAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/3052e7f5ddc0b1eb0b88b3da23bd6612/a6d36/image.png&quot;
        srcset=&quot;/static/3052e7f5ddc0b1eb0b88b3da23bd6612/222b7/image.png 163w,
/static/3052e7f5ddc0b1eb0b88b3da23bd6612/ff46a/image.png 325w,
/static/3052e7f5ddc0b1eb0b88b3da23bd6612/a6d36/image.png 650w,
/static/3052e7f5ddc0b1eb0b88b3da23bd6612/e548f/image.png 975w,
/static/3052e7f5ddc0b1eb0b88b3da23bd6612/d7e70/image.png 1286w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;객체지향에서 객체간의 연관관계는 &quot;참조&quot;를 통해 맺어지고, 관계형 데이터베이스에서 테이블간의 연관관계는 &quot;외래키(FK)&quot; 를 통해 맺어진다.&lt;/strong&gt; SQL 중심적인 코드를 작성하면, 객체의 구조를 테이블에 알맞게 변형시켜서 저장해줘야 한다. SQL 중심적인 개발이 된다면, 위에서 Member 가 Order 를 참조하는 형태가 아니라, Order 의 PK 를 보유하고 있는 형태로 설계해야한다.&lt;/p&gt;
&lt;p&gt;그런데 아직도 궁금증이 남는다. 왜 객체지향적인 연관계를 가진 객체들을 데이터베이스에 저장할 떄, 데이터베이스의 연관관계로 변경하는 것이 왜 문제가 될까? 더 높은 이해도를 위해, 예시를 들어보겠다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDate&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; member&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 위와같이 모델링된 Member 와 Order 를 데이터베이스에서 저장하기 위해선 앞서 설명한 &lt;strong&gt;반복적인 객체-테이블 매핑 작업&lt;/strong&gt; 이 필요하다. 매핑, 즉 객체를 DB 내의 테이블로 변환하기 위해 어떤 과정이 필요할까? 데이터베이스에 접근하고자 하는 DAO 객체에 Order 객체를 분해하고 각자 Order 테이블과 Member 테이블에 대한 쿼리를 작성해야할 것이다. 끔찍하게도, 단순히 Order 의 객체 정보를 DB 에 저장하는데만 3가지 과정을 거쳐야한다.&lt;/p&gt;
&lt;p&gt;이런 복잡하고 매핑 과정을 피하기 위해, 아래처럼 Member 와 Order 를 DB 테이블 구조에 맞추어 설계할 수 있다. &lt;strong&gt;즉, 객체를 데이터베이스에 저장하기 편하도록 객체간의 참조 관계를 외래키(FK) 로 변환해야한다. 하지만, 이는 객체 모델링을 할 떄 객체가 서로 참조하는 객체지향적인 개발이 아니라 데이터베이스 테이블 구조에 맞추어 개발하는 SQL 중심적인 개발을 하게 된다는 문제점을 가진다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDate&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; member_id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;모든-객체를-미리-로딩할-수-없다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A8%EB%93%A0-%EA%B0%9D%EC%B2%B4%EB%A5%BC-%EB%AF%B8%EB%A6%AC-%EB%A1%9C%EB%94%A9%ED%95%A0-%EC%88%98-%EC%97%86%EB%8B%A4&quot; aria-label=&quot;모든 객체를 미리 로딩할 수 없다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;모든 객체를 미리 로딩할 수 없다&lt;/h3&gt;
&lt;p&gt;맨 위의 그림에서 Member 객체를 데이터베이스로부터 가져오기 위해선 Team, Order 를 비롯한 여러 객체관계의 복잡한 참조관계를 데이터베이스에 쿼리로 삽입해야한다. 하지만, 객체 타입이 많아짐에따라 연관관계가 복잡해질수록 Dao 의 쿼리문 또한 복잡해지고, 갯수 또한 매우 많아지기에 유지.보수가 어려워질 것이다.&lt;/p&gt;
&lt;p&gt;필요한 데이터만 조회하는 기능을 만들때도 유지.보수가 어려운 것은 매한가지다. 필요한 데이터만을 조회하는 쿼리를 작성하더라도 쿼리가 다소 복잡한 것은 몰론, 필요한 데이터만을 조회하는 메소드를 만들기에도 유지보수성이 좋지 못하다. 아래와 같이 DAO 에다 Member 에 연관된 이들을 조회하기 위한 Getter 메소드를 개발하자니, 메소드 가독성은 몰론 내부 쿼리문도 매우 복잡할 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;memberDao&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
memberDao&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOrderWithItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
memberDao&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getItemWithOrderAndCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;orm-의-표준인-jpa-의-등장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#orm-%EC%9D%98-%ED%91%9C%EC%A4%80%EC%9D%B8-jpa-%EC%9D%98-%EB%93%B1%EC%9E%A5&quot; aria-label=&quot;orm 의 표준인 jpa 의 등장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ORM 의 표준인 JPA 의 등장&lt;/h2&gt;
&lt;p&gt;객체와 관계형 데이터베이스의 패러다임 불일치(차이) 로 인해, 우리는 객체지향 프로그래밍을 하지 못하고 데이터베이스에 종속적인 SQL 매퍼 개발자가 되버린다. 이러한 데이터베이스 및 SQL 중심적인 개발 문제점을 해결하기위해 ORM 이 등장했다.&lt;/p&gt;
&lt;p&gt;ORM 인 JPA 를 통해 개발자는 더 이상 쿼리문을 반복적으로 작성하거나 유지보수하는데에 신경쓰지 않아도 된다. JPA 가 자동으로 쿼리문을 작성해주며, 심지어 설정 하나만 바꾸면 얼마든지 DB 에 알맞은 전용 SQL 를 생성해주는 방언까지 지원해준다.&lt;/p&gt;
&lt;p&gt;향후 JPA 에 대해서 꾸준히 포스트를 다루어볼까 한다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[객체지향 생활 체조 원칙, 견고한 애플리케이션을 만들기 위해]]></title><description><![CDATA[…]]></description><link>https://haon.site/java/oop-principle/</link><guid isPermaLink="false">https://haon.site/java/oop-principle/</guid><pubDate>Thu, 29 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/oop/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt;에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fed502286dfefff478fb036e0e57c5d9/e0885/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.122699386503065%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACDUlEQVR42p3T30uTURjAca83Nt1GF3u3/6DLikwvgqKU3GpXYXfRhXd11XXJNjAIzMxskQ2W5RYNnW6auhlsiaROZaXiIoiWbVmwrWFjv959e33DiAJnHXg4N8/58DznOadOr9ezGwaDgfr6Bk6fOkHP3Wc0t16jqeU6Z853MT0dJBicwu/3Mzs7S0dHh5RbL5/53aj7E1SpVFjOmclti7xcSDK3lGRl7Su7q1wuy7vdbkepVGI0GmuDJpMZUSyT+PCOSqVMtVohn89TLJYoFIoyaLVa9w9aLBYymSxDQx7cbg8u1yMi4QiLi1ESiYQM2my2/YNm806FFTbi66yux1heifJ56xPFUl6q9Pt/gFLLUOF98jUfv6yxuRXn7eY8yUycfCH376DJZJIPpVIp0um01H6abDYj3WFR2rNUxao8FIVCURtUq9W0tbXJoCgNYyeqVZFS6ecwtr/lfk1ZoahRoSAYOKBr4OChk6w+7SU63MnU/APmAl30dPcSiXUTDw/AzA06r7aiVOkkUNijQgnUaDU0NR5nwnEb7+OHjHrvMTrYh88XwjN6n1cL02z03uTK2RZUWm3th62RkpobjzHhf8Hk2DAzYwEikWVC4TCx5TcsjYwQ93u4fOkiaulX7QkKgoBOp+XwkaM4Hf047/ThfuLDOTjI8/FxJgcchPpvEfC6aG+/gEaj+Qv8AQSlE911zXVGAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/fed502286dfefff478fb036e0e57c5d9/a6d36/image.png&quot;
        srcset=&quot;/static/fed502286dfefff478fb036e0e57c5d9/222b7/image.png 163w,
/static/fed502286dfefff478fb036e0e57c5d9/ff46a/image.png 325w,
/static/fed502286dfefff478fb036e0e57c5d9/a6d36/image.png 650w,
/static/fed502286dfefff478fb036e0e57c5d9/e0885/image.png 918w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;우리 팀이 가장 중요하게 생각하는 것 중 하나가 &lt;code class=&quot;language-text&quot;&gt;테스트&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;객체지향&lt;/code&gt; 이다. 테스트와 객체지향은 견고한 애플리케이션을 만들기 위한 진정한 설계원칙이다. &lt;strong&gt;객체지향 프로그래밍을 통해 각 컴포넌트간 역할과 책임을 명확히 수립하고, 그 역할과 책임을 완벽히 수행하는지에 대해 테스트로 검증해야한다.&lt;/strong&gt; 이 떄문에 우리 하모니 팀 또한 객체지향적인 코드를 만들기위해 많은 고민을 쏟아붓는다.&lt;/p&gt;
&lt;p&gt;그런데, 과연 우리 팀의 코드는 정말 객체지향 프로그래밍이 수행되는 것일까? 유지.보수가 손쉬운 프로덕션 코드를 개발하고 있을까? 이 의구심이 잠시 들때 객체지향 생활체조 원칙을 잘 준수하고 있는지에 대해 회고할 필요가 있음을 느꼈다.&lt;/p&gt;
&lt;p&gt;객체지향 생활체조 원칙은 이미 우리 팀에서 자주 언급된 원칙이다. 이 원칙에서 지침하는 프로그래밍 원칙을 준수하고, 안티패턴을 지양하고자 한다. &lt;strong&gt;객체지향 생활체조 원칙에서 권장하는 훈련 지침은 무엇이 있는가? 또한 각 원칙 사항이 어떠한 문제 상황을 제거하기 위해 만들어진 지침일까?&lt;/strong&gt; 우리 팀은 생활체조 원칙을 왜 지켜야하는지에 대한 당위성을 확보하고, 원칙에 담겨있는 숨겨진 의미를 이해하고자 한다. 높은 이해도와 함께 객체지향 생활체조 원칙을 준수하기 위해, 그리고 팀원들에게 지식을 공유하고자 이번 포스트를 작성해본다 🙂&lt;/p&gt;
&lt;h2 id=&quot;원칙1-한-메서드에-오직-한-단계의-들여쓰기만-한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%991-%ED%95%9C-%EB%A9%94%EC%84%9C%EB%93%9C%EC%97%90-%EC%98%A4%EC%A7%81-%ED%95%9C-%EB%8B%A8%EA%B3%84%EC%9D%98-%EB%93%A4%EC%97%AC%EC%93%B0%EA%B8%B0%EB%A7%8C-%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;원칙1 한 메서드에 오직 한 단계의 들여쓰기만 한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙1. 한 메서드에 오직 한 단계의 들여쓰기만 한다.&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;함수는 한 가지 일을 수행해야한다. 그 한가지만을 잘 해야한다. - Clean Code&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getLines&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt; stringBuilder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; j&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; j&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; j&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;one&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;two&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;한 메소드에 들여쓰기가 여럿 존재한다면, 해당 메소드는 여러가지 일을 수행한다고 봐도 무방하다. 메소드는 본인이 맡은 역할이 적을수록(잘개 쪼갤수록) 재사용성이 높고 디버깅도 유용하다. &lt;strong&gt;즉, 코드의 들여쓰기와 중첩 구조가 깊어질수록 가독성이 저하되므로, 들여쓰기는 최소화하는 것이 좋다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;메소드-추출-기법extract-method&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%94%EC%86%8C%EB%93%9C-%EC%B6%94%EC%B6%9C-%EA%B8%B0%EB%B2%95extract-method&quot; aria-label=&quot;메소드 추출 기법extract method permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메소드 추출 기법(Extract Method)&lt;/h3&gt;
&lt;p&gt;위와 같은 함수의 구조는 아래와 같이 개선할 수 있다. 소트웍스 앤솔로지 책에서 소개한 &quot;메소드 추출&quot; 기법을 활용하면 가독성이 개섢된다. 메소드 추출은 코드의 일부분을 메소드로 분리하여, 코드의 복잡도를 낮추는 기법이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getLines&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt; stringBuilder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;repeatPrint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;repeatRows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt; stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;repeatRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;two&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;repeatRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt; stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;one&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;원칙2-else-키워드를-사용하지-않는다-early-pattern-을-지향할-것&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%992-else-%ED%82%A4%EC%9B%8C%EB%93%9C%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4-early-pattern-%EC%9D%84-%EC%A7%80%ED%96%A5%ED%95%A0-%EA%B2%83&quot; aria-label=&quot;원칙2 else 키워드를 사용하지 않는다 early pattern 을 지향할 것 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙2. else 키워드를 사용하지 않는다. (Early Pattern 을 지향할 것)&lt;/h3&gt;
&lt;p&gt;필자도 객체지향 프로그래밍을 중요성을 깨닫지 못했던 시절, else 문을 정말 많이 사용해왔다. else 문이 있어야지 완벽한 로직 처리가 완성될 것이라 생각했지만, 이는 큰 오산이었다. 우리는 else 키워드를 사용하지 않고도 분기처리 코드를 충분히 작성할 수 있다.&lt;/p&gt;
&lt;p&gt;이 원칙은 앞선 &lt;strong&gt;원칙1. 한 메소드에 오직 한 단계의 들여쓰기만 한다&lt;/strong&gt; 내용과도 일맥상통한다. &lt;strong&gt;else 문을 남용하면 중첩된 여러 조건문이 작성되고, 이로인해 인텐트(들여쓰기) 의 깊이가 늘어날 수 있다. 즉, Arrow Anti Pattern 을 유발하는 사례까 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printPriceStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; price&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;price &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;price &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;price &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드는 파악하기 쉬운가? 중복되는 코드, 인텐트 깊이가 최소 4는 훌쩍 넘어보인다. 동작을 한 눈에 파악하기 힘들다. else 문을 남발함으로 인해 Arrow 안티패턴이 발생하여, 또 다시 중첩된 코드를 생선하여 인텐드가 더 깊어질 구조로 만들어질 것 같은 구조가 되었다. 중첩 구조가 깊어지니, 특정 if 절에 대한 else 문을 찾기 힘들어지기도 하다.&lt;/p&gt;
&lt;h3 id=&quot;early-pattern&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#early-pattern&quot; aria-label=&quot;early pattern permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Early Pattern&lt;/h3&gt;
&lt;p&gt;else 문을 남발하여 깊어진 인텐트 구조는 &lt;code class=&quot;language-text&quot;&gt;Early Pattern&lt;/code&gt; 을 적용하여 해겷 ㅏㄹ 수 있다. &lt;code class=&quot;language-text&quot;&gt;Early Pattern&lt;/code&gt; 이란 조건문내의 조건이 일치하면 그 즉시 리턴하는 디자인패턴이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printPriceStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; price&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;price &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;price &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;price &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 보호절(Guard Clause)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 의문점이 하나 생긴다. else 문을 사용했을 땐 일반 케이스를 벗어난 부분에 대해 처리할 수 있었는데, else 문을 사용하지 않을 경우 이 부분을 어떻게 해결할까? 이는 &lt;code class=&quot;language-text&quot;&gt;보호 구문(Guard Clause)&lt;/code&gt; 을 생성하여 해결 가능하다. &lt;code class=&quot;language-text&quot;&gt;보호 구문(Guard Clause)&lt;/code&gt;, &lt;strong&gt;즉 보호 절이란 if 문에서 일반적인 케이스로 처리가 되지 못하고 유효하지 않은 기타 예외처리 상황에 대해 처리해주는 구문을 뜻한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;다형성polymorphism-기반의-디자인패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%ED%98%95%EC%84%B1polymorphism-%EA%B8%B0%EB%B0%98%EC%9D%98-%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;다형성polymorphism 기반의 디자인패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다형성(Polymorphism) 기반의 디자인패턴&lt;/h3&gt;
&lt;p&gt;위처럼 간단한 경우는 &lt;code class=&quot;language-text&quot;&gt;보호 절&lt;/code&gt; 을 사용하면 충분하나, 객체지향 프로그래밍의 다형성을 활용하는 방법도 존재한다. 다형성을 활용하는 예로 &lt;a href=&quot;https://haon.blog/java/strategy-pattern/&quot;&gt;전략 패턴(Strategy Pattern)&lt;/a&gt; 이나 널 객체 패턴(Null Obect Pattern) 활용을 해볼 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;원칙3-모든-원시값과-문자열을-포장wrap한다-vo-로-변환할-것&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%993-%EB%AA%A8%EB%93%A0-%EC%9B%90%EC%8B%9C%EA%B0%92%EA%B3%BC-%EB%AC%B8%EC%9E%90%EC%97%B4%EC%9D%84-%ED%8F%AC%EC%9E%A5wrap%ED%95%9C%EB%8B%A4-vo-%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%A0-%EA%B2%83&quot; aria-label=&quot;원칙3 모든 원시값과 문자열을 포장wrap한다 vo 로 변환할 것 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙3. 모든 원시값과 문자열을 포장(Wrap)한다. (VO 로 변환할 것)&lt;/h2&gt;
&lt;p&gt;원시타입 데이터는 그 자체만으로 아무런 의미를 가지고 있지 않다. 원시값에 대한 의미를 변수명으로 추론하는 것도 한계가 존재하기 떄문에, 실수를 범할 가능성이 크다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;money &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;distance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 아래처럼 주문정보 오브젝트가 생성된다고 해보자. 주문 가격을 30만원이고, 배달거리는 1km 를 뜻하는 주문정보가 될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; order &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;300000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;반면 실수로 개발자가 로직을 잘못 서례하여 파라미터의 순서를 잘못 고려하여 값을 주입했다고 해보자. 아래 주문정보는 가격이 1원이며, 배달거리느 무려 300,000km 를 뜻하게 된다. 맙소사. 값 주입 로직을 잘못했더니 괴상한 주문 정보가 생성되었다. 이를 어찌할까?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; order &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3000000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;원시타입에-대한-집착&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%8B%9C%ED%83%80%EC%9E%85%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A7%91%EC%B0%A9&quot; aria-label=&quot;원시타입에 대한 집착 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원시타입에 대한 집착&lt;/h3&gt;
&lt;p&gt;위와 같은 실수를 범하게되는 주 원인은 무엇일까? 우선 money 와 distance 필드는 모두 int 라는 동일한 원시타입으로 선안되었다. 따라서 이 필드들을 구분할 수 있는것은 오로지 변수명에만 의존하게 된다. 비슷한 이유로, 생성자의 파라미터 순서에만 의존하여 필드주입을 시도한다. 이렇게 &lt;strong&gt;도메인 오브젝트를 표현시 원시타입만을 사용하여 표현하는 것을 원시타입에 대한 집착(Primitive Obession) 안티패턴&lt;/strong&gt; 이라고 한다.&lt;/p&gt;
&lt;h3 id=&quot;원시타입을-vovalue-object-로-감싸기-wrapping&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%8B%9C%ED%83%80%EC%9E%85%EC%9D%84-vovalue-object-%EB%A1%9C-%EA%B0%90%EC%8B%B8%EA%B8%B0-wrapping&quot; aria-label=&quot;원시타입을 vovalue object 로 감싸기 wrapping permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원시타입을 VO(Value Object) 로 감싸기 (Wrapping)&lt;/h3&gt;
&lt;p&gt;이 때문에 원시타입을 감싸야한다. 위 코드는 아래와 같이 개선할 수 있다. &lt;strong&gt;윈시 값을 도메인 클래스로 감싸서&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;VO(Value Object)&lt;/code&gt; &lt;strong&gt;로 재표현할 수 있으며, 이로써 원시 값에 대해 명확한 의미가 부여되고, 올바른 예외처리 및 비즈니스 검증이 가능해진다.&lt;/strong&gt; 기존의 돈, 거리 값과 관련한 비즈니스 검증 및 로직을 모두 Order 클래스 내에서 도맡에 수행했지만, 각 역할과 책임을 세분화하고 Money, Distance 에게 적절히 인가했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Distance&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Distance&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;money &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;distance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
   &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;validateInput&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;money&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;money &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Distance&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;validateInput&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;distance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;distance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 원칙은 추후 설명할 &lt;strong&gt;원칙8. 일급 컬렉션을 사용한다&lt;/strong&gt; 와도 매우 유사한 원리이자 규칙입니다.&lt;/p&gt;
&lt;h2 id=&quot;원칙4-한-줄에-점을-하나만-찍는다-디미터-법칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%994-%ED%95%9C-%EC%A4%84%EC%97%90-%EC%A0%90%EC%9D%84-%ED%95%98%EB%82%98%EB%A7%8C-%EC%B0%8D%EB%8A%94%EB%8B%A4-%EB%94%94%EB%AF%B8%ED%84%B0-%EB%B2%95%EC%B9%99&quot; aria-label=&quot;원칙4 한 줄에 점을 하나만 찍는다 디미터 법칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙4. 한 줄에 점을 하나만 찍는다. (디미터 법칙)&lt;/h2&gt;
&lt;p&gt;앞선 주문 정보 Order 클래스에 대한 금액인 Money 를 조회하고 싶다면 아래와 같이 조회할 수 있을 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;order&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;백만원 이상의 주문가격은 허용되지 않습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 위 코드는 문제점을 무엇일까? 바로 &lt;strong&gt;한 줄에 점이 2개이상 찍히면서 결함도가 높아졌다는 점이다.&lt;/strong&gt; 위 코드는 Order 뿐만 아니라 Money 에 대한 의존성도 동시에 갖게 되었다. 만약 Order 와 Money 중에 변동사항이 생길 경우, 그 파급력은 객체 모두에게 끼칠 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;디미터-법칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%94%94%EB%AF%B8%ED%84%B0-%EB%B2%95%EC%B9%99&quot; aria-label=&quot;디미터 법칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;디미터 법칙&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;디미터 법칙(Demeter Law) : 낯선 이와 대화하지 말고, 친구하고만 대화하라&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이번 4번째 원칙은 디미터 법칙을 풀어쓴 것이라고 볼 수 있다. &lt;strong&gt;즉, 본인이 소유한 객체, 본인이 생성한 객체, 그리고 누군가 준(파라미터로) 객체에만 메세지를 전송해야함을 뜻한다.&lt;/strong&gt; 그렇지 않을겨웅, 다른 객체에 너무 깊숙하게 관여하게 되는 것이며 (과한 결합도가 생긴다), 이는 객체지향 프로그래밍의 캡슐화를 위반하는 것이다.&lt;/p&gt;
&lt;p&gt;위는 아래와 같이 디미터 법칙을 적용하여 해결할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;order&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isOverFlowMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;백만원 이상의 주문가격은 허용되지 않습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isOverFlowMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;점을 하나만 사용하여 코드를 개선했다. Order 의 Money 의 메소드를 호출하는 것이 아니라, Order 에게 직접 질문을 던지는 방식으로 개선했다. 이로써 Order 는 자신이 가지고 있는 객체를 적절히 활용하여 더 능동적으로 행위를 수행하게 되었다.&lt;/p&gt;
&lt;h3 id=&quot;디미터-법칙을-적용하지-않아도-괜찮은-사례&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%94%94%EB%AF%B8%ED%84%B0-%EB%B2%95%EC%B9%99%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EC%95%84%EB%8F%84-%EA%B4%9C%EC%B0%AE%EC%9D%80-%EC%82%AC%EB%A1%80&quot; aria-label=&quot;디미터 법칙을 적용하지 않아도 괜찮은 사례 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;디미터 법칙을 적용하지 않아도 괜찮은 사례&lt;/h3&gt;
&lt;p&gt;디미터 법칙을 위반해도 괜찮은 예외적인 케이스가 일부 존재하는데, &lt;code class=&quot;language-text&quot;&gt;스트림(stream)&lt;/code&gt; &lt;strong&gt;처럼 메소드 체이닝(chaining) 하는 경우는 점을 여러번 사용해도 이미 디미터 법칙을 위반하지 않는다.&lt;/strong&gt; 또한 &lt;code class=&quot;language-text&quot;&gt;DTO&lt;/code&gt; 의 경우도 내부 구조를 외부에 유출하는 것을 목적으로 설계되기 떄문에 디미터 법칙을 특별히 적용하지 않는다.&lt;/p&gt;
&lt;h2 id=&quot;원칙5-줄여쓰지-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%995-%EC%A4%84%EC%97%AC%EC%93%B0%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;원칙5 줄여쓰지 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙5. 줄여쓰지 않는다&lt;/h2&gt;
&lt;p&gt;메소드, 클래스에 대한 이름을 과도하게 줄인다면 코드의 가독성을 저하시킨다. 짧다고 좋은 것이 아니다.&lt;/p&gt;
&lt;p&gt;축약하고 싶은 욕구가 생기는 이유는 이름이 길기 때문이다. &lt;strong&gt;이름이 긴 이유는 해당 클래스, 메소드가 혼자 너무 많은 일을 수행하기 때문입니다.&lt;/strong&gt; 만약 그게 클래스라면 &lt;code class=&quot;language-text&quot;&gt;SRP(단일 책임 원칙)&lt;/code&gt; 을 위반하고 있을 가능성이 크다.&lt;/p&gt;
&lt;h2 id=&quot;원칙6-모든-엔티티를-작게-유지한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%996-%EB%AA%A8%EB%93%A0-%EC%97%94%ED%8B%B0%ED%8B%B0%EB%A5%BC-%EC%9E%91%EA%B2%8C-%EC%9C%A0%EC%A7%80%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;원칙6 모든 엔티티를 작게 유지한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙6. 모든 엔티티를 작게 유지한다.&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;50줄이 넘어가는 클래스 또는 10개 이상의 패키지는 없어야한다는 원칙&lt;/strong&gt;이다. 50줄 이상이라면 보통 클래스가 1가지 일만 하지 않기 떄문에, 코드의 가독성은 저하된다. 이는 SRP 원칙을 위반하는 사례로 번질 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;원칙7-2개를-초과하는-인스턴스-변수를-가진-클래스를-쓰지-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%997-2%EA%B0%9C%EB%A5%BC-%EC%B4%88%EA%B3%BC%ED%95%98%EB%8A%94-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EB%B3%80%EC%88%98%EB%A5%BC-%EA%B0%80%EC%A7%84-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%A5%BC-%EC%93%B0%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;원칙7 2개를 초과하는 인스턴스 변수를 가진 클래스를 쓰지 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙7. 2개를 초과하는 인스턴스 변수를 가진 클래스를 쓰지 않는다.&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;인스턴스 변수가 많아질수록 클래스의 응집도가 낮아지는 것을 뜻한다.&lt;/strong&gt; 여기서 말하는 인스턴스 변수란 기본 원시타입 또는 컬렉션등을 뜻하지 않을까 싶다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/73e78be6c3e80807484dad7656674b2b/29beb/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.601226993865026%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACE0lEQVR42n2S3U/TUBjG+fPUEKOYmHjlH+FfYLwxJl6Z6IWReIGiEiXGbMEvZAgBNhgImLDh5ujWjbVNu7brPnrW/nxLI05Rn+bpaXPO+5zn/ZggQaQgOIJBW2iAVyYemMSyFceRMD5lguQtEYzgt/9knUACsDag9gSq03D0nLh8D5pPJcIdC0kxkvO+4dCay1Oa+cTy/ZfsPF7AfFHALjcSQbm5p0NfnLklWTWU18DJLdJZ20Nb+YqV/4ZfqOHUjjFsi05buFzC+LhPNZun/q6Iv1TCr5siOA4xMlARek1Hny1wPLNB7u4sh9OL2DN59IKIOBYjpeRoTCRmEsfx6UMqaGpltIMvdBrixHMZDgdp/cYS/vk9HIW0yhq1Ozl2b2XI3nhE4eY8+u0VjjcrqaBR3Wd39T2tUpFBv08YKumTQqkhUTQUR2HKkSKUDFr1A0ofrvH52SSvHpznzcNJDhYuYGhrf6Qs6AZ9GonjwnWsnUuszk9SWZnCLJ6jWcnQcRVd36JrvsVvZ3BbWcx6lsB8TT9opoLpeKQ86aSk5Zlb+OY6pr4uTdgQgTU8p4Hr9egFNr61JXtFtO+bOO0irrkjmfXOOhyft7OIRHAgTVvH2btMOXeFpbmLHC5PYWxfpdupnBX8JRz9helFQa9PvdHGtl2hjWHa6E1D6qv+Lfg/hKGURKYhCLr4vndC1+1IExU/AMkxNBLUD2OvAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/73e78be6c3e80807484dad7656674b2b/a6d36/image-1.png&quot;
        srcset=&quot;/static/73e78be6c3e80807484dad7656674b2b/222b7/image-1.png 163w,
/static/73e78be6c3e80807484dad7656674b2b/ff46a/image-1.png 325w,
/static/73e78be6c3e80807484dad7656674b2b/a6d36/image-1.png 650w,
/static/73e78be6c3e80807484dad7656674b2b/e548f/image-1.png 975w,
/static/73e78be6c3e80807484dad7656674b2b/3c492/image-1.png 1300w,
/static/73e78be6c3e80807484dad7656674b2b/29beb/image-1.png 1830w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;다시 풀어쓰자면, &lt;strong&gt;한 클래스에 최대 2개 이하의 인스턴스 변수만 배치해야한다는 원칙이다.&lt;/strong&gt; 이를 준수했다면 최대한 클래스를 세분화 및 분리함으로써 각 인스턴스에 대한 높은 응집도를 만들 수 있다는 것이다. 덩치가 큰 객체를 작은 크기의 여러 객체로 분해하면, 자연스레 인스턴스 변수들을 적절히 배치할 수 있을 것이다.&lt;/p&gt;
&lt;h2 id=&quot;원칙8-일급-컬렉션을-쓴다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%998-%EC%9D%BC%EA%B8%89-%EC%BB%AC%EB%A0%89%EC%85%98%EC%9D%84-%EC%93%B4%EB%8B%A4&quot; aria-label=&quot;원칙8 일급 컬렉션을 쓴다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙8. 일급 컬렉션을 쓴다.&lt;/h2&gt;
&lt;p&gt;이 원칙은 &lt;strong&gt;원칙3. 모든 원시값과 문자열을 감싼다(Wrap)&lt;/strong&gt; 와도 일맥상통하는 원칙이다. 컬렉션도 클래스로 래핑하지 않는다면 의미없는 단순 컬렉션이 될 수 있다. 또한 일급 컬렉션을 사용하면 이곳저곳에 흩어진 비즈니스 로직이 하나의 일급컬렉션 내부에 응집되고, 중복 코드가 최소화된다.&lt;/p&gt;
&lt;p&gt;만약 로또 생성기를 통해 로또를 당첨할 경우, 당첨된 로또의 숫자(아이템) 갯수는 최대 7개까지 허용될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Lotto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; lottoItems &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lottoItems&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RunTimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;당첨 로또의 갯수는 8개 이상일 수 없습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 위 비즈니스 검증 로직을 서비스 레이어에 배치하자니 여기저기에 비즈니스 검증 로직이 흩어지도, 중복 코드로 여럿 발생할 수 있을 것이다.&lt;/p&gt;
&lt;h3 id=&quot;일급-컬렉션으로-감싸기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%BC%EA%B8%89-%EC%BB%AC%EB%A0%89%EC%85%98%EC%9C%BC%EB%A1%9C-%EA%B0%90%EC%8B%B8%EA%B8%B0&quot; aria-label=&quot;일급 컬렉션으로 감싸기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;일급 컬렉션으로 감싸기&lt;/h3&gt;
&lt;p&gt;단순한 컬렉션을 일급 컬렉션으로 감싸보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LottoItems&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Lotto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; items&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LottoItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Lotto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; items&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;validateSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; items&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validateSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Lotto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; items&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RunTimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;당첨 로또의 갯수는 8개 이상일 수 없습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이곳저곳으로 흩어진 비즈니스 로직을 일급컬렉션이라는 객체 하나에 응집시켰다. 이로인해 관심사가 한 곳으로 응집되고, 중복 코드가 최소화될 것이다.&lt;/p&gt;
&lt;h2 id=&quot;원칙9-getter-setter-를-사용하지-않는다-tell-dont-ask&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%999-getter-setter-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4-tell-dont-ask&quot; aria-label=&quot;원칙9 getter setter 를 사용하지 않는다 tell dont ask permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙9. Getter, Setter 를 사용하지 않는다. (Tell. Don&apos;t Ask)&lt;/h2&gt;
&lt;p&gt;객체를 더 객체지향으로, 객체답게 활용하려면 &lt;strong&gt;객체가 충분히 혼자서 할 수 있는 작업은 최대한 객체에게 믿고 맡겨야한다는 원칙&lt;/strong&gt;이다. 이러한 원칙을 &lt;strong&gt;Tell, Don&apos;t Ask (뭍지 말고, 시켜라)&lt;/strong&gt; 원칙이라고 한다.&lt;/p&gt;
&lt;p&gt;예를들어 아래처럼 Person 이 있고, 그 안에 원시타입의 money 가 있다고해보자. (더 좋은 설계를 위해선 money 를 원시타입이 아닌 Money 라는 클래스로 감싸줘야겠지만, 편의상 원시타입으로 정의했다.)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;비즈니스 로직상 이 세상엔 0원 이하의 금액을 보유한 사람은 없다고 해보자. 그러면 money 값을 추출해내서 체킹하기 위해선 아래처럼 getter 를 사용하여 값을 가져올 것 이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; money &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;money &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;위는 바람직한 코드인가? 그렇지 않다. 위와 같은 코드는 객체의 역할과 책임, 자율성을 무시한 코드다.&lt;/strong&gt; 위 코드는 아래처럼 충분히 Person 도메인 객체 내부에서 역할을 수행할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isInvaliBalance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; money &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isInvalidBalance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;잔고를 체크하는데 Getter 와 Setter 가 필요한가? &lt;strong&gt;객체 상태에 기반한 모든 결정과 행동은 외부가 아닌 객체 내부에서 이루어져야한다. 그것이 진정한 의미의 객체지향 프로그래밍이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Getter 와 Setter  남발하면, 불필요한 객체 내부 구현의 노출로 이어지며 이는 곧 응집도 하락과, 캡슐화의 위반으로 이어진다.&lt;/strong&gt; 객체의 자율성을 보장하고 능동적으로 행동할 수 있도록 설계하도록 훈련해보자.&lt;/p&gt;
&lt;h2 id=&quot;우리-팀은-객체지향-생활체조-원칙을-준수하고-있는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%B0%EB%A6%AC-%ED%8C%80%EC%9D%80-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EC%83%9D%ED%99%9C%EC%B2%B4%EC%A1%B0-%EC%9B%90%EC%B9%99%EC%9D%84-%EC%A4%80%EC%88%98%ED%95%98%EA%B3%A0-%EC%9E%88%EB%8A%94%EA%B0%80&quot; aria-label=&quot;우리 팀은 객체지향 생활체조 원칙을 준수하고 있는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;우리 팀은 객체지향 생활체조 원칙을 준수하고 있는가?&lt;/h2&gt;
&lt;p&gt;우리 팀은 그래서 객체지향 생활체조 원칙을 준수하고 있을까? 진정한 의미의 객체지향 프로그래밍을 위한 충분한 코드를 작성했는가? 필자는 우리 팀이 잘 준수한점과, 아직 개선해야 할점은 아래와 같다고 생각이 든다.&lt;/p&gt;
&lt;h3 id=&quot;잘한점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%98%ED%95%9C%EC%A0%90&quot; aria-label=&quot;잘한점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;잘한점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 객체지향 프로그래밍의 근본이자 핵심인 &quot;역할과 책임&quot; 을 명확히 구분지어 살아 숨쉬는 코드를 만들고자  꾸준히 리팩토링중에 있다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 전략패턴, 싱글톤 디자인패턴을 원활히 활용하여 더 유지.보수에 용이한 코드를 개발하고 있다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 9가지 원칙 중 대부분의 원칙을 준수하며 비즈니스 로직을 작성했다. (else 미사용,  줄여쓰지 않기, ... 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;아직-아쉬운-점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%84%EC%A7%81-%EC%95%84%EC%89%AC%EC%9A%B4-%EC%A0%90&quot; aria-label=&quot;아직 아쉬운 점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;아직 아쉬운 점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 생각보다 지켜지지 않는 생활체조 원칙들이 있다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 원시타입을 VO 로 Wrapping 하지 않은 코드들이 일부 존재하며, 디미터 법칙을 위반한 사례가 간혹 존재한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 특히 Getter 와 Setter 사용을 최대한 지양하고자 했으나, Getter 의 경우 사용을 피할 수 없었다. POJO 프로그래밍을 지향하고 Getter 사용을 최대한 피하기 위해 Lombok 어노테이션을 사용하지 않았으나, 그럼에도 Getter 의 사용을 피하지 못한 코드들이 존재한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;최대한 객체지향 생활체조 원칙을 준수한 코드를 개발하고 있기에 만족스러운 프로덕션 코드를 생산하고 있지만, 아쉬운 점이 넘치고도 넘친다. 머리 밖으로 산발된 생각들을 이번 포스트에서 생각을 정교화했는데, 이번 회고를 계기로 더 객체지향적인 프로그래밍을 이어나가도록 해야겠다. 특히 객체의 자율성을 보장하고 능동적으로 행동할 수 있도록 설계하도록 훈련해보자.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[하모니 팀의 Jacoco 도입기 (feat. 테스트 커버리지)]]></title><description><![CDATA[💡 현재 포스트는 하모니 팀 기술 블로그 에 게시된 글 입니다. 코드 커버리지(Code Coverage)  코드 커버리지에 대한 자세한 이론은 코드 커버리지(Code Coverage…]]></description><link>https://haon.site/test/jacoco/</link><guid isPermaLink="false">https://haon.site/test/jacoco/</guid><pubDate>Tue, 27 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://main--kakaotech-moheng.netlify.app/backend/jacoco/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;코드-커버리지code-coverage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BD%94%EB%93%9C-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80code-coverage&quot; aria-label=&quot;코드 커버리지code coverage permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;코드 커버리지(Code Coverage)&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cc16308e65f4f7d1fdef71a320d94ef5/df438/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.061349693251532%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA6ElEQVR42o2Ru2oCQRSGfds0YiE2PkKqFHZRJCnUysJcBC8pQmxSaGGxTYSFjYmIZJvV1Zm9fJ4ZXSSQiGf4mXNmPv65nBzHSNNEFJ+E1GbY9dQQNjezJL9ZURa5Pw3FLNYKHW6I1A4VrlGbgDhS7NYBehuSJNFlhkmsbe102oxurumVi4zva3RLBZ6KecZ3VR4LVyymE8sZ/oxhJCcfNkeNJm/NFoNKhfeHZ6kbvNbrTPpDXm6r+HPvYHi86dknC8bn9wpP9BOEzNw5y5XP19LH9RY4Hx5Ka8td9ocZZJpg4awhJ/3XlD3gDch8dRgpcAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/cc16308e65f4f7d1fdef71a320d94ef5/a6d36/image.png&quot;
        srcset=&quot;/static/cc16308e65f4f7d1fdef71a320d94ef5/222b7/image.png 163w,
/static/cc16308e65f4f7d1fdef71a320d94ef5/ff46a/image.png 325w,
/static/cc16308e65f4f7d1fdef71a320d94ef5/a6d36/image.png 650w,
/static/cc16308e65f4f7d1fdef71a320d94ef5/e548f/image.png 975w,
/static/cc16308e65f4f7d1fdef71a320d94ef5/3c492/image.png 1300w,
/static/cc16308e65f4f7d1fdef71a320d94ef5/df438/image.png 1556w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;코드 커버리지에 대한 자세한 이론은 &lt;a href=&quot;https://haon.blog/test/code-coverage/&quot;&gt;코드 커버리지(Code Coverage) 에 대하여&lt;/a&gt; 를 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;코드 커버리지란 현재 작성한 테스트 코드가 실제 프로덕션 코드를 얼마나 커버하는지에 대해 표현한 퍼센트 지표이다.&lt;/strong&gt; 즉, 코드 커버리지란 테스트에 의해 실행된 프로덕션 코드의 양을 퍼센트로 표현한 것이라 할 수 있다. 코드 커버러지를 통해 현재 작성된 테스트 갯수가 충분한지, 테스트를 미쳐 놓치고 있는 프로덕션 코드가 존재하는지에 대해 검토할 수 있다.&lt;/p&gt;
&lt;p&gt;코드 커버리지 측정 기준에는 크게 &lt;strong&gt;함수 커버리지, 라인(구문) 커버리지, 결정 커버리지, 조건 커버리지가 존재&lt;/strong&gt;한다. 또한 높은 코드 커버리지는 &lt;strong&gt;더 과감하게 프로덕션 코드를 리팩토링할 수 있게 해주며, 프로덕션 및 요구사항에 대한 이해도를 높일 수 있게 해준다.&lt;/strong&gt; 코드 커버리지에 대한 자세한 이론은 필자가 작성한 &lt;a href=&quot;https://haon.blog/test/code-coverage/&quot;&gt;코드 커버리지(Code Coverage) 에 대하여&lt;/a&gt; 에 정리해 두었으니, 참고하면 좋을 듯 하다 😎&lt;/p&gt;
&lt;h2 id=&quot;jacoco-도입과-코드-커버리지-분석의-필요성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jacoco-%EB%8F%84%EC%9E%85%EA%B3%BC-%EC%BD%94%EB%93%9C-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80-%EB%B6%84%EC%84%9D%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1&quot; aria-label=&quot;jacoco 도입과 코드 커버리지 분석의 필요성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jacoco 도입과 코드 커버리지 분석의 필요성&lt;/h2&gt;
&lt;p&gt;우리 팀은 견고한 애플리케이션과 과감한 리팩토링을 만들기 위해 테스트 방법론을 도입하고 있다. 모든 테스트 코드는 BDD 스타일로 작성중이며, 다양한 히든 케이스를 테스트하고, 믿음직한 프로덕션 코드를 만들기 위해 노력하고 있다. 하지만, 많은 테스트 코드를 작성함에도 불구하고 휴먼 에러, 숨겨진 버그가 발생하고 있다. 이 문제점를 해결하기 위한 방안 중 하나로 코드 커버리지를 측정하기로 했다. 현재 우리 프로젝트는 어떤 부분에서 테스트를 커버하지 못했는지, 이 코드는 과연 신뢰할 수 있는 코드인지에 대한 객관적인 지표가 존재하지 않는다.&lt;/p&gt;
&lt;p&gt;이 떄문에 우리 하모니 팀은 Jacoco 를 도입하여 커버리지를 측정하기로 했다. Jacoco 란 자바 진영에서 프로젝트의 코드 커버리지를 분석하고, 보고서를 생성해주는 코드 버러리지 프레임워크다.  &lt;strong&gt;하모니 팀은 Jacoco 를 도입하여 코드 커버리지를 분석 및 리포트를 생성하고,&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;라인 커버리지(Line Coverage)&lt;/code&gt;&lt;strong&gt;가 일정 수준 이하라면 Gradle 빌드가 실패하도록 설정했다.&lt;/strong&gt; 우리 팀의 Jacoco 도입 과정, 왜 이런 테스트 환경을 구축했는지의 근거를 간단한 실습과 함께 글로 공유하고자 한다 🙂&lt;/p&gt;
&lt;h3 id=&quot;왜-커버리지-기준을-라인-커버리지line-coverage-로-설정했는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80-%EA%B8%B0%EC%A4%80%EC%9D%84-%EB%9D%BC%EC%9D%B8-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80line-coverage-%EB%A1%9C-%EC%84%A4%EC%A0%95%ED%96%88%EB%8A%94%EA%B0%80&quot; aria-label=&quot;왜 커버리지 기준을 라인 커버리지line coverage 로 설정했는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 커버리지 기준을 라인 커버리지(Line Coverage) 로 설정했는가?&lt;/h3&gt;
&lt;p&gt;코드 커버리지 측정 기준은 꽤 다양하다. Jacoco 도입과 동시에 어떤 기준으로 커버리지를 측정할지 많은 고민이 있었다. 고민 끝에, 우리 팀은 라인 커버리지 지표를 기반으로 측정하기로 결론내였다. 조건 커버리지와 결정 커버러지의 경우 조건문을 만족한다면 커버리지 만족 대상에 포함되기 때문에, 만약 조건문이 존재하지 않는 코드라면 커버리지 측정 대상에서 아예 제외되기 때문에 테스트를 수행하지 않는다.&lt;/p&gt;
&lt;p&gt;반면 라인 커버러지(일명, 구문 커버리지) 의 경우 조건식에 따라 실행되지 않는 코드들도 있기 때문에 모든 시나리오에 대해 테스트한다는 보장은 없지만, 전체 코드에 대한 테스크가 커버되었다고는 할 수가 있다. 이 때문에 라인 커버리지를 선택하게 되었다. (대중적으로도 라인 커버리지를 타깃으로 삼는다고 한다.)&lt;/p&gt;
&lt;h3 id=&quot;왜-커버리지가-일정-수준-이하라면-gradle-빌드가-실패하도록-설정했는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80%EA%B0%80-%EC%9D%BC%EC%A0%95-%EC%88%98%EC%A4%80-%EC%9D%B4%ED%95%98%EB%9D%BC%EB%A9%B4-gradle-%EB%B9%8C%EB%93%9C%EA%B0%80-%EC%8B%A4%ED%8C%A8%ED%95%98%EB%8F%84%EB%A1%9D-%EC%84%A4%EC%A0%95%ED%96%88%EB%8A%94%EA%B0%80&quot; aria-label=&quot;왜 커버리지가 일정 수준 이하라면 gradle 빌드가 실패하도록 설정했는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 커버리지가 일정 수준 이하라면 Gradle 빌드가 실패하도록 설정했는가?&lt;/h3&gt;
&lt;p&gt;커버리지는 그 자체만으로 테스트가 얼만큼 수행되었는지의 객관적이자 정량적 지표를 뜻한다. 커버리지가 낮다고 해서 무조건적으로 빈약한 애플리케이션이라 단정지을 순 없지만, 그럼에도 &lt;strong&gt;정말 낮은 커버리지는 빈약한 애플리케이션일 가능성이 충분하다는 것은 사실이다.&lt;/strong&gt; 특정 프로덕션 코드에 대해 테스트 자체가 단 1번도 실행되지 않았다면 커버리지에서 제외되기 떄문이다. &lt;strong&gt;빈약한 애플리케이션일 가능성이 높은 코드를 함부로 배포하기란 다소 무모한 짓이기에, 일정 수준보다 낮다면 빌드 자체가 실패하여 배포되지 않도록 막아놓았다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;현재 우리 팀은 여유있게 커버러지가 70% 이상일 때만 빌드가 성공하도록 했다. 데모데이 기간이 얼마 남지 않았기에, 정해진 기간내에 적당량의 테스트를 작성하는 것이 올바르다고 판단했기 때문이다. 1차 데모데이가 끝난 뒤에는 점차 커버러지 성공 기준을 75% 에서 90% 대로 올리는 것을 계획중이다.&lt;/p&gt;
&lt;h2 id=&quot;jacoco-도입을-위한-환경-구축하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jacoco-%EB%8F%84%EC%9E%85%EC%9D%84-%EC%9C%84%ED%95%9C-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&quot; aria-label=&quot;jacoco 도입을 위한 환경 구축하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jacoco 도입을 위한 환경 구축하기&lt;/h2&gt;
&lt;p&gt;우리 팀은 어떤 방식으로 Jacoco 를 도입했으며, 어떻게 커버러지가 일정 수준 이상일때만 Gradle 빌드를 성공하도록 구축했을까? 이를 간단한 실습으로 구현해보겠다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;실제 하모니 팀 프로젝트는 Jacoco 가 생성한 XML, CSV 파일을 이용하여 소나큐브가 정적 분석 을 시도하도록 설계되었지만, 소나큐브 연동과 관련한 내용은 이번 포스트 주제에서 다소 벗어나는 내용이므로 제외한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;buildgradle-전체-코드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#buildgradle-%EC%A0%84%EC%B2%B4-%EC%BD%94%EB%93%9C&quot; aria-label=&quot;buildgradle 전체 코드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;build.gradle 전체 코드&lt;/h3&gt;
&lt;p&gt;아래는 gradle 에 jacoco 관련 의존성 코드를 추가한 전체 코드다. 하나씩 살펴보도록 하자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;plugins &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	id &lt;span class=&quot;token char&quot;&gt;&apos;jacoco&apos;&lt;/span&gt; 
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

jacoco &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	toolVersion &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.8.11&quot;&lt;/span&gt; 
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

jacocoTestReport &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	reports &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		xml&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;required&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
		csv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;required&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		html&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;required&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

		xml&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destination &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;project&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buildDirectory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;jacoco/index.xml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; as &lt;span class=&quot;token class-name&quot;&gt;File&lt;/span&gt;
		csv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destination &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;project&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buildDirectory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;jacoco/index.csv&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; as &lt;span class=&quot;token class-name&quot;&gt;File&lt;/span&gt;
		html&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destination &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;project&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buildDirectory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;jacoco/index.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; as &lt;span class=&quot;token class-name&quot;&gt;File&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	afterEvaluate &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		classDirectories&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;token function&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;classDirectories&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;files&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;collect &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token function&quot;&gt;fileTree&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dir&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; it&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; excludes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
							&apos;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/*Exception*&apos;,
							&apos;**/&lt;/span&gt;dto&lt;span class=&quot;token comment&quot;&gt;/**&apos;,
							&apos;**/&lt;/span&gt;infrastructure&lt;span class=&quot;token comment&quot;&gt;/**&apos;
                            // ...
					])
				})
		)
	}
	finalizedBy(jacocoTestCoverageVerification)
}

jacocoTestCoverageVerification {
	violationRules {
		rule {
			enabled = true

			limit {
				counter = &quot;LINE&quot;
				value = &quot;COVEREDRATIO&quot;
				minimum = 0.70
			}

			excludes = [
					&apos;*.*Exception&apos;,
					&apos;*.dto.*&apos;,
					&apos;*.infrastructure.*&apos;
                    // ...
			]
		}
	}
}

test {
	finalizedBy &apos;jacocoTestReport&apos;
    // ...
}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;1-jacoco-플러그인-로드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-jacoco-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-%EB%A1%9C%EB%93%9C&quot; aria-label=&quot;1 jacoco 플러그인 로드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Jacoco 플러그인 로드&lt;/h3&gt;
&lt;p&gt;우선 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;build.gradle&lt;/code&gt; 에서 Jacoco 플러그인을 가져온다. 우리 팀의 경우 Jacoco 버전을 &lt;code class=&quot;language-text&quot;&gt;0.8.11&lt;/code&gt; 을 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;plugins &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	id &lt;span class=&quot;token char&quot;&gt;&apos;jacoco&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

jacoco &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	toolVersion &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.8.11&quot;&lt;/span&gt; 
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-xml-csv-html-파일로-보고서-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-xml-csv-html-%ED%8C%8C%EC%9D%BC%EB%A1%9C-%EB%B3%B4%EA%B3%A0%EC%84%9C-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;2 xml csv html 파일로 보고서 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. XML, CSV, HTML 파일로 보고서 생성&lt;/h3&gt;
&lt;p&gt;다음으로 Jacoco 가 커버리지 측정 보고서 파일을 생성할 때, 사람이 읽기 편한 형태인 XML, CSV, HTML 형식으로 생성하도록 설정해야한다. XML과 CSV파일은 소나큐브등과 연동할 때 주로 사용되며, HTML 파일은 사람이 직접 커버리지를 확인할 때 사용된다.&lt;/p&gt;
&lt;p&gt;Jacoco 는 기본적으로 코드 커버리지 결과 보고서를 바이너리 형식 (확장자 &lt;code class=&quot;language-text&quot;&gt;.exec&lt;/code&gt;) 으로 생성한다. 하지만 바이너리 파일은 일반적으로 사람이 읽을 수 없는 형태이기 때문에, 바이너리 형태의 보고서를 사람이 읽기 좋은 형태로 출력해주는 기능을 제공한다. 그 기능을 바로 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;jacocoTestReport&lt;/code&gt; 테스크에서 제공해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;jacocoTestReport &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	reports &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		xml&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;required&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
		csv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;required&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		html&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;required&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

		xml&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destination &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;project&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buildDirectory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;jacoco/index.xml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; as &lt;span class=&quot;token class-name&quot;&gt;File&lt;/span&gt;
		csv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destination &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;project&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buildDirectory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;jacoco/index.csv&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; as &lt;span class=&quot;token class-name&quot;&gt;File&lt;/span&gt;
		html&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destination &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;project&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buildDirectory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;jacoco/index.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; as &lt;span class=&quot;token class-name&quot;&gt;File&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 &lt;code class=&quot;language-text&quot;&gt;jacocoTestReport&lt;/code&gt; 테스크를 설정해준다면 위 보고서 파일들을 빌드 디렉토리 경로에 생성해준다. 즉, Jacoco 가 생성한 HTML 보고서의 경우 &lt;code class=&quot;language-text&quot;&gt;/build/jacoco/index.html&lt;/code&gt; 에 생성될 것이다.&lt;/p&gt;
&lt;p&gt;실제로 아래와 같이 jacocoTestReport` 테스크를 실행시킨 후 생성된 보고서 파일들을 조회해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;/gradlew jacocoTestReport&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;결과적으로 아래와 같은 빌드 디렉토리에 우리가 원하는 보고서 파일들이 생성된 모습을 확인할 수 있을것이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bab3960944ea96488d7f81c3f572532b/fcda8/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 87.11656441717791%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAABu0lEQVR42qWUS0+DUBSE+0NMTFHLq7xaoLS8pcVHBbRaX0uXxl13/vnxnIvGaDQBXZzcGyBfZu7MZeDZOnxnjHzhIQsmKEObVguupcGzNbH2mUE6PUAxU1AFCm7SMR5XNs5DBYG5j9AeIplIvWYQTw7Ak/oySgKvFxrqREdD8HWk0Uft+67zCfRGKAm6nqu4JNj22MQmM5BMD/8GzGYqTkhhHWrYFpZQNzOHZFv6n8JzUljHOiqaMpBxMle+2E46K/QVAbyKx7inYNjyLSl9LB1hO7CGWNjtRN2AsrDcRDo2uUHnaOAqG+Oa9hexJuC858ncw1+hn5ZdGSuC1mELbFJdhMNztlBEQJc0FTVAAJ0Ols8CFZvEwN2qVXNK5+cb+5hbEvWyp2Vx4NMj0cWKkr6glDkYhvJ+6Y8QO1L3UCL+2B1hyeWmCrFCDoct3y0tsq0idKTutWmhQ+QMLHw0dFbbwhQV4psTOz17+KEy92ScRgYVu70xrXWt840ZfH+QksqVvUeWTTy8W+Z97h79muyPQFYwpz9MVXjYvTzRT0IWFWF1HE7vq/cBrJc+XnfPaGKVit2WmzvYFfgGdP0j/MBqB+UAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/bab3960944ea96488d7f81c3f572532b/fcda8/image-1.png&quot;
        srcset=&quot;/static/bab3960944ea96488d7f81c3f572532b/222b7/image-1.png 163w,
/static/bab3960944ea96488d7f81c3f572532b/ff46a/image-1.png 325w,
/static/bab3960944ea96488d7f81c3f572532b/fcda8/image-1.png 590w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;3-테스트-커버리지-측정-대상-일부-제외시키기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80-%EC%B8%A1%EC%A0%95-%EB%8C%80%EC%83%81-%EC%9D%BC%EB%B6%80-%EC%A0%9C%EC%99%B8%EC%8B%9C%ED%82%A4%EA%B8%B0&quot; aria-label=&quot;3 테스트 커버리지 측정 대상 일부 제외시키기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 테스트 커버리지 측정 대상 일부 제외시키기&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;단순히 계층간 데이터를 전송하는 역할을 하는 DTO, Exception 클래스, Mocking 으로 대신한 외부 API 등 테스트가 불필요한 (또는 테스트가 불가능한) 대상들이 존재할 것이다.&lt;/strong&gt; 이들까지 모두 커버리지 측정 타깃으로 삼는다면 정작 커버리지 측정 대상에 집중하지 못하게 될 것이며, 불필요한 대상들로 인해 커버리지 결과가 낮게 측정될 것이다. 이들은 아래와 같이 커버리지 측정 대상에서 제외시킬 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;afterEvaluate &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		classDirectories&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;token function&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;classDirectories&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;files&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;collect &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token function&quot;&gt;fileTree&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dir&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; it&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; excludes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
							&apos;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/*Exception*&apos;,
							&apos;**/&lt;/span&gt;dto&lt;span class=&quot;token comment&quot;&gt;/**&apos;,
							&apos;**/&lt;/span&gt;infrastructure&lt;span class=&quot;token comment&quot;&gt;/**&apos;
                            // ...
					])
				})
		)
	}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;afterEvaluate&lt;/code&gt; 에 현재 프로젝트의 커버리지를 측정(평가) 한 뒤 수행할 행위를 기제할 수 있다. 우리는 &lt;code class=&quot;language-text&quot;&gt;excludes&lt;/code&gt; 에 제외시킬 클래스를 포함했다.&lt;/p&gt;
&lt;h3 id=&quot;4-커버리지가-70-이상일-때-빌드가-성공하도록-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80%EA%B0%80-70-%EC%9D%B4%EC%83%81%EC%9D%BC-%EB%95%8C-%EB%B9%8C%EB%93%9C%EA%B0%80-%EC%84%B1%EA%B3%B5%ED%95%98%EB%8F%84%EB%A1%9D-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;4 커버리지가 70 이상일 때 빌드가 성공하도록 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 커버리지가 70% 이상일 때 빌드가 성공하도록 설정&lt;/h3&gt;
&lt;p&gt;또한 Jacoco 는 &lt;code class=&quot;language-text&quot;&gt;JacocoTestCoverageVerification&lt;/code&gt; 이라는 태스크를 지원한다. &lt;strong&gt;이 태스크에서 규칙을 설정하여, 프로젝트의 코드 커버리지가 우리가 설정한 규칙을 통과하지 않으면 빌드가 실패하도록 만들 수 있다.&lt;/strong&gt; 규칙은 &lt;code class=&quot;language-text&quot;&gt;rule&lt;/code&gt; 이라는 블럭 내부에서 정의해야한다. 우리 하모니 팀은 아래와 같은 설정을 통해 라인 커버리지가 70% 미만일 경우 빌드가 실패하도록 규칙을 만들었다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;jacocoTestCoverageVerification &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	violationRules &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		rule &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			enabled &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 규칙을 활성화한다. (false 일 경우 비활성화)&lt;/span&gt;

			limit &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;LINE&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 라인 커버리지 기준임을 명시&lt;/span&gt;
				value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;COVEREDRATIO&quot;&lt;/span&gt;
				minimum &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.70&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 최소 70% 이상을 만족해야 함&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

			excludes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
					&apos;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;*&lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt;&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&apos;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;*&apos;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&apos;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infrastructure&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;*&apos;
                    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;우리 팀은 라인 커버리지를 지표로 하여 측정하고 있지만, 만약 다른 커버리지 지표를 사용하고 싶다면 아래와 같은 옵션중에 골라서 사용하면 될 것이다. (옵션 미지정시 기본값은 &lt;code class=&quot;language-text&quot;&gt;INSTRUCTION&lt;/code&gt;)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;LINE&lt;/code&gt; : 빈 줄을 제외한 실제 코드의 라인 수&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;BRANCH&lt;/code&gt; : 조건문등의 분기 수&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;CLASS&lt;/code&gt; : 클래스 수&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;METHOD&lt;/code&gt; : 메소드 수&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;INSTRUCTION&lt;/code&gt; : 자바 바이트코드 명령 수&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;COMPLEXITY&lt;/code&gt; : 복잡도&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;5-테스트-실행-후-커버리지-리포트-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%A4%ED%96%89-%ED%9B%84-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80-%EB%A6%AC%ED%8F%AC%ED%8A%B8-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;5 테스트 실행 후 커버리지 리포트 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. 테스트 실행 후 커버리지 리포트 생성&lt;/h3&gt;
&lt;p&gt;test 테스크가 끝난 뒤에, 즉 모든 테스트가 실행된 뒤에 곧 바로 &lt;code class=&quot;language-text&quot;&gt;jacocoTestReport&lt;/code&gt; 가 실행되도록 설정할 수 있다. 이 설정을 통해 테스트가 완료되면 항상 Jacoco 분석 리포트가 자동으로 생성된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;test &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	finalizedBy &apos;jacocoTestReport&apos;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;커버리지-리포트-확인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80-%EB%A6%AC%ED%8F%AC%ED%8A%B8-%ED%99%95%EC%9D%B8&quot; aria-label=&quot;커버리지 리포트 확인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커버리지 리포트 확인&lt;/h2&gt;
&lt;p&gt;이렇게까지 Jacoco 환경 구축을 마쳤다면, 매 테스트 실행 결과로 아래와 같은 HTML 형식의 리포트를 확인할 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/769bbed7397e38555dcdb1f5fa2df0de/09ede/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 77.30061349693251%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC2UlEQVR42nWUWZPaOBSF+f8/ZZaHSaomDzM1mYd0Kt00mIaGXqBpW7K14N3GsjHgkyvRyaRSNS5uSWVLn8+5OmbENy949LbwHyU2Cw7NE3SdgTEt8lhj+nAFmWjEuxiv2y3UTrp5kReQQkIpBSmlKzsfJVzhYZrjwStxP87xNK8QsR5aAHm5xwfxDp+yK3R9h10o8bybwW8D2EsT4HA4oG1blGWJ0+mEUcw41l6IzVzSKPDsRaRUIGYEjhWudp+QtinaroOIIjym9zC9wXAewDl3sI6eVVXlxlHCGIE4Xu4E1rOQ5hFeVwr7rEPWJrjPFk6NVRLS2l0tcDwfMQxnKK2oNcY9a5rmAtQ+x/JGYDXWWFLpsEZdNGjrHkVX4KP6CNYE4CWHCEJM9Bcoo91LwjB0Ci3UWn4DRri7zrAYl/A+52DbhpYO7lcdKrwL/8Dv4W+YqAkUk/hX/YO/1V8wJwM/eHXA/X6PNE3dfLQLGB5vIzzcCjx5Cq3piXV+A5b4lf2C9+I90joF9xnmqYeb5Bqh4dj6L+ja7mcgJ1hEliW2ywSxbJCoEn17QtmX+BD9CWmEW8xp7SydYlOtL5aji2XbvzzPL0AdSHhfKsxuGkw/1wjWNfalAU5A1mWY53O32faJcUZWW3fCwzBAa02OjANala6HMacokNX1PMHqVoNvSpj9wUHSNsOm3HwH2phYkM2bHYuicPct0I4Xy4z6N9UEjbGa7BA8F7ThCNvGxCTYVlsHtJuCwCf70gHP5zOEEA5k1dlTtmoJKLGcFlQ1Vl6Fp2VHn9TRQTJSKBrxXWHAAuhWO5hV+C029mW2Lj1kCvNJg8W0w92YorBpkaW9g9jYJBTuHy3b0//ZsnmDvZ1yhOUkx2pWY3Fb4nlRodn3OFIbq/4/oFXg+/7/9tDadkAV0D/KzQHe+ITJ9ZGqx/T6gDIf0FF4rUoHNA0YfXo/Am1Uvtk15mL5K1JpZerZOk9SAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/769bbed7397e38555dcdb1f5fa2df0de/a6d36/image-2.png&quot;
        srcset=&quot;/static/769bbed7397e38555dcdb1f5fa2df0de/222b7/image-2.png 163w,
/static/769bbed7397e38555dcdb1f5fa2df0de/ff46a/image-2.png 325w,
/static/769bbed7397e38555dcdb1f5fa2df0de/a6d36/image-2.png 650w,
/static/769bbed7397e38555dcdb1f5fa2df0de/e548f/image-2.png 975w,
/static/769bbed7397e38555dcdb1f5fa2df0de/3c492/image-2.png 1300w,
/static/769bbed7397e38555dcdb1f5fa2df0de/09ede/image-2.png 1710w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;효율적인 테스트를 작성하고, 버그를 최소화하고, 견고한 애플리케이션을 만들기 위한 여정은 쉽지 않다 😅 앞으로도 코드 커버리지를 통해 우리 하모니 팀의 서비스가 어떻게 더 발전해나갈 수 있을지 고민해봐야곘다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[코드 커버리지(Code Coverage) 에 대하여]]></title><description><![CDATA[코드 커버리지 (Code Coverage) In computer science, test coverage is a measure used to describe the degree to which the source code of a program is…]]></description><link>https://haon.site/test/code-coverage/</link><guid isPermaLink="false">https://haon.site/test/code-coverage/</guid><pubDate>Sat, 24 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;코드-커버리지-code-coverage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BD%94%EB%93%9C-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80-code-coverage&quot; aria-label=&quot;코드 커버리지 code coverage permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;코드 커버리지 (Code Coverage)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;In computer science, test coverage is a measure used to describe the degree to which the source code of a program is executed when a particular test suite runs. A program with high test coverage, measured as a percentage, has had more of its source code executed during testing, which suggests it has a lower chance of containing undetected software bugs compared to a program with low test coverage - wikipedia&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;코드 커버리지란 현재 작성한 테스트 코드가 실제 프로덕션 코드를 얼마나 커버하는지에 대해 표현한 퍼센트 지표&lt;/strong&gt;이다. 즉, 코드 커버리지란 테스트에 의해 실행된 프로덕션 코드의 양을 퍼센트로 표현한 것이라 할 수 있다. 코드 커버러지를 통해 현재 작성된 테스트 갯수가 충분한지, 테스트를 미쳐 놓치고 있는 프로덕션 코드가 존재하는지에 대해 검토할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;화이트-박스-테스트-vs-블랙박스-테스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%99%94%EC%9D%B4%ED%8A%B8-%EB%B0%95%EC%8A%A4-%ED%85%8C%EC%8A%A4%ED%8A%B8-vs-%EB%B8%94%EB%9E%99%EB%B0%95%EC%8A%A4-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;화이트 박스 테스트 vs 블랙박스 테스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;화이트 박스 테스트 vs 블랙박스 테스트&lt;/h3&gt;
&lt;p&gt;테스트 기법에는 크게 2가지로 화이트 박스, 블랙박스 테스트 기법이 존재한다. 코드 커버리지는 이 중 어떤 방식에서 유래된 것일까?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;코드 커버리지는 테스트에 의해 실행되는 소스 코드의 양을 퍼센트로 표현한 것인데, 이는 화이트 박스 테스트라는 기법에 기반하고 있다.&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;화이트 박스 테스트&lt;/code&gt;란 프로덕션 코드의 모든 내부 경로(코드를) 테스트하여 모듈 내부의 작동을 확인하는 기법을 뜻한다. 반면 &lt;code class=&quot;language-text&quot;&gt;블랙 박스 테스트&lt;/code&gt; 란 사용자 입장에서 구현된 기능이 완전하게 동작하는지를 확인하는 테스트 기법이다. 프로덕션 코드 및 세부 동작을 알 필요가 없으며, 단순히 입력 값에 따라 올바른 기댓값이 출력되는지에 대해 확인하는 테스트다.&lt;/p&gt;
&lt;h2 id=&quot;코드-커버리지-측정-기준&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BD%94%EB%93%9C-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80-%EC%B8%A1%EC%A0%95-%EA%B8%B0%EC%A4%80&quot; aria-label=&quot;코드 커버리지 측정 기준 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;코드 커버리지 측정 기준&lt;/h2&gt;
&lt;p&gt;코드 커버리지를 측정하는 기준에는 여러 방식이 있다. 크게 &lt;strong&gt;함수 커버리지, 라인(구문) 커버리지, 결정 커버리지, 조건 커버리지가 존재한다.&lt;/strong&gt; 각 커버리지 측정 기준에 대해 학습해보도록 하자.&lt;/p&gt;
&lt;h3 id=&quot;함수-커버리지-function-coverage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%A8%EC%88%98-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80-function-coverage&quot; aria-label=&quot;함수 커버리지 function coverage permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;함수 커버리지 (Function Coverage)&lt;/h3&gt;
&lt;p&gt;어떤 함수가 최소 1번 이상 호출되었는지 여부를 기준으로 커버리지를 측정한다. 함수 내부에 작성된 모든 코드가 실행될 필요가 없으며, 해당 함수 그 자체가 호출되었다면 커버리지에 포함시킨다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 함수 커버리지 = (실행된 함수의 수 / 전체 함수의 수 ) * 100 %&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;func1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...    &lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;func2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...    &lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 위 코드에서 함수 &lt;code class=&quot;language-text&quot;&gt;func1()&lt;/code&gt; 이 테스트 코드로 인해 호출되었고, &lt;code class=&quot;language-text&quot;&gt;func2()&lt;/code&gt; 는 호출되지 않았다면 커버리지 계산 결과가 어떻게 될까? 2게의 함수 중에 1개만 호출된 상태이므로 테스트 커버리지는 50% 가 될 것이다.&lt;/p&gt;
&lt;h3 id=&quot;라인-커버리지line-coverage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9D%BC%EC%9D%B8-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80line-coverage&quot; aria-label=&quot;라인 커버리지line coverage permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;라인 커버리지(Line Coverage)&lt;/h3&gt;
&lt;p&gt;라인 커버리지는 &lt;code class=&quot;language-text&quot;&gt;구문 커버리지 (Statement Coverage)&lt;/code&gt; 라고도 불린다. &lt;strong&gt;프로덕션 코드내의 전체 구문 중에서 몇 줄의 구문이 실행되었는지를 기준으로 측정&lt;/strong&gt;한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 구문 커버리지 = (실행된 구문의 수 / 전체 구문의 수) * 100 %&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calculate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;char&lt;/span&gt; operator&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; num1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; num2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;함수를 동작합니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)    &lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;operator &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;+&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)   &lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num1 &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; num2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (3)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; 
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;operator &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;-&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (4)&lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num1 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; num2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (5)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;함수를 종료합니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (6)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 위 코드에서 연산자를 &quot;+&quot; 으로 실행했다면 커버리지는 어떻게 될까? 테스트 실행 결과, &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; ~ &lt;code class=&quot;language-text&quot;&gt;(6)&lt;/code&gt; 구문 중에 &lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; 는 실행되지 않을 것이다. 즉, 6개의 구문중에 4개의 구문이 실행되었으므로 커버리지는 약 66.6% 으로 측정 될 것이다.&lt;/p&gt;
&lt;h3 id=&quot;결정-커버리지-decision-coverage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EC%A0%95-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80-decision-coverage&quot; aria-label=&quot;결정 커버리지 decision coverage permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결정 커버리지 (Decision Coverage)&lt;/h3&gt;
&lt;p&gt;결정 커버리지는 &lt;code class=&quot;language-text&quot;&gt;브랜치 커버리지(Branch Coverage)&lt;/code&gt; 라고도 불린다. (개인적으로 커버리지 중에 다소 햇갈려했던 개념이다.) 프로덕션 코드에 조건문이 있다면, &lt;strong&gt;조건문의 전체 조건식 최종 결과(출력값)가 True 인 케이스와 False 인 케이스 2가지가 최소 1번 이상 실행되면 커버리지에 포함&lt;/strong&gt;된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;조건문 내부 코드를 실행합니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드에서 함수 파리미터로 &lt;code class=&quot;language-text&quot;&gt;func(100, 100)&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;func(-100, -100)&lt;/code&gt; 을 넣어 실행한 테스트 케이스 2개가 있다면, 전체 조건식 최종 결과(출력값)가 각각 true, false 가 도출된다. 즉, 전체 조건식 결과가 true, false 가 한번씩 출력되었으므로 커버리지에 포함시킨다.&lt;/p&gt;
&lt;h3 id=&quot;조건-커버리지-condition-coverage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A1%B0%EA%B1%B4-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80-condition-coverage&quot; aria-label=&quot;조건 커버리지 condition coverage permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;조건 커버리지 (Condition Coverage)&lt;/h3&gt;
&lt;p&gt;앞서 학습했던 결정 커버리지는 전체 조건식을 기준으로 판단했다면, 조건 커버리지는 개별(각각의 세부) 조건식을 기준으로 판단하여 커버리지를 측정한다. &lt;strong&gt;모든 개별 조건식의 실행 결과에 True 인 케이스와 False 인 케이스가 포함된다면 커버리지에 포함된다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;조건문 내부 코드를 실행합니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드에서 함수 파라미터로 &lt;code class=&quot;language-text&quot;&gt;func(100, -100)&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;func(-100, 100)&lt;/code&gt;을 넣어 테스트 케이스를 각각 실행되었다면 어떻게 될까? 우선 &lt;code class=&quot;language-text&quot;&gt;func(100, -100)&lt;/code&gt; 을 부여한 테스트의 경우 개별 조건식 결과가 각각 True, False 가 된다. 따라서 &lt;code class=&quot;language-text&quot;&gt;func(100, -100)&lt;/code&gt; 를 넣어 실행한 테스트 케이스의 경우 커버리지에 포함된다. 한편 &lt;code class=&quot;language-text&quot;&gt;func(-100, 100)&lt;/code&gt; 의 개별 조건식 결과는 False, True 가 되므로 이 테스트 케이스 또한 커버리지에 포함된다.&lt;/p&gt;
&lt;h2 id=&quot;진정한-의미의-코드-커버리지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%84%EC%A0%95%ED%95%9C-%EC%9D%98%EB%AF%B8%EC%9D%98-%EC%BD%94%EB%93%9C-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80&quot; aria-label=&quot;진정한 의미의 코드 커버리지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;진정한 의미의 코드 커버리지&lt;/h2&gt;
&lt;p&gt;코드 커비리지는 왜 중요할까? 단순히 프로덕션 코드가 얼만큼 커버되었는가를 측정하는 행위가 불과할까?&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/796baa1fafa151bad6806c4a2c0d76d2/3c492/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.71165644171779%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAACJElEQVR42oWTbU/TUBSA+csm/gD9qF8wUfQLxJhI8AMSiGAwYShExLgo40Xn1uwF2NaVrVu70rd7b/vYrgVHMudJTu65Nz3Peek5c1IplJTI0E3UQ/oOSgnsagWjsIOnd4gBIQQy+U7kqlK/XNP3VEzTZC41YuGi9GNUr0JkVlF2D3N9DfvDNpX79zBWXo8d4ihimsRxPD4ty8qAyUsC9camqH9EHTymXGty2mwyPDzg8NUK652QZc1ipT7is+HjqQwexdOAWfgMWCsgi89otHuUz05YbfssnEs+6R4/LcGPgeBNw+HJL4uGI3PoVGD2GI06+F/mk3vIZlfyUhslPVbErSaR3iIOskrOhiGPTodc+eoWcReYZznGNnapVb4yX/HxZebg1Gp0t97hXV5y08mdJOvlujMDOJH++5bHdssd28HA4mprk/7bVUztiJCs1EGoeFq2cEQ0C5idaeRizx/bqqvRq5bY/3bC93KBam+X/qhOx66xlLSk7WYBhrOAaxfX7HezDOX5HsHxMn1DZ6+8hOFoSOWy3ujysDRM+hn8H3g0CFms2uDqqFEX2SoSGSVMr0W6DKlstT0eHA/QvRkZxhPgFxWbfSOYHII7Uh2FFPsBnpzRw0loOhLpvG0k5RueQCSb4ghFyQxY1Gw0W9APFJ28h/8ETmZ0nUTfuHB5/ttmIdf0R6RzeBN8+qZMkWytMjtILsMwyVBGtxXEuUbRX+Af+ccpbENbZPwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/796baa1fafa151bad6806c4a2c0d76d2/a6d36/image.png&quot;
        srcset=&quot;/static/796baa1fafa151bad6806c4a2c0d76d2/222b7/image.png 163w,
/static/796baa1fafa151bad6806c4a2c0d76d2/ff46a/image.png 325w,
/static/796baa1fafa151bad6806c4a2c0d76d2/a6d36/image.png 650w,
/static/796baa1fafa151bad6806c4a2c0d76d2/e548f/image.png 975w,
/static/796baa1fafa151bad6806c4a2c0d76d2/3c492/image.png 1300w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;코드 커버리지의 중요성은 테스트 코드의 중요성과 일맥상통한다.&lt;/strong&gt; 테스트의 장점이자 진정한 의미의 TDD, BDD 방법론을 적용했을 떄의 이점은 &lt;a href=&quot;https://haon.blog/test/tdd-bdd/&quot;&gt;TDD(테스트 주도 개발) 와 BDD(행위 주도 개발)
&lt;/a&gt; 에서 다룬적이 있다.&lt;/p&gt;
&lt;p&gt;우선, &lt;strong&gt;높은 커버리지는 이전보다 더 과감하게 코드를 리팩토링할 수 있게 해준다.&lt;/strong&gt; 리팩토링시 낮은 커버리지가 존재하는 코드는 이전보다 변화에 두려운 레거시 코드가 될 것이다. 비슷한 이유로 높은 커버리지는 비교적 신뢰도 있는 프로덕션 코드임을 알려주는 객관적인 지표이기에, 견고한 애플리케이션임을 믿고 더 자신있게 서비스를 배포할 수 있게 해준다.&lt;/p&gt;
&lt;p&gt;또한 &lt;strong&gt;휴먼 에러를 최소화할 수 있다.&lt;/strong&gt; 테스트에서 놓칠 수 있는 부분들을 코드 버러지지를 통해 직접 확인핣 수 있고, 그에 따라 부족한 테스트를 보강할 수 있다. 비슷한 이유로, &lt;strong&gt;낮은 커버리지를 보완하기 위해 테스트를 작성하는 과정속에서 프로덕션에 대한 이해도를 높일 수 있다.&lt;/strong&gt; 커버리지가 미흡한 부분을 직접 찾아내고, 해당 프로덕션에 대한 시나리오를 테스트로 작성함으로써 모호한 이해도를 갖고 개발한 코드를 보완하며, 숨겨진 엣지 케이스 또한 찾아낼 수 있다.&lt;/p&gt;
&lt;p&gt;이 떄문에 많은 서비스 기업에서는 테스트의 중요성을 인지하고 높은 커버리지를 최대한 유지 및 지속적으로 상승시키며 개발하고 있다고 한다.&lt;/p&gt;
&lt;h3 id=&quot;높은-커버리지를-맹목적으로-신뢰하지-말-것&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%86%92%EC%9D%80-%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80%EB%A5%BC-%EB%A7%B9%EB%AA%A9%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%8B%A0%EB%A2%B0%ED%95%98%EC%A7%80-%EB%A7%90-%EA%B2%83&quot; aria-label=&quot;높은 커버리지를 맹목적으로 신뢰하지 말 것 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;높은 커버리지를 맹목적으로 신뢰하지 말 것&lt;/h3&gt;
&lt;p&gt;그렇다고 해서, 높은 커버리지는 절대적으로 견고한 애플리케이션임을 보장하진 않는다. 예를들어 아래와 같이 계산을 위한 코드를 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calculate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;char&lt;/span&gt; operator&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; num1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; num2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;함수를 동작합니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)    &lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;operator &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;+&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)   &lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num1 &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; num2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (3)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; 
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;operator &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;-&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (4)&lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num1 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; num2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (5)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;함수를 종료합니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (6)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;덧셈 연산에 대한 테스트를 커버하기 위해 아래와 같은 테스트를 작성했다고 해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;연산자가 + 인 경우 덧셈 연산이 수행된다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;assertDoesNotThrow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; calculator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;calculate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token char&quot;&gt;&apos;+&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 테스트는 문제없이 성공하게 된다. 하지만, 위에 작성한 테스트는 정말 바람직할까? 만약 연산자를 마이너스 &apos;-&apos; 연산자로 바꾼다면? 이 또한 테스트가 통과한다.&lt;/p&gt;
&lt;p&gt;따라서 높은 커버리지가 절대적으로 견고한 애플리케이션임을 보장하지 않는다. &lt;strong&gt;진정하게 견고한 애플리케이션이 되기 위해선 테스트 코드 자체에서 불필요한 테스트가 없도록 해야한다.&lt;/strong&gt; 높은 커버리지를 유지하기 위해서 불필요한 코드에 대해 테스크 코드를 작성하는 행위는 그저 자기만족에 불과하다. 따라서 커버리지가 높다고 한들, 해당 프로덕션이 절대적으로 완벽함을 믿지 말고, 정말 필요한 테스트를 작성하여 높은 커버리지를 달성하는 것이야 말로 진정한 테스트 커버리지가 아닐까 싶다.&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;다음 포스트에선 우리 하모니 팀의 코드 커버리지 측정 및 JACOCO, 소나큐브 도입기에 대해 다루어보고자 한다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[격리된 테스트(Isolated Test) 구축과 빌드 최적화 여정 - 실전편]]></title><description><![CDATA[💡 현재 포스트는 하모니 팀 기술 블로그 에 게시된 글 입니다. 문제 상황  우리 팀은 완벽한 테스트 격리 환경을 구축하는 과정속에서, 초기에 빌드가 약 2…]]></description><link>https://haon.site/test/isolated-active/</link><guid isPermaLink="false">https://haon.site/test/isolated-active/</guid><pubDate>Thu, 22 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://main--kakaotech-moheng.netlify.app/backend/isolated-active/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;문제-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%A0%9C-%EC%83%81%ED%99%A9&quot; aria-label=&quot;문제 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문제 상황&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2311e0d7653b0f844b7e66dc46286449/691b3/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.607361963190186%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAABk0lEQVR42kWQTU8TQRyH90N4MEWgNaDdV/aNbkuzLQSBEiRrUjUaLnwXYyRRikgLyIkES7fWvtAY7yIJcMC7esMT3prgsXmYrBEmefKb/DLzTP4jlTffs1HZpiJyc3uHypbIrZ2IsujPz3/zf52dfefFyxVKb99RWiuzWtrgzeqaYF3s13m18hpp+XkRb9zDcScE8ySTGrJikJR1hhL3+Hp4dC1sN5s8DQqMOQ6e9wDLmSWp6CiajayaxO4kkJ4Vi8yks7hTD9GmH6PmFjDygqmAMdvj+OSUfr8fCZuNFjk/gzmTw5gMkP0F5Owcqj+P4RdQNQvJTefw7RTBoyfk5xZxJybR7DS6kxHCVCTs9Xr8vbwkDBskFIV8MEtQXGK6sEgqK85bKUw3g6rbSLJqocZHGReXDdMRr5gMDt39x/AIh99uRt7d3eNWbID7ro4uxtR0K/qagcEEw/ERbsfiSN3PX+h0urRaHZqCVvuAzkE3oi36i4s/18IfP3+xV60RfvxEtVanuh+yLzKsN6iL7kOtzhWgJhmZ0CXpLgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/2311e0d7653b0f844b7e66dc46286449/a6d36/image-1.png&quot;
        srcset=&quot;/static/2311e0d7653b0f844b7e66dc46286449/222b7/image-1.png 163w,
/static/2311e0d7653b0f844b7e66dc46286449/ff46a/image-1.png 325w,
/static/2311e0d7653b0f844b7e66dc46286449/a6d36/image-1.png 650w,
/static/2311e0d7653b0f844b7e66dc46286449/e548f/image-1.png 975w,
/static/2311e0d7653b0f844b7e66dc46286449/3c492/image-1.png 1300w,
/static/2311e0d7653b0f844b7e66dc46286449/691b3/image-1.png 2156w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;우리 팀은 완벽한 테스트 격리 환경을 구축하는 과정속에서, 초기에 빌드가 약 20분 가까이 걸리는 경우가 발생했다. 심지어 빌드 도중에 간혹 젠킨스 서버가 터져버려서 접속할 수 없게되는 경우도 심심치 않게 목격되었다.&lt;/strong&gt; 하모니 팀이 젠킨스를 실행하기 위해 사용하고 있는 EC2 인스턴스 사양은 t4g.micro 로 좋은 사양도 아니였기에, 테스트와 빌드에 있어 더 효과적인 방안이 필요했다.&lt;/p&gt;
&lt;h2 id=&quot;테스트-격리란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B2%A9%EB%A6%AC%EB%9E%80&quot; aria-label=&quot;테스트 격리란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;테스트 격리란?&lt;/h2&gt;
&lt;p&gt;테스트 격리는 &lt;strong&gt;공유 자원을 사용하는 여러 테스트끼리 격리시켜서 서로 영향을 주고 받지 못하게 하는 기법&lt;/strong&gt;을 뜻한다. 우리 하모니 팀에서는 견고한 애플리케이션과 과감한 리팩토링을 위해 테스트 전략을 수립하고 있는데, 그 중 테스트 격리를 어떻게 효과적으로 구축할 수 있는지에 대한 많은 고민과 빌드 최적화 여정이 있다. 앞선 문제 상황을 해결하기 위한 우리 팀의 고민과 최적화 여정은 어떠했을까? 🤔&lt;/p&gt;
&lt;p&gt;지난 &lt;a href=&quot;https://haon.blog/test/isolated-theory/&quot;&gt;격리된 테스트(Isolated Test) 구축과 빌드 최적화 - 이론편
&lt;/a&gt; 을 포스팅 한 적이 있다. 이어서, 우리 팀에서 어떤 방식으로 테스트 격리 환경을 구축하고 빌드 속도를 최적화했는지에 대하여 이번 포스트에서 실전편으로 다루고자 한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 테스트 격리(Isolated Test) 에 대한 자세한 이론은 &lt;a href=&quot;https://haon.blog/test/isolated-theory/&quot;&gt;격리된 테스트(Isolated Test) 구축과 빌드 최적화 - 이론편&lt;/a&gt; 을 참고하자 🙂&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;완벽한-테스트-격리를-위해&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%84%EB%B2%BD%ED%95%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B2%A9%EB%A6%AC%EB%A5%BC-%EC%9C%84%ED%95%B4&quot; aria-label=&quot;완벽한 테스트 격리를 위해 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;완벽한 테스트 격리를 위해&lt;/h2&gt;
&lt;p&gt;지난 이론편에서 다루었듯이, &lt;code class=&quot;language-text&quot;&gt;비결정적 테스트(Non-Deterministic)&lt;/code&gt; 테스트를 제거하기 위해 완벽히 격리된 테스트 환경을 구축해야 헀다. 우리 팀에서 또한 격리된 테스트 환경을 구축하기 전까지 특정 테스트가 실행 순서와 시점에 따라서 성공할 때도 있고, 실패할 때도 있었다 😅 특히나, 공유 자원을 사용하는 여러 리소스 중에 데이터베이스를 어떻게 격리시킬지가 가장 큰 이슈가 되었다. 아래 코드는 지난 이론편에서 사례로 들었던 예시로, 각 테스트간에 서로 격리되지 못하고 영향을 끼치는 &lt;code class=&quot;language-text&quot;&gt;비결정적 테스트(Non-Deterministic)&lt;/code&gt; 전형적인 사례이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@SpringBootTest&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberServiceTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberService&lt;/span&gt; memberService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;멤버를 저장한다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// given&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;devHaon@kakao.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// when, then&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;assertDoesNotThrow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; memberService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;멤버 정보가 없으면 예외가 발생한다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// given&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;devHaon@kakao.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// when, then&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;assertThatThrownBy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; memberService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;finByEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isInstanceOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NoExistMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이를위해 효과적으로 테스트를 격리시키는 방법에는 크게 &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@DataJpaTest&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@JdbcTest&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@Sql&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@DirtiesContext&lt;/code&gt; 등의 기법이 존재했다. 하지만 &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@DataJpaTest&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@JdbcTest&lt;/code&gt; 의 경우 우리의 기대와 달리, 전 구간 E2E 테스트에서 롤백이 보장되지 않는다는 특징으로 인해 완벽한 테스트 격리가 불가능하다고 설명했었다. E2E, 인수 테스트 등 전 구간 테스트는 &lt;code class=&quot;language-text&quot;&gt;@SpringBootTest&lt;/code&gt; 어노테이션에 port 를 지정하여 서버를 띄우게 되는데, 이때 HTTP 클라이언트와 서버는 각각 다른 쓰레드에서 실행된다. 따라서 &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 어노테이션을 명시했다고 한들, 호출되는 쪽은 다른 쓰레드에서 새로운 트랜잭션으로 커밋하기 때문에 롤백 전략이 무의미해진다.&lt;/p&gt;
&lt;h2 id=&quot;1-diritescontext-를-사용한-테스트-격리의-성능-문제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-diritescontext-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B2%A9%EB%A6%AC%EC%9D%98-%EC%84%B1%EB%8A%A5-%EB%AC%B8%EC%A0%9C&quot; aria-label=&quot;1 diritescontext 를 사용한 테스트 격리의 성능 문제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. @DiritesContext 를 사용한 테스트 격리의 성능 문제&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 를 이용한 롤백 전략은 E2E 테스트에서 테스트 격리를 보장해주지 않는다. 따라서 우리 팀은 완벽한 격리를 위해 더 효과적인 방안을 고민하던 끝에 &lt;code class=&quot;language-text&quot;&gt;@DirtiesContext&lt;/code&gt; 에 대해 알게 되었다. 서비스 개발 초기에는 모든 테스트에서 완벽한 격리를 위해 &lt;code class=&quot;language-text&quot;&gt;@DirtiesContext&lt;/code&gt; 를 활용하게 되었다.&lt;/p&gt;
&lt;p&gt;아래는 우리 하모니 팀에서 E2E 인수 테스트를 위해 &lt;code class=&quot;language-text&quot;&gt;@DirtiesContext&lt;/code&gt; 를 활용한 코드다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@SpringBootTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;webEnvironment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpringBootTest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WebEnvironment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RANDOM_PORT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; classes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TestConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@ActiveProfiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AcceptanceTestConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@LocalServerPort&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; port&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@BeforeEach&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RestAssured&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; port&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이로써 각 슬라이드 테스트를 위한 추상 클래스 상단에 &lt;code class=&quot;language-text&quot;&gt;@DirtiesContext&lt;/code&gt; 를 붙여서 매 테스트가 실행될 떄 마다 새로운 &lt;code class=&quot;language-text&quot;&gt;ApplicationContext&lt;/code&gt; 를 로드하게 된다.&lt;/p&gt;
&lt;h3 id=&quot;왜-diritescontext-로-격리시켰는가-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-diritescontext-%EB%A1%9C-%EA%B2%A9%EB%A6%AC%EC%8B%9C%EC%BC%B0%EB%8A%94%EA%B0%80-&quot; aria-label=&quot;왜 diritescontext 로 격리시켰는가  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 @DiritesContext 로 격리시켰는가? 🤔&lt;/h3&gt;
&lt;p&gt;초기에는 이렇듯 테스트 격리 환경을 위해 &lt;code class=&quot;language-text&quot;&gt;@DiritesContext&lt;/code&gt; 를 사용했다. &lt;code class=&quot;language-text&quot;&gt;@DiritesContext&lt;/code&gt; 를 사용하여 얻는 이점은 자명했기 떄문이다.&lt;/p&gt;
&lt;p&gt;우선 각 테스트마다 &quot;완벽한 격리&quot; 가 가능함을 보장하기 떄문이다. 지난 이론편에서 설명했듯이, 스프링부트 애플리케이션은 기본적으로 특정 하나의 테스트에서 띄운 &lt;code class=&quot;language-text&quot;&gt;ApplicationContext&lt;/code&gt; 을 재활용한다. 즉, &lt;code class=&quot;language-text&quot;&gt;ApplicationContext&lt;/code&gt; 는 한번 로딩되면 각각의 테스트에서 매번 동일한 컨텍스트를 재사용한다. 하지만 문제점은, 동일한 컨텍스트를 캐싱함으로 인하여 데이터베이스와 같은 공유 자원도 캐싱되어 모든 테스트가 동일한 공유 자원을 사용한다는 점이다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@DirtiesContext&lt;/code&gt; 를 사용하면, 각 테스트마다 별도의 새로운 컨텍스트를 띄워서 독립적인 테스트를 할 수 있게된다. 즉, 기존 내용을 캐싱하지 않고 서로 다른 컨텍스트를 띄워서 테스트하기 떄문에 완벽한 격리가 가능해진다. 어노테이션 하나만을 명시하여 완벽한 격리가 보장된다는 점이 매우 매력적으로 다가왔다.&lt;/p&gt;
&lt;h3 id=&quot;dirtiescontext-로-인한-빌드-성능-저하-문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dirtiescontext-%EB%A1%9C-%EC%9D%B8%ED%95%9C-%EB%B9%8C%EB%93%9C-%EC%84%B1%EB%8A%A5-%EC%A0%80%ED%95%98-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;dirtiescontext 로 인한 빌드 성능 저하 문제점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@DirtiesContext 로 인한 빌드 성능 저하 문제점&lt;/h3&gt;
&lt;p&gt;하지만, 아쉽게도 &lt;code class=&quot;language-text&quot;&gt;@DirtiesContext&lt;/code&gt; 가 안겨주는 치명적인 단점이  존재헀다. 이 방식은 매 테스트마다 별도의 컨텍스트를 새로 로드하는 방식일텐데, 컨텍스트를 매번 여러개를 로드하는 작업은 비용이 매우 큰 작업이다. 서비스 개발 초기에는 테스트 갯수가 그리 많지 않았기에 빌드가 오래걸리지 않아 안심하고 있었으나, 시간이 거듭될수록 빌드 속도는 20분이나 걸리게 되며, 젠킨스 서버가 일시적으로 중단되는 모습도 목격되었다 😹&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 620px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0e19915f4b6f545ef380be51672f0ab6/2a195/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.89570552147239%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACA0lEQVR42o1TW27TQBTNQpBooXUS22OPZxzbsZuH86qTNElD0pJSCoUAIj9IqAgJJP75Yg0sAIlN8MFC2MfhzsRtSlWJfhydedx75sy9ugXhV3EdshJfgzqL8rsYN2NvQ4GLEAqOF8CTERzXh+1IMGKHV0h0T9+7dL9GBZc5PD9TeZcohFENP79/w5cPKxi2wOLpOV4uV1i+WmJ2fIGLT79QS6fo9Q/QzSbo7I+QdgaamzkrZ1eCWdbHn98/8PXzO3CToxvXUU0aSKoJwqiFZO8IggfwpUIIqUrg+XovhQ9J7IqNy4Ib1LCcjNFOakiZQLNoYpuHMESComJXwqh2YfgNFP06SiLWXKS9EWfYDVIwxuHlLgse1cJkEmURoUPJqc1xr73A1vA1trJz3J+9x4PJW+z0TolX2O2eYCc7w8PRG2w//qjPHdshwTgXlJHuJKMX2krQZGh2M9TrTURRojtdNkowSyZsSrRsFzbFWBaxRXuL6QZefVkLyo1giwJmT55hPJlhRKg3WjrQDxKYFofFPILIeQ1GebcL8rXD+elzDIbU0d4Ak+kRHs0XGI6mMKkctvOv2H8EfTTLFo7PXuCEXPaHY6TtfYwP5xgcHGrRSpigTN+9k2BLCVJdysSMnKhAlWDna4U7OVTgqk7UcammIp8ENQXrqQjydbCZkhu4FPwLd/OQ06/dI+8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/0e19915f4b6f545ef380be51672f0ab6/2a195/image.png&quot;
        srcset=&quot;/static/0e19915f4b6f545ef380be51672f0ab6/222b7/image.png 163w,
/static/0e19915f4b6f545ef380be51672f0ab6/ff46a/image.png 325w,
/static/0e19915f4b6f545ef380be51672f0ab6/2a195/image.png 620w&quot;
        sizes=&quot;(max-width: 620px) 100vw, 620px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-sql-어노테이션과-truncate-하는-sql-파일을-활용한-성능-개선&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-sql-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EA%B3%BC-truncate-%ED%95%98%EB%8A%94-sql-%ED%8C%8C%EC%9D%BC%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0&quot; aria-label=&quot;2 sql 어노테이션과 truncate 하는 sql 파일을 활용한 성능 개선 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. @Sql 어노테이션과 TRUNCATE 하는 SQL 파일을 활용한 성능 개선&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@DirtiesContext&lt;/code&gt; 를 사용함으로 인해 매번 컨텍스트를 띄우는 일은 매우 비효율적이었다. 이를 위해 &lt;code class=&quot;language-text&quot;&gt;@DirtiesContext&lt;/code&gt; 를 제거하고, 스프링부트 애플리케이션 컨텍스트를 한 번 띄우고 난뒤 매번 동일한 컨텍스트를 재활용하는 방안을 찾게 되었다. 이를 가능케하는 것이 바로 지난 이론편에서 살펴봤던 &lt;code class=&quot;language-text&quot;&gt;@Sql&lt;/code&gt; 어노테이션이었다.&lt;/p&gt;
&lt;h3 id=&quot;하지만-sql-파일을-관리하기에-번거롭다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%98%EC%A7%80%EB%A7%8C-sql-%ED%8C%8C%EC%9D%BC%EC%9D%84-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0%EC%97%90-%EB%B2%88%EA%B1%B0%EB%A1%AD%EB%8B%A4&quot; aria-label=&quot;하지만 sql 파일을 관리하기에 번거롭다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;하지만, SQL 파일을 관리하기에 번거롭다.&lt;/h3&gt;
&lt;p&gt;결국 &lt;code class=&quot;language-text&quot;&gt;@DirtiesContext&lt;/code&gt; 의 성능이 좋지 않아, 이후에는 &lt;code class=&quot;language-text&quot;&gt;@Sql&lt;/code&gt; 과 테이블을 &lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; 하는 SQL 파일을 작성하여 테스트를 격리하는 방법으로 마이그레이션 하고자 했다. 클래스 테스트가 실행되기 전에 &lt;code class=&quot;language-text&quot;&gt;@Sql&lt;/code&gt; 이 가리키는 경로에 있는 SQL 실행이 먼저 일어나는 방식으로, 따라서 이 파일안에 모든 테이블에 대한 TRUNCATE SQL을 미리 작성해 놓으면, 파일하나와 어노테이션만으로 테스트 격리를 이뤄낼 수 있으니 꽤나 획기적인 방식이라고 생각이 들었다. 실제로 아래와 같이 SQL 파일 내용을 구성할 것을 고려했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;TRUNCATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; member&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;TRUNCATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; keyword&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;TRUNCATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; live_information&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 DirtiesContext를 사용할 때보다 성능은 개선될테지만, 엔티티가 추가/제거될 때마다 SQL 파일도 변경해줘야 하는 번거로움이 존재했습니다. 유지.보수에 있어 변동사항이 있더라도 개발자가 직접 일일이 수정해줘야하는 번거로움 또한 제거하고 싶었다.&lt;/p&gt;
&lt;h2 id=&quot;3-entitymanager로-직접-truncate-쿼리를-실행하는-databasecleaner-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-entitymanager%EB%A1%9C-%EC%A7%81%EC%A0%91-truncate-%EC%BF%BC%EB%A6%AC%EB%A5%BC-%EC%8B%A4%ED%96%89%ED%95%98%EB%8A%94-databasecleaner-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;3 entitymanager로 직접 truncate 쿼리를 실행하는 databasecleaner 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. EntityManager로 직접 TRUNCATE 쿼리를 실행하는 DatabaseCleaner 구현&lt;/h2&gt;
&lt;p&gt;고민끝에 &lt;code class=&quot;language-text&quot;&gt;@Sql&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; 쿼리가 작성된 SQL 파일을 이용한 문제점을 해결하기 위해, &lt;strong&gt;테스트가 실행되기 직전 호출되어 데이터베이스를 초기 상태로 만들어주는 DatabaseCleaner를 구현했다.&lt;/strong&gt; EntityManager를 통해 존재하는 모든 Entity를 가져오고, 리플렉션을 통해 Entity &lt;code class=&quot;language-text&quot;&gt;@Table&lt;/code&gt; 어노테이션의 테이블 명을 가져오는 방식이다. 가져온 테이블명 목록을 통해 TRUNCATE 쿼리를 실행하여 테이블을 초기화한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DatabaseCleaner&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EntityManager&lt;/span&gt; entityManager&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; tableNames&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DatabaseCleaner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EntityManager&lt;/span&gt; entityManager&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;entityManager &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entityManager&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tableNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entityManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMetamodel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntities&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getJavaType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;javaType &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; javaType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAnnotation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Table&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        entityManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flush&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        entityManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createNativeQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;SET foreign_key_checks = 0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; tableName &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; tableNames&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            entityManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createNativeQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;TRUNCATE TABLE &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; tableName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        entityManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createNativeQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;SET foreign_key_checks = 1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 방식은 SQL 파일을 직접 실행시키기보다 JPA에서 쿼리를 직접 만들 수 있는 EntityManager 를 빈으로 주입받고, 모든 테이블 이름을 조사해서 각각의 테스트가 시작할 때, TRUNCATE 쿼리를 실행시키는 방식이다.&lt;/p&gt;
&lt;p&gt;이 DatabaseCleaner 를 만들어 놓음으로써, 한번 만들어놓은뒤 엔티티가 얼마나 더 추가되고 삭제되는지와 상관없이 모든 테스트를 효과적으로 격리시킬 수 있게 된다 🙂&lt;/p&gt;
&lt;h2 id=&quot;databasecleaner-를-통한-최적화-결과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#databasecleaner-%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%B5%9C%EC%A0%81%ED%99%94-%EA%B2%B0%EA%B3%BC&quot; aria-label=&quot;databasecleaner 를 통한 최적화 결과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DatabaseCleaner 를 통한 최적화 결과&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5394eaa04b78790471e6909bf08a3076/e4611/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.34969325153374%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAADB0lEQVR42m2T21NTVxTGz//SNzuM9sGOT32wD8zUzqjDtJ2x8oK22qn1NnQqaEesaERCIOEWEwJCEkhyuAQIMRBMAMlF5FIugSSYQLiNTowRqAWk4echWMtDv5m999qzvllr7W/tJfA/2NnZSS9pY2DgCfLCm/Q0qnhkUGGsVZNMvknzUqmUREntcT9A+DfArlOy9tkgis1cPH+WsNvAor8FXvrxiSrkRbK9xB94+yHsv2xsbbO5+S5tJxKvOPPjT5TczmfRJxJyNxEf62Qj5KSjXklydZ2F6Cwzo15ikTDr63/9F3A2NMP08BNmfA6eDbrSjpWVZU59m4Us/xLEBpjsrmekq5ZRm46WmhJexhM0astprfidkU4NrfoHbKd2EOKvEpir7/HMWoWh+Nd0RbtYiMWoLMwl4GxIV5UYt7EW6GZQrESsUaQ5T31etFUqRLUMj1XHwuIywnQgwIiUVSu/SaO+nuGhoY8SNJuM+K1qtsKPeT1hp+Z+Pg/v51GtuEOXrYPZcJiRsQkmnAbCfUamA1MILqeD5HgHnvYaguEI0WgUo76BWDSCxe5CXVXKuMvIcJeOXksFxXev0aot4sVQM/52LbrSP3gb7GF+0ISrx47Q1lRHfLSNtWkHD+QFuC2VLPtFPGI5OdducSGviNtffsH1q5dQ517k/KHDNNaqJE36JSl66DOr2HruIuTS095iQWjQKFl5KvLPXB+9xlISf3ayPdcvdaobWVkJv+RcZunAJ8gPf86VjIMYpDP3ViHbQQfvnveSnLRLHfQwYFbi9fqkgHWSmF4TLHsk4bvYlLKuT9lgyU258h7nbiiQHTlCZmYWnx77nqyfr1Pw0Jp+Qb+lHJ2iAKdFg81cx+rqGkIoGMQidcnVpMSqlTFu1/F6rA1fSwWZx7O4om0jR1FPxtfZfHYih29y73DDYEet0eD3+YgtrRBPJHn798beP9ydjPnYIpFIFLe7H0OthrrqMswmEyezf+BUfjFyxzBHsy+Q8dVpvvtNRp7+EVPB8Mfx2z+y7wEx3AOo69GM3wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/5394eaa04b78790471e6909bf08a3076/a6d36/image-2.png&quot;
        srcset=&quot;/static/5394eaa04b78790471e6909bf08a3076/222b7/image-2.png 163w,
/static/5394eaa04b78790471e6909bf08a3076/ff46a/image-2.png 325w,
/static/5394eaa04b78790471e6909bf08a3076/a6d36/image-2.png 650w,
/static/5394eaa04b78790471e6909bf08a3076/e548f/image-2.png 975w,
/static/5394eaa04b78790471e6909bf08a3076/e4611/image-2.png 1298w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2311e0d7653b0f844b7e66dc46286449/691b3/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.607361963190186%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAABk0lEQVR42kWQTU8TQRyH90N4MEWgNaDdV/aNbkuzLQSBEiRrUjUaLnwXYyRRikgLyIkES7fWvtAY7yIJcMC7esMT3prgsXmYrBEmefKb/DLzTP4jlTffs1HZpiJyc3uHypbIrZ2IsujPz3/zf52dfefFyxVKb99RWiuzWtrgzeqaYF3s13m18hpp+XkRb9zDcScE8ySTGrJikJR1hhL3+Hp4dC1sN5s8DQqMOQ6e9wDLmSWp6CiajayaxO4kkJ4Vi8yks7hTD9GmH6PmFjDygqmAMdvj+OSUfr8fCZuNFjk/gzmTw5gMkP0F5Owcqj+P4RdQNQvJTefw7RTBoyfk5xZxJybR7DS6kxHCVCTs9Xr8vbwkDBskFIV8MEtQXGK6sEgqK85bKUw3g6rbSLJqocZHGReXDdMRr5gMDt39x/AIh99uRt7d3eNWbID7ro4uxtR0K/qagcEEw/ERbsfiSN3PX+h0urRaHZqCVvuAzkE3oi36i4s/18IfP3+xV60RfvxEtVanuh+yLzKsN6iL7kOtzhWgJhmZ0CXpLgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/2311e0d7653b0f844b7e66dc46286449/a6d36/image-1.png&quot;
        srcset=&quot;/static/2311e0d7653b0f844b7e66dc46286449/222b7/image-1.png 163w,
/static/2311e0d7653b0f844b7e66dc46286449/ff46a/image-1.png 325w,
/static/2311e0d7653b0f844b7e66dc46286449/a6d36/image-1.png 650w,
/static/2311e0d7653b0f844b7e66dc46286449/e548f/image-1.png 975w,
/static/2311e0d7653b0f844b7e66dc46286449/3c492/image-1.png 1300w,
/static/2311e0d7653b0f844b7e66dc46286449/691b3/image-1.png 2156w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;앞선 고민과 최적화 여정들을 통해, &lt;strong&gt;빌드 성능이 약 20분에서 3분대로 6.1배 가까이 상승했다.&lt;/strong&gt; 이로써 완벽한 테스트 격리는 몰론, 빌드 속도 최적화, 젠킨스 서버에 가하졌던 부하를 감축시킬 수 있게 되었다. 코드가 Develop 브랜치에 병합되고나서 20분이라는 오랜 시간이 지나서야 빌드가 완료되었다는 알림을 받았는데, 이제 짧은 시간내로 빌드 완료 알림 또한 받을 수 있게 되었다 😎&lt;/p&gt;
&lt;p&gt;많은 삽질이 있었지만, 그 만큼 재밌었던 경험이었다. 이만 글을 마쳐보겠다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[격리된 테스트(Isolated Test) 구축과 빌드 최적화 - 이론편]]></title><description><![CDATA[…]]></description><link>https://haon.site/test/isolated-theory/</link><guid isPermaLink="false">https://haon.site/test/isolated-theory/</guid><pubDate>Tue, 20 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;이론편&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%A1%A0%ED%8E%B8&quot; aria-label=&quot;이론편 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이론편&lt;/h2&gt;
&lt;p&gt;우리 카테부 하모니 팀에서는 견고한 애플리케이션 구축을 위해 테스트 전략을 수립하고, 다양한 테스트 코드를 작성하고 있다. 이 과정속에서 우리 팀 백엔드 애플리케이션 테스트 환경을 가장 괴롭혔던 키워드가 바로 &lt;code class=&quot;language-text&quot;&gt;테스트 격리&lt;/code&gt; 이다.&lt;/p&gt;
&lt;p&gt;테스트 격리란 무엇인가? 그리고 이는 무엇이길래 우리 팀의 테스트 환경 구축을 어렵게 만들었을까? 이를위해, 우선 테스트 격리가 무엇인지에 대해 자세히 다루어보고자 한다. 이번 포스팅에선 테스트 격리의 이론편을 구성하고, 다음 포스트에선 우리 팀에서 마주한 테스트 격리 관련 트러블슈팅을 실전편으로 다루어보겠다.&lt;/p&gt;
&lt;h2 id=&quot;격리되지-않은-테스트의-문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%A9%EB%A6%AC%EB%90%98%EC%A7%80-%EC%95%8A%EC%9D%80-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;격리되지 않은 테스트의 문제점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;격리되지 않은 테스트의 문제점&lt;/h2&gt;
&lt;p&gt;우리가 작성한 여러 테스트들은 실행 순서에 상관없이 각기 독립적으로 수행되어야 한다. 즉, 각 테스트는 독립적으로 &quot;격리된&quot; 환경속에서 서로에게 영향을 미치지 않고 수행되어야 한다. 하지만, 데이터베이스와 같이 공유 자원을 사용하는 테스트들은 &lt;strong&gt;실행 순서에 따라 성공, 실패 여부가 결정되는 비결정적(Non-Deterministic) 테스트&lt;/strong&gt; 가 될 수 있다.&lt;/p&gt;
&lt;p&gt;비결정적 테스트(Non-Deterministic)란 같은 입력값에 대해 항상 같은 결과를 보장하지 않는 테스트를 뜻한다. 또한 이 비결정적 테스트가 발생하는 원인은 테스트 격리가 부족하게 될 때 발생하는 것이다. 가령 아래의 테스트를 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@SpringBootTest&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberServiceTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberService&lt;/span&gt; memberService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;멤버를 저장한다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// given&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;devHaon@kakao.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// when, then&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;assertDoesNotThrow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; memberService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;멤버 정보가 없으면 예외가 발생한다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// given&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;devHaon@kakao.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// when, then&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;assertThatThrownBy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; memberService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;finByEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isInstanceOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NoExistMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;첫번째 테스트는 두번째 테스트에 아무런 영향을 끼치지 않을까? 만약 test1 을 수행 후 test2 를 수행하면, 아쉽게도 첫번째 테스트는 의도치 않게 두번째 테스트에 영향력을 행사하게 되어 실패한다. 반면 test2 만을 싫행하면, 신가하게도 테스트는 성공한다. &lt;strong&gt;이것이 바로 비결정적 테스트이며, 테스트 격리가 부족하여 발생한 상황이다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;테스트-격리의-필요성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B2%A9%EB%A6%AC%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1&quot; aria-label=&quot;테스트 격리의 필요성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;테스트 격리의 필요성&lt;/h3&gt;
&lt;p&gt;테스트 격리는 &lt;strong&gt;공유 자원을 사용하는 여러 테스트끼리 격리시켜서 서로 영향을 주고 받지 못하게 하는 기법&lt;/strong&gt;이다. 공유 자원이라 함은, 대표적으로 데이터베이스가 해당한다. 데이터베이스를 사용하는 스프링 객체를 테스트할 때 테스트 격리는 반드시 필요하다.&lt;/p&gt;
&lt;h2 id=&quot;스프링부트-애플리케이션에서-테스트를-격리하는-방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90%EC%84%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EA%B2%A9%EB%A6%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;스프링부트 애플리케이션에서 테스트를 격리하는 방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링부트 애플리케이션에서 테스트를 격리하는 방법&lt;/h2&gt;
&lt;p&gt;격리된 테스트 환경을 구축하지 않을 경우 발생하는 문제점과, 테스트 격리가 왜 필요한지에 대해 살펴봤다. 그렇다면 스프링부트 애플리케이션에서 효과적으로 테스트를 격리하는 방법으로 어떤 방법이 있는지 학습해보도록 하자.&lt;/p&gt;
&lt;h3 id=&quot;transactional&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#transactional&quot; aria-label=&quot;transactional permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@Transactional&lt;/h3&gt;
&lt;p&gt;가장 먼저 떠올릴 수 있는 방법은 스프링부트에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 어노테이션으로 각 테스트를 트랜잭션 단위로 처리하는 방식이다. 각 테스트가 실행된 후 매번 롤백을 시킨다면 다음 테스트에 영향을 끼치지 않으리란 가정을 깔고 격리시키는 기법이다. &lt;strong&gt;하지만, 아쉽게도 @Transactional 기반 트랜잭션 롤백하는 전략은 E2E 테스트, 인수 테스트 등에서는 사용할 수 없다.&lt;/strong&gt; 다시말해, 테스트 종류에 따라 격리가 불가능한 케이스가 존재한다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 어노테이션을 명시하여 트랜잭션을 끝난 뒤 롤백되는 것은 자명한 사실이다. 하지만, E2E, 인수 테스트 등 전 구간 테스트는 &lt;code class=&quot;language-text&quot;&gt;@SpringBootTest&lt;/code&gt; 어노테이션에 port 를 지정하여 서버를 띄우게 되는데, 이때 HTTP 클라이언트와 서버는 각각 다른 쓰레드에서 실행된다. &lt;strong&gt;즉, @Transactional 어노테이션이 있다고 한들 호출되는 쪽은 다른 쓰레드에서 새로운 트랜잭션으로 커밋하기 때문에 롤백 전략이 무의미해진다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@SpringBootTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;webEnvironment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpringBootTest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WebEnvironment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RANDOM_PORT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; classes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TestConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@ActiveProfiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AcceptanceTestConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@LocalServerPort&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; port&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DatabaseCleaner&lt;/span&gt; databaseCleaner&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@BeforeEach&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RestAssured&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; port&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        databaseCleaner&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위는 실제 우리 하모니 팀 인수 테스트를 위한 추상 클래스인데, 보듯이 &lt;code class=&quot;language-text&quot;&gt;@SpringBootTest&lt;/code&gt; 내에 포트를 지정하고 있다. 우리 팀 또한 인수 테스트를 작성하고 있기 때문에, &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 기반 롤백 전략보다 더 효과적인 격리 기법이 필요하다고 판단했다.&lt;/p&gt;
&lt;h3 id=&quot;datajpatest-jdbctest&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#datajpatest-jdbctest&quot; aria-label=&quot;datajpatest jdbctest permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@DataJpaTest, @JdbcTest&lt;/h3&gt;
&lt;p&gt;다음으로 영속성 레이어 관련 테스트 (Dao, Repository 등) 을 테스트할 때 테스트를 격리시키는 방법이다. 이는 &lt;code class=&quot;language-text&quot;&gt;@DataJpaTest&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;@JdbcTest&lt;/code&gt; 어노테이션을 통해 테스트 격리 환경을 구축하는 방법이다. 이번에 테스트 격리에 대해 학습하면서 처음 알게된 사실인데, 이 어노테이션에선 테스트 격리를 보장하고 있었다는 점이다. 여지껏 하모니 팀에서 영속성 계층 테스트 코드를 작성시 별 생각없이 &lt;code class=&quot;language-text&quot;&gt;슬라이스 테스트(Slice Test)&lt;/code&gt; 만을 위해 이 어노테이션들을 사용했는데, 반성해야겠다.&lt;/p&gt;
&lt;p&gt;아래에 실제 하모니 팀 프로젝트에서 작성한 테스트 코드 중 일부를 가져와봤다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberLiveInformationRepositoryTest&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RepositoryTestConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberLiveInformationRepository&lt;/span&gt; memberLiveInformationRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberRepository&lt;/span&gt; memberRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LiveInformationRepository&lt;/span&gt; liveInformationRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;멤버의 생활정보를 찾는다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; 멤버의_생활정보를_찾는다&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// given&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; 하온 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; memberRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;하온_기존&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;LiveInformation&lt;/span&gt; 생활정보&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; liveInformationRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LiveInformation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;생활정보1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;LiveInformation&lt;/span&gt; 생활정보&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; liveInformationRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LiveInformation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;생활정보2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;MemberLiveInformation&lt;/span&gt; memberLiveInformation1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberLiveInformation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;생활정보&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 하온&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;MemberLiveInformation&lt;/span&gt; memberLiveInformation2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberLiveInformation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;생활정보&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 하온&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MemberLiveInformation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; memberLiveInformations &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;memberLiveInformation1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; memberLiveInformation2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        memberLiveInformationRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;saveAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;memberLiveInformations&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// when, then&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;memberLiveInformationRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findByMemberId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;하온&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;멤버의 생활정보를 삭제한다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; 멤버의_생활정보를_삭제한다&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// given&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; 하온 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; memberRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;하온_기존&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;LiveInformation&lt;/span&gt; 생활정보&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; liveInformationRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;생활정보&lt;span class=&quot;token number&quot;&gt;1_&lt;/span&gt;생성&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;LiveInformation&lt;/span&gt; 생활정보&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; liveInformationRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;생활정보&lt;span class=&quot;token number&quot;&gt;2_&lt;/span&gt;생성&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;MemberLiveInformation&lt;/span&gt; 멤버_생활정보&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberLiveInformation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;생활정보&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 하온&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;MemberLiveInformation&lt;/span&gt; 멤버_생활정보&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberLiveInformation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;생활정보&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 하온&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        memberLiveInformationRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;saveAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;멤버_생활정보&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 멤버_생활정보&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// when&lt;/span&gt;
        memberLiveInformationRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deleteByMemberId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;하온&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// then&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;memberLiveInformationRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isEqualTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;신기하게도 &lt;code class=&quot;language-text&quot;&gt;@DataJpaTest&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;@JdbcTest&lt;/code&gt; 는 내부적으로 &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 어노테이션을 가지고 트랜잭션 처리를 하고 있었다. 이 어노테이션들은 단순 슬라이스 테스트만이 아닌, 영속성 계층내에 작성된 테스트 코드간의 격리 환경을 만들어주고 있었다. 따라서 영속성 계층 테스트를 격리시키기 위해 &lt;code class=&quot;language-text&quot;&gt;@DataJpaTest&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;@JdbcTest&lt;/code&gt; 활용을 다소 기대해볼 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@TypeExcludeFilters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DataJpaTypeExcludeFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 트랜잭션 활용&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@AutoConfigureCache&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@AutoConfigureDataJpa&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@AutoConfigureTestDatabase&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@AutoConfigureTestEntityManager&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@ImportAutoConfiguration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataJpaTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 앞선 &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 어노테이션 롤백 전략의 한계점과 같이, 동일한 문제점을 가지고 있다. 영속성 레이어 테스트에 한정해선 간편히 테스트 격리가 가능하지만, 이 외의 계층을 테스트할 땐 이들만으로는 완전한 격리가 불가능하다. 따라서 이 방법 외에 더 완전한 격리를 위한 기법이 필요하다.&lt;/p&gt;
&lt;h3 id=&quot;sql&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sql&quot; aria-label=&quot;sql permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@Sql&lt;/h3&gt;
&lt;p&gt;다음으로 스프링부트에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;@Sql&lt;/code&gt; 어노테이션으로 격리시킬 수 있다. 테스트 클래스에 이 어노테이션을 명시하여 매 테스트 메소드를 실행하기전에, 지정된 경로의 SQL 스크립트를 실행할 수 있다.&lt;/p&gt;
&lt;p&gt;SQL 스크립트내에 &lt;code class=&quot;language-text&quot;&gt;DROP&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;DELETE&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; 와 같은 SQL 명령어를 작성하여 깨끗하게 데이터베이스를 비울 수 있다. 참고로 &lt;code class=&quot;language-text&quot;&gt;DROP&lt;/code&gt; 은 테이블 자체를 제거하는 것이고, &lt;code class=&quot;language-text&quot;&gt;DELETE&lt;/code&gt; 는 특정 행을 제거하는 것이며, &lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; 는 테이블 자체를 제거하진 않지만, 테이블의 모든 행을 제거하는 것이다.&lt;/p&gt;
&lt;p&gt;우리는 이 SQL 명령어 중 &lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; 를 활용하여 각 테스트 실행 뒤 데이터베이스를 깨끗하에 비워줄 수 있을 것이다. 작성 방법은 아래와 같다.&lt;/p&gt;
&lt;h4&gt;clear.sql&lt;/h4&gt;
&lt;p&gt;임의로 clear 라는 네이밍을 지었지만, 각 상황에 알맞게 truncate.sql 과 같이 명시하여 SQL 파일을 생성하면 될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;TRUNCATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; member&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;TRUNCATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; keyword&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;TRUNCATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; live_information&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또한 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;@Sql&lt;/code&gt; 어노테이션을 명시하여 테스트를 격리시킬 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Sql&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/clear.sql&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberServiceTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@Sql&lt;/code&gt; 어노테이션과 &lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; 를 작성한 SQL 파일을 활용하면 앞선 방식들보다 확실히, 완전한 테스트 격리가 가능해진다. 더 정확히는, 공유 자원이 데이터베이스인 경우에 사용하면 완벽한 테스트 격리가 가능할 것이다. &lt;strong&gt;특히나 앞선 방식들과 달리 전 구간 E2E 테스트에서도 완벽한 테스트 격리가 가능해진다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;dirtiescontext&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dirtiescontext&quot; aria-label=&quot;dirtiescontext permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@DirtiesContext&lt;/h3&gt;
&lt;p&gt;사실 이 방법을 사용하면 아주 간단하게, 또한 아주 완벽한 테스트 격리가 가능해진다. 바로 &lt;code class=&quot;language-text&quot;&gt;@DirtiesContext&lt;/code&gt; 어노테이션을 각 테스트에 명시하는 기법이다.&lt;/p&gt;
&lt;p&gt;스프링부트 애플리케이션은 기본적으로 특정 하나의 테스트에서 띄운 &lt;code class=&quot;language-text&quot;&gt;ApplicationContext&lt;/code&gt; 을 재활용한다. 즉, &lt;code class=&quot;language-text&quot;&gt;ApplicationContext&lt;/code&gt; 는 한번 로딩되면 각각의 테스트에서 매번 동일한 컨텍스트를 재사용한다. 하지만 문제점은, 동일한 컨텍스트를 캐싱함으로 인하여 데이터베이스와 같은 공유 자원도 캐싱되어 모든 테스트가 동일한 공유 자원을 사용한다는 점이다. 이 때문에 지금까지 앞선 테스트 격리를 통해 공유 자원을 활용하는 여러 테스트간에 어떻게 격리시킬지에 대해 학습해왔던 것이다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@DirtiesContext&lt;/code&gt; 를 사용하면, 각 테스트마다 별도의 새로운 컨텍스트를 띄워서 독립적인 테스트를 할 수 있게된다. 즉, 기존 내용을 캐싱하지 않고 서로 다른 컨텍스트를 띄워서 테스트하기 떄문에 완벽한 격리가 가능해진다.&lt;/p&gt;
&lt;p&gt;아래는 우리 하모니 프로젝트에서 테스트 격리 환경을 리팩토링하기전에 &lt;code class=&quot;language-text&quot;&gt;@DirtiesContext&lt;/code&gt; 를 활용한 실제 코드이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@DirtiesContext&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@SpringBootTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;webEnvironment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpringBootTest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WebEnvironment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RANDOM_PORT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; classes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TestConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@ActiveProfiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AcceptanceTestConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@LocalServerPort&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; port&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@BeforeEach&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RestAssured&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; port&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;하지만 컨텍스트를 캐싱하지 않고 매번 새롭게 띄우는 작업은 비용이 메우 큰 작업이다.&lt;/strong&gt; 따라서 이 어노테이션은 사용 타당성이 충분히 확보된 상황에서 사용하는 것이 좋다. 우리 팀에서 왜 테스트 격리를 위해 초기에 &lt;code class=&quot;language-text&quot;&gt;@DirtiesContext&lt;/code&gt; 를 제거했는지의 자세한 이유는 실전편 포스팅에서 다루고자 한다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/isolated-test/&quot;&gt;https://hudi.blog/isolated-test/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2020-09-15-test-isolation/&quot;&gt;https://tecoble.techcourse.co.kr/post/2020-09-15-test-isolation/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/264&quot;&gt;https://mangkyu.tistory.com/264&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[하모니 팀의 Rest Docs 와 MockMvc 를 활용한 API 문서 자동화]]></title><description><![CDATA[💡 현재 포스트는 하모니 팀 기술 블로그 에 게시된 글 입니다. Rest Docs 도입  현재 카테부 하모니 팀은 원활한 API 문서 자동화를 위해 Rest Docs 를 도입하였다. Rest Docs 는 자바 진영에서 가장 많이 사용되는 API…]]></description><link>https://haon.site/spring/rest-docs/</link><guid isPermaLink="false">https://haon.site/spring/rest-docs/</guid><pubDate>Sat, 17 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://main--kakaotech-moheng.netlify.app/backend/rest-docs/&quot;&gt;하모니 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;rest-docs-도입&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rest-docs-%EB%8F%84%EC%9E%85&quot; aria-label=&quot;rest docs 도입 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rest Docs 도입&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/76ee6fbc6be0a192d500d56d24788950/11a8f/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.16564417177914%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAACmUlEQVR42n2U+0+SURjH+YNcbrVaW60y+6VcbV3W2qqhM13eyjKoJdnUiJlTKy2bLjVXlvcLIigoqYjihUxMrJkiMa8NCgU0Lt/O+6CvldJhZ8B5z/k83+d7nucVIMQIsI8/4KffzjU7XL+cwXW2xj0LNQQ7wjZA3LfhWzse9cQhQ3MeQzb1tj3/BVL0QDD6jP0TCvtTcaf9FHK6Y1FreoL0jjPI7Y2HxTHBZ/GvWkEQFOAjrqw7UDNWgIfaaGR3XcLL4QzUmArw3CBC/XghKozZEKui8PZjLhyeJR68TaEv4IN+VgGJ+ixi6sOZmtNonniBSqMUBbpkVI/lI1+XyGYSlF9eIbU1EpmaC7C7F7Epigdy6T3QXsaVhj1IbDlImxObDyC+cR/ydAloYuCi/jSIVCcgrA1DEtvDKR+wtmHd5+GvkQcur9pQ+UGKZPkhxDXuxTV5BJtHcL01gv7fUBxj6V+EsG4XpFohCfjumkPpYDrGF/QwLfRtVEQgCDQvGZiKYhjntHg2cAuxDbtxtWk/Ax4lcHRdOMTKKOgszVRCys/leD0qwxgDvRnNYWdEUE9VbSn84VlGi7mEjO/6Wk0Hs0hRGFLkhynYoK0Dk8tDBLinPgdZdwwqRrIgN5eyargJg1W1BRyd70HZyH06VMVur2TwLvNHic6pd0x1VzAYU8GVi+x9DHk6bNPgtuokHO4luphNLwno83sJkNebQGr6rQrmj4R8LR2S0K1yKbVNlpEtIuVxZkckemYaQ9ThxqLH66Loj/tS0MlS107XotfShPKRTBQbxJh1TJL5804LFlZmseZ1h65DP3VJsLgXV60EKRpIw1N96l8t5/+j5fw7tJ8gVB9zY9puwur6T75wN5Vstdz2l8RvF8PAK9oe7OQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/76ee6fbc6be0a192d500d56d24788950/a6d36/image.png&quot;
        srcset=&quot;/static/76ee6fbc6be0a192d500d56d24788950/222b7/image.png 163w,
/static/76ee6fbc6be0a192d500d56d24788950/ff46a/image.png 325w,
/static/76ee6fbc6be0a192d500d56d24788950/a6d36/image.png 650w,
/static/76ee6fbc6be0a192d500d56d24788950/e548f/image.png 975w,
/static/76ee6fbc6be0a192d500d56d24788950/11a8f/image.png 1272w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;현재 카테부 하모니 팀은 원활한 API 문서 자동화를 위해 Rest Docs 를 도입하였다. Rest Docs 는 자바 진영에서 가장 많이 사용되는 API 자동화 라이브러리이다. 왜 우리는 Rest Docs 를 도입하였는가?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; &lt;strong&gt;API 문서 작성을 자동화한다&lt;/strong&gt; : 당연하게도, 백엔드와 프론트엔드 개발자 사이의 협업시 가장 중요한 요소 중 하나가 바로 API 명세서이다. 개발해놓은 REST API 명세에 대한 문서화가 잘 되어있어야지 연동이 쉬워진다. Rest Docs 와 같은 API 문서화 툴은 이를 손쉽게 가능하도록 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; &lt;strong&gt;문서화의 실수할 여지를 줄여준다&lt;/strong&gt; : 구글 독스, 노션등 사람이 직접 API 명세를 문서화할 수 있지만, 이는 사람으로 하여금 실수할 큰 여지를 만든다. 한 API 를 명세하는데에 있어서도 요청과 응답사이의 헤더, 바디, 쿠키, URI, 메소드 종류, 상태코드 값, 쿼리 파라미터 등 많은 요소들을 단 한치의 오차없이 사람이 명세하기란 힘들다. API 문서 자동화 툴을 사용한다면 (일반적으로) 실제 API 스팩과 달라지는 것 없이 오차없는 명세를 작성해준다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; &lt;strong&gt;자동화를 통한 지속적인 명세 최신화&lt;/strong&gt; : 또한 요구사항 변경, 팀내 지속적인 변화로 인해 API 스팩을 변경해야하는 상황이 있다면 어떻게할까? 실제 API 내용이 변경된다면, 기존 API 명세 또한 변경되어야 한다. API 자동화 툴을 사용한다면 API 변경이 일어날 때 마다 명세 또한 빠르게 최신화할 수 있다. 노션, 위키등으로 사람이 직접 수정할 때와 달리, 코드 몇 줄만 바꾸는것이 끝이다. 자동화는 잦은 요구사항 변경, 유지.보수에 있어서 큰 장점을 보인다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;자바-진영에서-제공하는-api-문서-자동화-툴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EB%B0%94-%EC%A7%84%EC%98%81%EC%97%90%EC%84%9C-%EC%A0%9C%EA%B3%B5%ED%95%98%EB%8A%94-api-%EB%AC%B8%EC%84%9C-%EC%9E%90%EB%8F%99%ED%99%94-%ED%88%B4&quot; aria-label=&quot;자바 진영에서 제공하는 api 문서 자동화 툴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자바 진영에서 제공하는 API 문서 자동화 툴&lt;/h2&gt;
&lt;p&gt;Rest Docs 를 비롯하여 API 문서 작성을 도와하는 자동화 툴은 많이 개발되어 있다. 자바 진영에서 가장 많이 쓰이는 툴에는 &lt;strong&gt;Swagger 와 Rest Docs&lt;/strong&gt; 이 2가지가 있다. 우리 하모니 팀은 이 중에 &lt;strong&gt;Rest Docs 를 선택하게 되었다.&lt;/strong&gt; 또한 Rest Docs 의 &lt;strong&gt;문서 자동화를 도와주는 보조 테스트 도구로 MockMvc, 문서로는 Asciidoc 을 선택하게 되었다.&lt;/strong&gt; 우리 팀이 왜 이런 문서화 툴을 선택하게 되었는지 근거를 정리해보겠다.&lt;/p&gt;
&lt;p&gt;또한 실제로 Rest Docs 를 활용하여 API 문서 자동화 환경을 구축하는 간단한 환경을 실습해보도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;spring-rest-docs-vs-swagger&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#spring-rest-docs-vs-swagger&quot; aria-label=&quot;spring rest docs vs swagger permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Spring Rest Docs vs Swagger&lt;/h2&gt;
&lt;p&gt;우리 팀은 Rest Docs 를 선택했지만, Swagger 또한 막강한 장점을 가지고 있다. 스프링부트 프로젝트에서 Swagger 를 사용하면 컨트롤러 메소드 상단에 간단히 어노테이션을 추가하고, API 명세하면 끝이다. 컨트롤러 계층에 간단히 어노테이션을 붙이고 API 명세를 자동화하는 방식은 직관적이고 명세하기 쉽다.&lt;/p&gt;
&lt;p&gt;아래 코드는 이번 하모니 팀 프로젝트 진행전에, 다른 프로젝트에서 Swagger 로 명세했던 코드이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@ResponseBody&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@PostMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/makeTeam&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Operation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;summary &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;팀 생성&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;jwt 를 HttpHeader에 넘겨주시고 생성해주세요. 누락된 정보가 있거나 20글자를 초과할 때 에러를 발생시켰습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseResponse&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestBody&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PostTeamReq&lt;/span&gt; postTeamReq&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; userIdxByJwt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; jwtService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        mappingService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;makeTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userIdxByJwt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; postTeamReq&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BaseException&lt;/span&gt; baseException&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;baseException&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;간단히 어노테이션만 &quot;틱&quot; 붙혀서 API 문서를 자동으로 생성해주니, 아주 간단하고 편리하다. 하지만 Swagger 는 아래와 같은 단점이 존재한다.&lt;/p&gt;
&lt;h3 id=&quot;swagger-를-왜-사용하지-않았는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#swagger-%EB%A5%BC-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EC%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;swagger 를 왜 사용하지 않았는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Swagger 를 왜 사용하지 않았는가?&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; API 스팩에 변동이 일어나도, Swagger 는 이를 감지하고 API 명세를 수정해주지 않는다. &lt;strong&gt;즉, 실제 API 스팩과 Swagger 기반 명세서 사이에 스팩이 일치함을 보장하지 않는다.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 프로덕션 코드, 즉 컨트롤러 계층에 API 명세에 관련한 코드를 장황하게 추가해줘야한다. 이 떄문에 모든 컨트롤러내의 프로덕션 코드에 가독성을 해친다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;특히 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 번의 치명적인 단점이 Swagger 를 선택하지 않은 결정적인 이유다. 당연하게도, API 명세는 API 스팩과 항상 일치함을 보장하여 일관성, 무결성을 보장해야한다. &lt;strong&gt;필자가 생각하기에 API 자동화 툴을 사용하는 가장 큰 목적 중 하나가 문서화의 실수할 여지를 줄여주는 것이라 생각하는데, 스웨거는 이 장점을 잃어버리게 만든다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Swagger 는 아주 작은 미니 프로젝트, 단기간에 연동 및 릴리즈가 일어나야 하는 프로젝트 (해커톤 등) 에 적합하다. 다시말해, 프로젝트 규모가 커질수록 스웨거는 변동사항에 대해 개발자가 일일이 직접 체크하고 수정해줘야 하기 때문에, 우리 프로젝트에 적합하지 않다고 결론을 내렸다.&lt;/p&gt;
&lt;h2 id=&quot;spring-rest-docs-vs-swagger-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#spring-rest-docs-vs-swagger-1&quot; aria-label=&quot;spring rest docs vs swagger 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Spring Rest Docs (vs Swagger)&lt;/h2&gt;
&lt;p&gt;Spring Rest Docs 는 Swagger 의 단점을 보완한 강력한 API 자동화 툴이라고 생각한다. 어떤점에서 단점을 보완했는가?&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ac47f2c80d8302e2678cc0a4eeb14ff4/73b94/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.987730061349694%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABKUlEQVR42oVSi46EIAzk///y9u529cz54Cmgdp2SGvRMrkkDFGbaKVUpJVqWhbZtYw8h0DzPh8cYjzu8895RnCPHa5M3CgTee4T4whhD4ziS1nr3kaZppJQKGITOOY4x8U4KLBJLUQoHa+2RyTm7AyYGWGuYWKoB6Pvrk17PJ7VtQz9tuycfGJNz4jeqLrfIyoQ2wIvkIh1KQNj3v5wEKoqyM17RxQDo+54JBAg3RtO6rrw6W6rPOV/hpBCUxsNAApDEQSKGPWJQgVUqh0MRE+KAbGIheO4LejgMw6kKED4eH9Q0L/48t/fe6NJnkN/2UCqt13psuq7jFqBCqfY0NleyO/J61kCGqYBjfKDwX8IiPfDM4Sexyh7S6kRX7J9fFqsbXpPXH3hnb8RxXnOuYtwJAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/ac47f2c80d8302e2678cc0a4eeb14ff4/a6d36/image-1.png&quot;
        srcset=&quot;/static/ac47f2c80d8302e2678cc0a4eeb14ff4/222b7/image-1.png 163w,
/static/ac47f2c80d8302e2678cc0a4eeb14ff4/ff46a/image-1.png 325w,
/static/ac47f2c80d8302e2678cc0a4eeb14ff4/a6d36/image-1.png 650w,
/static/ac47f2c80d8302e2678cc0a4eeb14ff4/e548f/image-1.png 975w,
/static/ac47f2c80d8302e2678cc0a4eeb14ff4/73b94/image-1.png 1004w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 작성한 테스트 코드를 기반으로 API 명세가 일어난다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 프로덕션 코드내의 컨트롤러 계층에 문서화를 위한 코드를 추가하지 않아도 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 이 안겨주는 장점은 매우 강력하다. &lt;strong&gt;API 명세가 개발자가 작성한 테스트 코드를 기반으로 일어나기 떄문이다.&lt;/strong&gt; Junit 진영에서 작성한 테스트가 실행되고, 해당 테스트가 성공했을 때 테스트에 대한 AsciiDoc 스닛펫이 생성된다. (스닛펫이란 API 명세가 자동 생성되기 위한 일종의 리소스를 뜻한다.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;테스트 코드를 기반으로 API 명세가 생성되니, 테스트가 성공하는 올바른 프로덕션 코드에 대해서만 API 명세가 제공된다.&lt;/strong&gt; 다시말해, 테스트가 실패하면 API 명세가 생성되지 않는다. 이 떄문에 Swagger 와 달리 &lt;strong&gt;항상 프로뎍션 코드와 일치하는 API 명세를 제공하게 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;단점을 굳이 뽑으라 함은, 아무래도 API 명세를 위한 테스트 작성이 필요하다는 점이다. 테스트 작성이 처음인 사람들에게 아무래도 Swagger 에 비해 러닝커브가 다소 높은것은 사실이다. 하지만, 개발자에게 테스트는 언제나 항상 중요하지 않겠는가? 🤔&lt;/p&gt;
&lt;h3 id=&quot;mockmvc-vs-restassured&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mockmvc-vs-restassured&quot; aria-label=&quot;mockmvc vs restassured permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MockMvc vs RestAssured&lt;/h3&gt;
&lt;p&gt;Spring Rest Docs 를 활용하여 API 문서를 자동화하려면 테스트 코드가 필요하다. 테스트 코드를 작성시, 자바 진영에서 대표적으로 사용되는 툴이 &lt;code class=&quot;language-text&quot;&gt;MockMvc&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;Rest Assured&lt;/code&gt; 이다. 우리 팀은 &lt;code class=&quot;language-text&quot;&gt;MockMvc&lt;/code&gt; 를 사용하게 됐다. 왜 우리 서비스에 &lt;code class=&quot;language-text&quot;&gt;MockMvc&lt;/code&gt; 가 적합하다고 판단하여 이를 선택하게 됐는가?&lt;/p&gt;
&lt;h3 id=&quot;rest-assured&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rest-assured&quot; aria-label=&quot;rest assured permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rest Assured&lt;/h3&gt;
&lt;p&gt;Rest Assured 는 &lt;code class=&quot;language-text&quot;&gt;@SpringBootTest&lt;/code&gt; 와 함께 동작해야한다. &lt;code class=&quot;language-text&quot;&gt;@SpringBootTest&lt;/code&gt; 는 스프링 컨테이너내에 등록된 모든 애플리케이션 빈을 로드하여 빈을 주입하기 때문에, 테스트 수행 속도가 느리다. 하지만, Mocking 이 아닌 실제 객체(Bean) 을 로드하여 테스트 수행하기 때문에 테스트에 대한 신뢰도가 높다.&lt;/p&gt;
&lt;p&gt;정리하자면, &lt;strong&gt;Rest Assured 를 활용한 방식은 @SpringBootTest 로 전 레이어 빈을 로드하기 때문에 테스트가 느리고, 비용이 많이든다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;mockmvc&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mockmvc&quot; aria-label=&quot;mockmvc permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MockMvc&lt;/h3&gt;
&lt;p&gt;반면 MockMvc 는 테스트 환경 구성에 따라 필요한 최소한의 빈만 로드할 수 있다. 이 떄문에 일반적으로 Rest Assured 를 활용한 방식보다 테스트 속도가 빠르다.&lt;/p&gt;
&lt;p&gt;MockMvc 는 &lt;code class=&quot;language-text&quot;&gt;@SpringBootTest&lt;/code&gt; 와 함께 사용할 수도 있고, &lt;code class=&quot;language-text&quot;&gt;@WebMvcTest&lt;/code&gt; 와 함께 사용할 수도 있다. &lt;code class=&quot;language-text&quot;&gt;@WebMvcTest&lt;/code&gt; 는 &lt;code class=&quot;language-text&quot;&gt;@SpringBootTest&lt;/code&gt; 와 달리 프레젠테이션(컨트롤러) 계층의 객체(Bean) 들만 로드한다. 즉, 프레젠테이션 계층을 제외한 나머지 계층의 빈들은 모두 Mocking 한다. 이렇게 각 계층별로 독립적인 계층 하나만을 테스트 하는 기법을 &lt;code class=&quot;language-text&quot;&gt;슬라이스 테스트(Slice Test)&lt;/code&gt; 라고 한다.&lt;/p&gt;
&lt;h3 id=&quot;mockmvc-를-선택한-이유-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mockmvc-%EB%A5%BC-%EC%84%A0%ED%83%9D%ED%95%9C-%EC%9D%B4%EC%9C%A0-&quot; aria-label=&quot;mockmvc 를 선택한 이유  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MockMvc 를 선택한 이유 🤔&lt;/h3&gt;
&lt;p&gt;우리 하모니 팀에서 처음 Rest Docs 를 도입했을 때는 Mocking 에 대한 거부감을 가지고 있었다. 실제 객체를 테스트 해야만 믿음직한 테스트라는 굳은 믿음이 있었기 때문에, &lt;code class=&quot;language-text&quot;&gt;@SpringBootTest&lt;/code&gt; 로 전 레이어의 빈들을 로드하여 테스트했다.&lt;/p&gt;
&lt;p&gt;하지만 시간이 거듭될수록 API 문서를 위한 테스트에는 MockMvc 가 적합하다는 판단이 나왔다. 즉, &lt;code class=&quot;language-text&quot;&gt;MockMvc&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;@WebMvcTest&lt;/code&gt; 기반 &lt;code class=&quot;language-text&quot;&gt;슬라이스 테스트(Slice Test)&lt;/code&gt; 를 적용한 방식으로 마이그레이션했다. 아무래도 프레젠테이션 계층내에 API 명세 관련 테스트가 많이지다보니, 시간이 지날수록 테스트 속도가 저하되었다. &lt;code class=&quot;language-text&quot;&gt;@SpringBootTest&lt;/code&gt; 로 전 레이어 빈을 로드하는 기법은 이미 &lt;code class=&quot;language-text&quot;&gt;Rest Assured&lt;/code&gt; 기반 인수 테스트에서 진행되고 있었기 떄문에, API 명세를 위해 전 레이어를 또 다시 로드하여 테스트할 필요성이 점점 없어졌다.&lt;/p&gt;
&lt;h2 id=&quot;asciidoc-vs-markdown&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#asciidoc-vs-markdown&quot; aria-label=&quot;asciidoc vs markdown permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AsciiDoc vs MarkDown&lt;/h2&gt;
&lt;p&gt;Spring Rest Docs 는 AsciiDoc 을 문서 번역을 위한 텍스트 프로세서로 활용한다. 쉽게말해, &lt;strong&gt;Asciidoc 이란 .adoc 파일을 html 등의 형식으로 변환해주는 툴이다.&lt;/strong&gt; 마크다운 형식의 문서는 이미 대중적으로 사용되고 있는 문서화 포맷이지만, AsciiDoc 에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; 라는 강력한 기능이 있었기에 마크다운은 사용하지 않았다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; 기능이란 다른 asciidoc 문서를 현재 asciidoc 문서로 가져와 명세에 포함시킬 수 있는 기능이다. 즉, 이곳저곳에 흩어진 문서를 한 곳에 모아서 제공하여, 문서화 내용을 한 눈에 파악할 수 있게한다.&lt;/p&gt;
&lt;h2 id=&quot;rest-docs-기반-api-문서-자동화-환경-구축하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rest-docs-%EA%B8%B0%EB%B0%98-api-%EB%AC%B8%EC%84%9C-%EC%9E%90%EB%8F%99%ED%99%94-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&quot; aria-label=&quot;rest docs 기반 api 문서 자동화 환경 구축하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rest Docs 기반 API 문서 자동화 환경 구축하기&lt;/h2&gt;
&lt;p&gt;백문이불여일타. 실제로 Rest Docs 를 활용하여 API 문서 자동화 환경을 구축하는 간단한 환경을 실습해보도록 한다.&lt;/p&gt;
&lt;h3 id=&quot;buildgradle-의존성-추가-및-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#buildgradle-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%B6%94%EA%B0%80-%EB%B0%8F-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;buildgradle 의존성 추가 및 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;build.gradle 의존성 추가 및 설정&lt;/h3&gt;
&lt;p&gt;Rest Docs 도입을 위해 gradle 내에 설정이 필요하다. 이를 위한 방법을 학습해보도록 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;plugins &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	id &lt;span class=&quot;token char&quot;&gt;&apos;java&apos;&lt;/span&gt;
	id &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;boot&apos; version &lt;span class=&quot;token char&quot;&gt;&apos;3.3.2&apos;&lt;/span&gt;
	id &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;asciidoctor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jvm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;convert&apos; version &lt;span class=&quot;token char&quot;&gt;&apos;3.3.2&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

configurations &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	asciidoctorExtensions &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
	compileOnly &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		extendsFrom annotationProcessor
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

ext &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
	snippetsDir &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&apos;build&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;generated&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;snippets&apos;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (3)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

dependencies &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	testImplementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;test&apos;
	asciidoctorExtensions &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;restdocs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;restdocs&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;asciidoctor&apos; &lt;span class=&quot;token comment&quot;&gt;// (4)&lt;/span&gt;
    testImplementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;restdocs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;restdocs&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;mockmvc&apos; &lt;span class=&quot;token comment&quot;&gt;// (5)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

test &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	outputs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dir snippetsDir &lt;span class=&quot;token comment&quot;&gt;// (6)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


asciidoctor &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (7)&lt;/span&gt;
	configurations &apos;asciidoctorExtensions&apos; &lt;span class=&quot;token comment&quot;&gt;// (8)&lt;/span&gt;
	inputs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dir snippetsDir &lt;span class=&quot;token comment&quot;&gt;// (9)&lt;/span&gt;
	dependsOn test &lt;span class=&quot;token comment&quot;&gt;// (10)&lt;/span&gt;
	sources &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;**/index.adoc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;baseDirFollowsSourceFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

tasks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token char&quot;&gt;&apos;build&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	dependsOn asciidoctor
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; asciidoctor 를 위한 플러그인을 적용한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; asciidoctor 를 확장하는 &lt;code class=&quot;language-text&quot;&gt;asciidoctorExtensions&lt;/code&gt; 에 대한 종속성 구성을 선언한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 생성되는 스닛펫들이 생성되는 경로를 설정한다. 이 실습의 경우 명시한바와 같이 &lt;code class=&quot;language-text&quot;&gt;/build/generated-snippets&lt;/code&gt; 에 스니펫이 생성될 것이다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;asciidoctorExtensions&lt;/code&gt; 에  Rest Docs 및 AsciiDoctor 에 대한 의존성을 추가한다. 이 의존성 주입을 통해 &lt;code class=&quot;language-text&quot;&gt;/build/generated-snippets&lt;/code&gt; 에 있는 &lt;code class=&quot;language-text&quot;&gt;.adoc&lt;/code&gt; 파일을 읽어들여 &lt;code class=&quot;language-text&quot;&gt;.html&lt;/code&gt; 파일을 만들어낼 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; MockMvc 를 사용하기 위한 의존성을 추가한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(6)&lt;/code&gt; 생성될 스닛펫들이 &lt;code class=&quot;language-text&quot;&gt;snippetsDir&lt;/code&gt; 경로에 생성되도록 &lt;code class=&quot;language-text&quot;&gt;test&lt;/code&gt; 테스크에 대한 상세 설정을 부여한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(7)&lt;/code&gt; asciidoctor 테스크에 대한 세부 설정을 진행한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(8)&lt;/code&gt; Asciidoctor 확장에 대한 설정을 한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(9)&lt;/code&gt; 로드해올 스닛펫 경로를 &lt;code class=&quot;language-text&quot;&gt;snippetsDir&lt;/code&gt; 로 설정한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(10)&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;test&lt;/code&gt; 테스트가 수행된 후, &lt;code class=&quot;language-text&quot;&gt;asciidoctor&lt;/code&gt; 테스크가 수행되도록 설정한다. 즉, 테스트가 수행되고 난 뒤 asciidoctor 기반 문서화가 진행된다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(11)&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;build&lt;/code&gt; 테스크가 &lt;code class=&quot;language-text&quot;&gt;asciidoctor&lt;/code&gt; 테스크에 의존하도록 설정한다. &lt;code class=&quot;language-text&quot;&gt;./gradlew clean build&lt;/code&gt; 명령 실행시 ascciidoctor 테스크가 수행된 뒤 build 가 수행됨으로, Rest Docs 명세서가 최신화됨을 보장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;restdocs-mockmvc-를-활용한-테스트-코드-작성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#restdocs-mockmvc-%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1&quot; aria-label=&quot;restdocs mockmvc 를 활용한 테스트 코드 작성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RestDocs, MockMvc 를 활용한 테스트 코드 작성&lt;/h3&gt;
&lt;p&gt;이어서 RestDocs, MockMvc 를 활용한 프레젠테이션 레이어내의 테스트 코드를 작성한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@AutoConfigureRestDocs&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@WebMvcTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;MemberController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@ActiveProfiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (3)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberControllerTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (4)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MockMvc&lt;/span&gt; mockvc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@MockBean&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (5)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberService&lt;/span&gt; memberService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;사용자 본인의 회원 정보를 조회하고 상태코드 200을 리턴한다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; 본인의_회원_정보를_조회하고_상태코드_200을_리턴한다&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// given&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;given&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jwtTokenProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMemberId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;anyString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;willReturn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;given&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;memberService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;anyLong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;willReturn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;하온_응답&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// when, then&lt;/span&gt;
        mockMvc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;perform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/member/me&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (6)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Authorization&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Bearer aaaaaa.bbbbbb.cccccc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MediaType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MediaType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;andDo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;andDo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;member/me/success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (7)&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;preprocessRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prettyPrint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;preprocessResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prettyPrint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;requestHeaders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                                &lt;span class=&quot;token function&quot;&gt;headerWithName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Authorization&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;엑세스 토큰&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;responseFields&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                                &lt;span class=&quot;token function&quot;&gt;fieldWithPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;고유 id 값&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;token function&quot;&gt;fieldWithPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;profileImageUrl&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;프로필 이미지 경로&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;token function&quot;&gt;fieldWithPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;nickname&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;성별. 형식: MEN 또는 WOMEN&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;token function&quot;&gt;fieldWithPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;birthday&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;생년월일. 형식:yyyy-MM-dd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;token function&quot;&gt;fieldWithPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;genderType&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;프로필 이미지 경로.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;andExpect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isOk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; Rest Docs 를 위한 어노테이션을 추가하여, 스닛펫이 생성되도록 한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;@WebMvcTest&lt;/code&gt; 를 통해 슬라이스 테스트를 진행한다. API 문서화를 위한 최소한의 빈만 로드하는 것으로, 위 실습의 경우 Member 컨트롤러에 대한 객체만을 로드했다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 테스트 환경내에서 프로파일을 활성화하기 위한 설정을 진행한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; MockMvc 오브젝트를 활용하기 위한 의존성을 &lt;code class=&quot;language-text&quot;&gt;@Autowired&lt;/code&gt; 로 주입받는다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; MemberController 가 의존하고 있는 Bean 객체인 MemberService 를 Mocking 한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(6)&lt;/code&gt; mockMvc 로 API 를 호출한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(7)&lt;/code&gt; 명세할 API 에 대한 상세 설정을 명시한다. 이때 &lt;code class=&quot;language-text&quot;&gt;preprocessRequest&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;preprocessResponse&lt;/code&gt; 를 활용하여 JSON 포맷을 더 깔끔하게 포맷팅하여 문서를 생성해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;adoc-스닛펫-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#adoc-%EC%8A%A4%EB%8B%9B%ED%8E%AB-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;adoc 스닛펫 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;adoc 스닛펫 생성&lt;/h3&gt;
&lt;p&gt;앞서 작성한 테스트를 실행하고 성공했다면, build -&gt; generated-snippets 내에 스닛펫이 생성되었을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/951a944276731dc28bb7599c7c280177/2dc7d/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.44171779141104%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABgklEQVR42p2R3W6jMBCFeY+2AQOJy1/A2MEQB6fJZtO02qqr1Urt+7/I6dhE6U0vml58Or5gPs5oAqMVnvcj9naAVg06WXu0qtEsS/CshJQdVDegEQqt0lCrHkVZIydcZsXyksFaFjiaGqt6QfALXcPRlBwRS1EUFXL6uCwbyhoLniOKUoRRgjCMcXsX4eY29BlYmeJ5neHYc5g2wXhmKxN0ywQsph/pNbUaPJ02kNRSSg1BzbO8woykTu4yGNsUf0yOV1viV7cgUYoNCa0TVjHiZA7RSlRLgZYELt3gLGSYzRjuCP8+E7g2pyHDv4cKT9R0t5pjIyahrlNwXiDLaMWEk5yDOWL3vv+SwLU56Hu8joVvZ0TsV/ZCWnlaZcrv4Bvu1Bx/bUHiBbX7FHYXYfxtgml4OsyWxJvzUX4uFIxaMRzpIL/1HKaJCAbbMjoKu1647RvYvsbeSPw/WbzsFJ62Cg+DgBb5VbJJSIOWpDuj8P5ywNtjj7fTGodReWF4pfADDtBI/pclPdwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/951a944276731dc28bb7599c7c280177/a6d36/image-2.png&quot;
        srcset=&quot;/static/951a944276731dc28bb7599c7c280177/222b7/image-2.png 163w,
/static/951a944276731dc28bb7599c7c280177/ff46a/image-2.png 325w,
/static/951a944276731dc28bb7599c7c280177/a6d36/image-2.png 650w,
/static/951a944276731dc28bb7599c7c280177/e548f/image-2.png 975w,
/static/951a944276731dc28bb7599c7c280177/3c492/image-2.png 1300w,
/static/951a944276731dc28bb7599c7c280177/2dc7d/image-2.png 1760w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;adoc-스닛펫을-api-명세로-표현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#adoc-%EC%8A%A4%EB%8B%9B%ED%8E%AB%EC%9D%84-api-%EB%AA%85%EC%84%B8%EB%A1%9C-%ED%91%9C%ED%98%84&quot; aria-label=&quot;adoc 스닛펫을 api 명세로 표현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;adoc 스닛펫을 API 명세로 표현&lt;/h3&gt;
&lt;p&gt;생성된 adoc 스닛펫들은 아직 한 눈에 파악하기 힘들다. 이곳저곳에 스닛펫들이 흩어져있을 뿐더러, &lt;code class=&quot;language-text&quot;&gt;.adoc&lt;/code&gt; 형식은 우리들에게 익숙하지 않아 명세를 읽기에 불편함을 느낄 수 있다. 이 스닛펫들을 한 곳에 모아두고, 그를 우리에게 익숙한 &lt;code class=&quot;language-text&quot;&gt;.html&lt;/code&gt; 형식으로 예쁘게 표현해보자.&lt;/p&gt;
&lt;p&gt;우선 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; 를 통해 앞서 생성된 adoc 스닛펫을 문서에 끌어모을 수 있다. 이를위해, src -&gt; docs -&gt; asciidoc 내에 폴더를 생성하고, &lt;code class=&quot;language-text&quot;&gt;member.adoc&lt;/code&gt; 파일을 하나 생성한 뒤 아래처럼 asciidoc 문서를 작성해주도록 하자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; ⛳️ &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;회원&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 마이페이지 &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Header&lt;/span&gt; 조회
&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;doctype&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; book
&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;icons&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; font
&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;source&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;highlighter&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; highlightjs
&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;toc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; left
&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;toclevels&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; 회원 정보 조회&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;마이페이지&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;API&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;HTTP&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Request&lt;/span&gt;

include&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;snippets&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;member&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;me&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;http&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;adoc&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Request&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Header&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

include&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;snippets&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;member&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;me&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;request&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;adoc&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;HTTP&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;

include&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;snippets&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;member&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;me&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;http&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;adoc&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Body&lt;/span&gt;

include&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;snippets&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;member&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;me&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;response&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;fields&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;adoc&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;build-수행-및-html-문서-확인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#build-%EC%88%98%ED%96%89-%EB%B0%8F-html-%EB%AC%B8%EC%84%9C-%ED%99%95%EC%9D%B8&quot; aria-label=&quot;build 수행 및 html 문서 확인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;build 수행 및 html 문서 확인&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;asciidoctor&lt;/code&gt; 테스크를 실행하여 html 문서를 생성할 수 있다. 문서는 src -&gt; docs -&gt; asciidoc 경로에 생성한 &lt;code class=&quot;language-text&quot;&gt;.adoc&lt;/code&gt; 파일을 기반으로 생성된다. 앞서 build.gradle 설정시 build 테스크가 asciidoctor 테스크에 의존하도록 설정했으므로, 아래와 같이 빌드 실행시 asciidoctor 테스크가 수행되어 html 명세서 파일이 생성될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;/gradlew clean build&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이후 스프링부트 애플리케이션 실행 후 &lt;code class=&quot;language-text&quot;&gt;http://localhost:8080/docs/index.html&lt;/code&gt; 로 접속하면 HTML 파일이 정상적으로 보이는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/57f98cf71f0266a283b41442a0dc33e6/ac307/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 76.68711656441718%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAABhUlEQVR42p2Ti3KCMBRE+/9/2GpRhAoCihDyBrabKJ2+HG0zs0wmyZxk716ewDEqCVMfYNoTXN9dJHp4rTDPM2Y8Pp7CxzsHQ4g/nzEpDec8/DjGAxFITdP0kCJQKYNtUqLcNqjSPd52O+R5jkPJNappGp5RMMbc1RVosX5tsN2V2KQZMsIC5Hg84nQ6oe/7h2AfwGBZ9hJDd4ag7aHrMHn/xerIEnye31IEOqOhuhaetkIQVg5xzRP6WVLy0oF7fMD3vUUXy1qjrGsMBFouOt7kvPsi6yyKooiy1v7YXxSB0+hh2TpB8zXd30awG8pwt21G3q7ZNlG0tNRjqdcte7cts22y7ICseMOB6QohYrJt28Z5sBgS1CxN0N2UpbR4fi6xekmxSahNgiRJkKZphC7Ah9vGWoeq6lDXgv135uu6GJQh6C+wD+DIUAbRQTIUFdqGbWEDcJB3Lf4KDMXf7/dYrdcosxwiK6CrI3TT/g8YkqyqisFk/Et66LyE6kW0/BdYAL4DSgKPlxXPPssAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/57f98cf71f0266a283b41442a0dc33e6/a6d36/image-3.png&quot;
        srcset=&quot;/static/57f98cf71f0266a283b41442a0dc33e6/222b7/image-3.png 163w,
/static/57f98cf71f0266a283b41442a0dc33e6/ff46a/image-3.png 325w,
/static/57f98cf71f0266a283b41442a0dc33e6/a6d36/image-3.png 650w,
/static/57f98cf71f0266a283b41442a0dc33e6/e548f/image-3.png 975w,
/static/57f98cf71f0266a283b41442a0dc33e6/3c492/image-3.png 1300w,
/static/57f98cf71f0266a283b41442a0dc33e6/ac307/image-3.png 2058w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[자바의 BigDecimal, BigInteger]]></title><description><![CDATA[BigInteger BigInteger 는 int 와 long 원시타입이 표현할 수 있는 범위보다 훨씬 큰 정수를 표현하기 위한 클래스입니다. 특히 long 으로 8Byte…]]></description><link>https://haon.site/haon/java/bigdecimal-biginteger/</link><guid isPermaLink="false">https://haon.site/haon/java/bigdecimal-biginteger/</guid><pubDate>Fri, 16 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;biginteger&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#biginteger&quot; aria-label=&quot;biginteger permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BigInteger&lt;/h2&gt;
&lt;p&gt;BigInteger 는 int 와 long 원시타입이 표현할 수 있는 범위보다 훨씬 큰 정수를 표현하기 위한 클래스입니다. 특히 long 으로 8Byte 나 표현 가능하므로 일반적인 모든 숫자를 대응할 수 있겠지만, 금액과 같은 신중한 값을 표현해야하는 상황에선 혹시모를 상황에 대비해야합니다.&lt;/p&gt;
&lt;h3 id=&quot;생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%9D%EC%84%B1&quot; aria-label=&quot;생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;생성&lt;/h3&gt;
&lt;p&gt;BigInteger 는 long 형으로 다룰수없는 범위를 숫자를 다루기 떄문에, 보통은 문자열로 생성하는 것이 일반적입니다. 단, 비교적 작은 숫자를 생성하고 싶다면 &lt;code class=&quot;language-text&quot;&gt;valueOf()&lt;/code&gt; 정적 팩토리 메소드를 사용하여 int 혹은 long 타입의 값을 전달할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 문자열로 생성&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt; bigInteger1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;99999999999999999999&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// n진수 문자열로 생성 -&gt; n진수 형태로 생성하더라도 10진수로 자동변환되어 표현&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt; bigInteger2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;FFFF&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// valueOf 생성&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt; bigInteger3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10000L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt; bigInteger4 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MAX_VALUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;연산-메소드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%B0%EC%82%B0-%EB%A9%94%EC%86%8C%EB%93%9C&quot; aria-label=&quot;연산 메소드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;연산 메소드&lt;/h3&gt;
&lt;p&gt;BigInteger 는 int, long 와 같은 원시타입이 아니기 떄문에 사칙연산자( +, -, *, / ) 를 사용할 수 없으며, 대신 사칙연산을 위한 메소드를 사용하여 연산 가능합니다. 이때 BigInteger 는 정수를 표현하기 때문에 나숫셈 연산 결과에서 소수점은 버려집니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigInteger1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigInteger2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigInteger1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;substract&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigInteger2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigInteger1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;multiply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigInteger2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigInteger1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;divide&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigInteger2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigInteger1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigInteger2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;연산시-유의사항&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%B0%EC%82%B0%EC%8B%9C-%EC%9C%A0%EC%9D%98%EC%82%AC%ED%95%AD&quot; aria-label=&quot;연산시 유의사항 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;연산시 유의사항&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;또, BigInteger 는 불변성을 가지고있다는 특징이 있습니다. 때문에 연산 메소드를 사용시 객체 내부의 값이 변경되지않고, 연산 결과값을 갖는 새로운 객체가 생성되어 반환됩니다.&lt;/strong&gt; 따라서 성능 저하의 원인이 될 수 있으니 조심해야합니다.&lt;/p&gt;
&lt;h3 id=&quot;형변환&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%98%95%EB%B3%80%ED%99%98&quot; aria-label=&quot;형변환 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;형변환&lt;/h3&gt;
&lt;p&gt;원하는 타입으로도 형변환이 가능합니다. &lt;code class=&quot;language-text&quot;&gt;int&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;long&lt;/code&gt; 뿐만 아니라 &lt;code class=&quot;language-text&quot;&gt;float&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;double&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;String&lt;/code&gt; 형으로도 모두 변환 가능합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt; bigNumber &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; intNumber &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bigNumber&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;intValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; floatNumber &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bigNumber&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floatValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; stringNumber &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bigNumber&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;대소비교&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8C%80%EC%86%8C%EB%B9%84%EA%B5%90&quot; aria-label=&quot;대소비교 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;대소비교&lt;/h3&gt;
&lt;p&gt;사칙연산과 마찬가지로 &quot;==&quot; 와 같은 연산이 불가능하고, &lt;code class=&quot;language-text&quot;&gt;compareTo()&lt;/code&gt; 메소드로 대소비교가 가능합니다. &lt;code class=&quot;language-text&quot;&gt;compareTo()&lt;/code&gt; 에 대한 자세한 설명은 &lt;a href=&quot;https://velog.io/@msung99/JAVA-Comparator-%EA%B3%BC-Comparable-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4&quot;&gt;[JAVA] Comparator 과 Comparable 인터페이스는 어떻게 사용해야할까?&lt;/a&gt; 은 참고해도 좋을듯합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt; num1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt; num2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;compareTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 양수 반환&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;상수&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%81%EC%88%98&quot; aria-label=&quot;상수 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;상수&lt;/h3&gt;
&lt;p&gt;추가적으로 BigInteger 는 아래처럼 자주 사용하는 숫자를 상수로 미리 정의해두었습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ZERO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 0&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ONE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// (TWO THREE ... NINE)&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 10&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;bigdecimal&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bigdecimal&quot; aria-label=&quot;bigdecimal permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BigDecimal&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;BigInteger&lt;/code&gt; 가 정수를 표현하기 위한 것이라면, &lt;code class=&quot;language-text&quot;&gt;BigDecimal&lt;/code&gt; 은 실수를 표현하기 위한 것입니다. &lt;code class=&quot;language-text&quot;&gt;double&lt;/code&gt; 이나 &lt;code class=&quot;language-text&quot;&gt;float&lt;/code&gt; 원시타입들은 대부분의 연산에서 오차를 보이곤하는데, 미세한 값을 사용하거나 값의 오차가 발생하면 안되는 경우에 &lt;code class=&quot;language-text&quot;&gt;BigDecimal&lt;/code&gt; 을 사용하면 유용합니다. 원시타입보단 조금 느리지만, 매우 정밀한 결과값을 보장합니다.&lt;/p&gt;
&lt;h3 id=&quot;생성-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%9D%EC%84%B1-1&quot; aria-label=&quot;생성 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;생성&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;BigDecimal&lt;/code&gt; 은 &lt;code class=&quot;language-text&quot;&gt;BigInteger&lt;/code&gt; 와 달리 원시타입 float, dobule 을 생성자에 바로 넣어 생성할 수 있습니다. 하지만 생성자로 바로 원시타입을 주입하면 오차가 발생할 수 있기때문에, &lt;code class=&quot;language-text&quot;&gt;BigDecimal&lt;/code&gt; 도 문자열로 표현하는 것이 일반적입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 문자열로 생성&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt; bigDecimal1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1.23&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigDecimal1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1.23&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// double 형으로 생성&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt; bigDecimal2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.23&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigDecimal2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1.2300000000000000001928382712&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// int, long 형으로 생성&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt; bigDecimal3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// valueOf 로 생성&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt; bigDecimal4 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;기본-연산&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EB%B3%B8-%EC%97%B0%EC%82%B0&quot; aria-label=&quot;기본 연산 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기본 연산&lt;/h3&gt;
&lt;p&gt;BigInteger 와 마찬가지로 직접적인 연산이 불가능하고, 메소드를 사용해야 합니다. 이때 나눗셈 연산은 주의해야합니다. 나숫셈의 결과가 무한소수라면, &lt;code class=&quot;language-text&quot;&gt;ArithmeticException&lt;/code&gt; 예외가 발생합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;bigDecimal1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigDecimal2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
bigDecimal1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;substract&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigDecimal2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
bigDecimal1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;multiply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigDecimal2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
bigDecimal1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;divide&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigDecimal2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ArithmeticException 예외가 발생할 수도 있다.&lt;/span&gt;
bigDecimal1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remainder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigDecimal2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;올림-내림-반올림-연산&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%AC%EB%A6%BC-%EB%82%B4%EB%A6%BC-%EB%B0%98%EC%98%AC%EB%A6%BC-%EC%97%B0%EC%82%B0&quot; aria-label=&quot;올림 내림 반올림 연산 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;올림, 내림, 반올림 연산&lt;/h3&gt;
&lt;p&gt;이어서 올림, 내림, 반올림 등의 소수점 처리 연산을 제공합니다. &lt;code class=&quot;language-text&quot;&gt;setScale()&lt;/code&gt; 메소드에 반올림할 소수점 아래 자리수와, 소수점 처리 방식을 전달하면 됩니다. 소수점 처리 방식은 &lt;code class=&quot;language-text&quot;&gt;RoundingMode&lt;/code&gt; 라는 클래스의 상수로 정의되어있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt; bigNumber &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1.25&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 올림&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigNumber&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setScale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RoundingMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CEILING&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1.3&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 내림&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigNumber&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setScale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RoundingMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FLOOR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1.2&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 반올림&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigNumber&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setScale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RoundingMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HALF_UP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1.3&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 반내림&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigNumber&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setScale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RoundingMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HALF_DOWN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1.2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;roundingmode-를-활용한-무한대-소수점-처리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#roundingmode-%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%AC%B4%ED%95%9C%EB%8C%80-%EC%86%8C%EC%88%98%EC%A0%90-%EC%B2%98%EB%A6%AC&quot; aria-label=&quot;roundingmode 를 활용한 무한대 소수점 처리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RoundingMode 를 활용한 무한대 소수점 처리&lt;/h3&gt;
&lt;p&gt;앞서 언급했듯이, 나눗셈 연산은 &lt;code class=&quot;language-text&quot;&gt;ArithmeticException&lt;/code&gt; 에 예외가 터지는것에 대한 유의가 필요합니다. 이 문제를 해결하기위해 &lt;code class=&quot;language-text&quot;&gt;divide()&lt;/code&gt; 의 인자로 &lt;code class=&quot;language-text&quot;&gt;RoundingMode&lt;/code&gt; 를 전달하면 예외가 발생하지 않습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt; num1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt; num2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;divide&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RoundingMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CEILING&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;비교연산&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EA%B5%90%EC%97%B0%EC%82%B0&quot; aria-label=&quot;비교연산 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비교연산&lt;/h3&gt;
&lt;p&gt;마찬가지로 &lt;code class=&quot;language-text&quot;&gt;comapreTo()&lt;/code&gt; 를 활용하면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;our&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigDecimal1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;compareTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bigDecimal2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 양수, 0, 음수중에 반환&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;형변환-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%98%95%EB%B3%80%ED%99%98-1&quot; aria-label=&quot;형변환 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;형변환&lt;/h3&gt;
&lt;p&gt;마찬가지로 int, long, float, double, String 등의 다양한 타입으로 형변환이 가능합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt; bigDecimal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000.123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// double -&gt; BigDeicmal&lt;/span&gt;
bigDecimal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;intValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
bigDecimal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floatValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
bigDecimal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;equals-와-compareto-의-구분&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#equals-%EC%99%80-compareto-%EC%9D%98-%EA%B5%AC%EB%B6%84&quot; aria-label=&quot;equals 와 compareto 의 구분 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;equals() 와 compareTo() 의 구분&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt; num1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1.10&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt; num2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigDecimal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prinln&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prinln&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;compareTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 는 BigDecimal 의 &lt;code class=&quot;language-text&quot;&gt;scale()&lt;/code&gt; 값까지 같아야 true 를 반환합니다. 1.10 과 1.1 은 논리적으로 같은 값일지라도, 소수점아래 자릿수가 다르므로 결과는 false 가 나옵니다. 둘을 같은수로 보고싶다면 &lt;code class=&quot;language-text&quot;&gt;compareTo()&lt;/code&gt; 를 활용하면 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://reakwon.tistory.com/156&quot;&gt;DecimalFormat&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Effective Java (Joshua Bloch 지음)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-04-26-BigInteger_BigDecimal/&quot;&gt;https://tecoble.techcourse.co.kr/post/2021-04-26-BigInteger_BigDecimal/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@hgo641/BigDecimal%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90&quot;&gt;https://velog.io/@hgo641/BigDecimal%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/java-biginteger-bigdecimal/&quot;&gt;https://hudi.blog/java-biginteger-bigdecimal/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[트랜잭션의 격리수준과 각 수준의 문제점과 해결방안]]></title><description><![CDATA[💡 본 포스팅은 CS 기술 면접 스터디에도 작성된 글 입니다. 트랜잭션 ACID 트랜잭션 ACID 에 관한 이론은 데이터베이스 트랜잭션(Transaction…]]></description><link>https://haon.site/database/transaction-isolation-level/</link><guid isPermaLink="false">https://haon.site/database/transaction-isolation-level/</guid><pubDate>Wed, 14 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 본 포스팅은 &lt;a href=&quot;https://github.com/kakaotech-25/cs-plant-interview&quot;&gt;CS 기술 면접 스터디&lt;/a&gt;에도 작성된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;트랜잭션-acid&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-acid&quot; aria-label=&quot;트랜잭션 acid permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 ACID&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;트랜잭션 ACID 에 관한 이론은 &lt;a href=&quot;http://localhost:8000/database/transaction/&quot;&gt;데이터베이스 트랜잭션(Transaction)&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;자바 기반의 스프링부트 웹 애플리케이션은 기본적으로 요청에 대해 트랜잭션 단위의 안전한 처리를 요구한다. 트랜잭션은 &lt;a href=&quot;https://haon.blog/database/transaction/&quot;&gt;ACID&lt;/a&gt; 라는 성질을 지니고 있다. 원자성(Atomic), 일관성(Consistency), 고립성(Isolation), 지속성(Durability) 라는 4가지 속성이 만족되었을 때 해당 연산은 하나의 트랜잭션 단위라고 볼 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;트랜잭션-격리수준isolation-level&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80isolation-level&quot; aria-label=&quot;트랜잭션 격리수준isolation level permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 격리수준(Isolation level)&lt;/h2&gt;
&lt;h3 id=&quot;격리수준은-왜-등장했는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80%EC%9D%80-%EC%99%9C-%EB%93%B1%EC%9E%A5%ED%96%88%EB%8A%94%EA%B0%80&quot; aria-label=&quot;격리수준은 왜 등장했는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;격리수준은 왜 등장했는가?&lt;/h3&gt;
&lt;p&gt;트랜잭션은 각 데이터베이스 시스템내에서 격리수준에 의해 동작한다. 또한 각 트랜잭션은 스프링 웹 애플리케이션과 같은 멀티 쓰레드 환경일 경우, 각 쓰레드에 의해 트랜잭션이 생성된다. 다시말해, 멀티 쓰레드환경에서 동시간대에 트랜잭션이 생성될 수 있으며, 생성된 여러 트랜잭션들이 동시간대에 공유 자원에 접근하여 동시성 이슈가 발생할 수 있다.&lt;/p&gt;
&lt;p&gt;트랜잭션 격리수준은 이를 해결하기 위해 등장했다. &lt;strong&gt;즉, 격리수준은 트랜잭션의 ACID 를 보장하면서도 각 트랜잭션간의 공유 자원 접근으로 인한 동시성 제어, 동시성 제어시 성능 저하를 최소화하기 위해 등장했다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;격리수준-4단계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80-4%EB%8B%A8%EA%B3%84&quot; aria-label=&quot;격리수준 4단계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;격리수준 4단계&lt;/h3&gt;
&lt;p&gt;격리수준은 크게 4가지로 분류된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;READ UNICOMMITED (커밋되지 않은 읽기)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;READ COMMITED (커밋된 읽기)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;REPREATABLE READ (반복 가능한 읽기)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SERIALIZABLE (직렬화 기능)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;READ UNCOMMITED 부터 시작하여 SERIALIZABLE 방향으로 내력갈 수록 더 엄격하고도 안전한 격리가 보장된다. 다시말해, ACID 성질 중 &lt;code class=&quot;language-text&quot;&gt;고립성(Isolation)&lt;/code&gt; 이 더욱 안전하게 보장된다. 하지만 격리수준이 높은 데이터베이스 시스템일수록 처리 성능이 떨어진다는 치명적인 단점이 존재하며, 마구잡이로 격리수준을 설정했을 때 데드락(Deadlock) 에 빠질 위험이 존재하니, 각 애플리케이션에 적절한 격리수준을 설정하는 것이 적합하다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 데이터 정합성(안전한 고립성) 과 성능은 반비례한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;격리수준에-따라-발생할-수-있는-문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80%EC%97%90-%EB%94%B0%EB%9D%BC-%EB%B0%9C%EC%83%9D%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;격리수준에 따라 발생할 수 있는 문제점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;격리수준에 따라 발생할 수 있는 문제점&lt;/h2&gt;
&lt;p&gt;트랜잭션 격리수준을 바로 알아보기전, 각 격리수준에서 발생할 수 있는 문제점이 존재한다. 이 문제점을 간단히 사전 지식으로 학습하고, 격리수준을 상세히 알아보도록 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;더티 리드 (Dirty Read)&lt;/code&gt; : 더티 리드란 특정 트랜잭션에 의해 데이터가 변경되었지만, &lt;strong&gt;아직 커밋되지 않은 상황에서 다른 트랜잭션이 해당 변경 사항을 읽어와버리는 문제&lt;/strong&gt;를 뜻한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;반복 불가능한 조회 (Non-Repeatable Read)&lt;/code&gt; : &lt;strong&gt;같은 트랜잭션 내에서 같은 데이터를 여러번 조회했을 떄 읽어온 데이터가 다른 경우&lt;/strong&gt;를 뜻한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;팬텀 리드 (Phantom Read)&lt;/code&gt; : Non-Repeatable Read 의 한 종류로, 일겅온 결과가 새로운 행이 생겼거나 또는 없어진 현상을 뜻한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;read-uncommited-커밋되지-않은-읽기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#read-uncommited-%EC%BB%A4%EB%B0%8B%EB%90%98%EC%A7%80-%EC%95%8A%EC%9D%80-%EC%9D%BD%EA%B8%B0&quot; aria-label=&quot;read uncommited 커밋되지 않은 읽기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;READ UNCOMMITED (커밋되지 않은 읽기)&lt;/h2&gt;
&lt;p&gt;가장 낮은 격리수준이다. 한 트랜잭션이 아직 커밋되지 않은 상태임에도 불구하고, &lt;strong&gt;다른 트랜잭션에서 커밋도 안된 해당 변경사항을 읽어와버릴 수 있는 격리수준&lt;/strong&gt;이다. 데이터 정합성이 깨질 가능성이 매우 높은 격리수준이지만, 성능은 가장 빠르다. 데이터를 일관성이 꺠져도 전혀 문제가 없는 연산에서 사용하기에 적합하다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e10c59c72fa5fdadca48ba9ad918df9d/c0566/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.079754601226995%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA/klEQVR42nWSCQrAMAgE8/9vFnrf920ZQUgCFULTaGdXU/e+rxD3fcswDNI0jazrKs/zCLl4Eed5Stu2Wnsch/jhbEPxNE3SdZ3s+x4UGcjiui4Zx1FrgQdADgBs26bO5nlWd5yZOkJAOOeJAPllWSQOhxKtpmmqLfCOCAIsADgBzh4QQHIG9DtwOGJlWSZ93+vHVVXpjPwnMD8AY8ScIqBAf9hcDFAgSZJIURT6Xte15vzAMUJ0ZLXBpRDMCACqzBNV2i/LUvI8VzB7QHTCooYcJn6BdovscQbI4IiRR9CfoXUZAPkYEEk+ZrG338POid9Lif83XMaBm3iGf7Uf6ZgNzyk1DKMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e10c59c72fa5fdadca48ba9ad918df9d/a6d36/image.png&quot;
        srcset=&quot;/static/e10c59c72fa5fdadca48ba9ad918df9d/222b7/image.png 163w,
/static/e10c59c72fa5fdadca48ba9ad918df9d/ff46a/image.png 325w,
/static/e10c59c72fa5fdadca48ba9ad918df9d/a6d36/image.png 650w,
/static/e10c59c72fa5fdadca48ba9ad918df9d/e548f/image.png 975w,
/static/e10c59c72fa5fdadca48ba9ad918df9d/3c492/image.png 1300w,
/static/e10c59c72fa5fdadca48ba9ad918df9d/c0566/image.png 1544w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;트랜잭션 A 와 B 가 동시에 수행되고 있다. 트랜잭션 A 에서 홍길동의 이름을 홍길동 2로 변경했으며, 아직 커밋 &amp;#x26; 롤백을 하지 않았다.&lt;/p&gt;
&lt;p&gt;이때 트랜잭션 B 가 아직 커밋되지 않은 A 의 변경 내용을 읽어와버리면 어떻게 될까? 불행하게도 이 격리수준에선 변경된 이름을 읽어와버릴 수 있다. 즉, 트랜잭션 B 는 트랜잭션 A 에서 아직 커밋되지 않은 내용을 조회할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;발생할-수-있는-문제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%9C%EC%83%9D%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%AC%B8%EC%A0%9C&quot; aria-label=&quot;발생할 수 있는 문제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;발생할 수 있는 문제&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;더티 리드&lt;/li&gt;
&lt;li&gt;Non-Repeatable Read&lt;/li&gt;
&lt;li&gt;팬텀 리드&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;read-commited&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#read-commited&quot; aria-label=&quot;read commited permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;READ COMMITED&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;한 트랜잭션의 변경내용중에 커밋이 완료된 데이터만 다른 트랜잭션에서 조회 가능한 격리수준&lt;/strong&gt;이다. 즉, 트랜잭션이 수행되는동안 다른 트랜잭션에서 해당 데이터의 커밋되지 않고, 변경사항이 생기기 이전의 내용을 읽어온다. 가장 많이 사용되는 격리수준으로, Oracle DB 등에서 기본값으로 설정되어있다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c4b9ef468b41a5b2709bc45500a021af/2a333/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.46625766871166%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABHElEQVR42n2Sh6rEQAhF5///MRDSe+/NxxmebLKBHZBM0aNeY+R/tW0rvu9LWZayrqv8WsuySJIk1n8cx8eb0c22bRY6TZOc5/lwuq7rcT6OQ4ZhkK7rZN/3J5BsQOZ5tg7AcOKOQIxEQKkcf91/V2eBfd9LGIbWCMzzXLIss5amqT1jmohO2AMmlsVZuzBUhh4EAyyKQoIgEMdx7F0cx1JV1asSKuSeeMB0B9Tc6QBxIDsO2iLVUyUDIwnfpmnsHT4UwT0scxcdCEBaIwgYZwLRFM0YhmqOLICZtuu6H6AugGhEAh5VNyShCloEwlf1Bl7XtX1/AdGCqd4XTlSrcFokISDgdBFFkXie9wbqBL+BKria/ovAiAGOMZQ/jU4Jak+QGsMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/c4b9ef468b41a5b2709bc45500a021af/a6d36/image-1.png&quot;
        srcset=&quot;/static/c4b9ef468b41a5b2709bc45500a021af/222b7/image-1.png 163w,
/static/c4b9ef468b41a5b2709bc45500a021af/ff46a/image-1.png 325w,
/static/c4b9ef468b41a5b2709bc45500a021af/a6d36/image-1.png 650w,
/static/c4b9ef468b41a5b2709bc45500a021af/e548f/image-1.png 975w,
/static/c4b9ef468b41a5b2709bc45500a021af/3c492/image-1.png 1300w,
/static/c4b9ef468b41a5b2709bc45500a021af/2a333/image-1.png 1484w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이전과 달리, 커밋되기 전에는 변경사항이 발생하기 이전의 값인 &quot;홍길동&quot; 이 그대로 조회되며, 커밋된 이후에는 &quot;홍길동2&quot; 가 조회되어 &lt;code class=&quot;language-text&quot;&gt;더티 리드&lt;/code&gt; 가 해결되었다. 하지만 문제점은, 트랜잭션 B 내에서 트랜잭션 A 의 커밋 이전/이후로 다른 데이터가 조회된다는 것이다.&lt;/p&gt;
&lt;h3 id=&quot;발생할-수-있는-문제-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%9C%EC%83%9D%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%AC%B8%EC%A0%9C-1&quot; aria-label=&quot;발생할 수 있는 문제 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;발생할 수 있는 문제&lt;/h3&gt;
&lt;p&gt;특정 트랜잭션에서 데이터가 변경되었으나, 아직 커밋되지 않은 상태라면 다른 트랜잭션에선 해당 데이터에 접근했을 떄 트랜잭션 시작 전 데이터 값을 읽어온다. 커밋된 이후에서야 변경된 데이터를 읽어올 수 있다. 이 떄문에 팬텀 리드와 반복 불가능한 조회 문제가 발생한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Non-Repeatable Read&lt;/li&gt;
&lt;li&gt;팬텀 리드&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;repeatable-read&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#repeatable-read&quot; aria-label=&quot;repeatable read permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;REPEATABLE READ&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;트랜잭션이 시작되기 전에 커밋된 내용에 대해서만 조회할 수 있는 격리수준&lt;/strong&gt;이다. 트랜잭션이 완료될 때 까지 SELECT 쿼리가 사용되는 모든 데이터에 &lt;code class=&quot;language-text&quot;&gt;공유 락(Shared Lock)&lt;/code&gt; 이 걸리는 수준이다. &lt;strong&gt;트랜잭션 범위 내에서 조회한 내용이 항상 동일함을 보장하며, 다른 트랜잭션은 트랜잭션 영역에 해당되는 데이터에 대한 수정이 불가능&lt;/strong&gt;하다.&lt;/p&gt;
&lt;p&gt;특정 행을 조회시 항상 같은 데이터를 응답하는 것을 보장하는 격리수준이다. 하지만, SERIALIABLE 과 다르게 행이 추가되는 것을 막지는 않는다. 이 때문에 팬텀 리드가 발생할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;발생할-수-있는-문제-2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%9C%EC%83%9D%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%AC%B8%EC%A0%9C-2&quot; aria-label=&quot;발생할 수 있는 문제 2 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;발생할 수 있는 문제&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;팬텀 리드&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;serializable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#serializable&quot; aria-label=&quot;serializable permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SERIALIZABLE&lt;/h2&gt;
&lt;p&gt;한 트랜잭션에서 사용되고 있는 데이터는 다른 트랜잭션에서 절대 접근할 수 없는 격리수준이다. 즉, 특정 트랜잭션이 사용중인 테이블의 모든 행을 다른 트랜잭션이 접근할 수 없도록 잠근다. 이 격리수준에선 단순 SELECT 쿼리만 실행되더라도, DB Lock 이 걸려서 다른 트랜잭션에 의해 데이터에 접근할 수 없게된다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/44cecdd9aaf79cc7888b1c70230566ed/95e59/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 69.93865030674846%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAABKUlEQVR42pWTiaqEMAxF/f+fVFFwwX3f8ziFDOpYeRMIdomn6U3qdF0nRVFIkiTSNI1M0yT/NWL5J4oiyfNc+r4Xp21bA/N9X+q6/gk4jqNUVSWu6xoGyTn7vsu6rgZ0HIfxu23bJsTdjVjW+Zcv7rAxz7NkWWaCWCR1TucgDEmGYbBmyj4M7AJED7Qoy9I4a4DiOH4FEnsBLssiaZoaGI7IQRAYEIeEYfgbkAlAtOKaXIE5Y/wMZI4kqpsVSGZaAC0OY4w9qqiHeZ73kUHl+gISqJU7VxHj2gCRgswZA2TO3mNR3oD0JxB6FieW/gNGNzxmiA53oBpAmlb3ief6GvulIQJrH9qAFOK8r02tGl+AWjkbkOu9tQ3ZK9y5n2Z7s5rB09MjIX16f2fARuzCMRwIAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/44cecdd9aaf79cc7888b1c70230566ed/a6d36/image-2.png&quot;
        srcset=&quot;/static/44cecdd9aaf79cc7888b1c70230566ed/222b7/image-2.png 163w,
/static/44cecdd9aaf79cc7888b1c70230566ed/ff46a/image-2.png 325w,
/static/44cecdd9aaf79cc7888b1c70230566ed/a6d36/image-2.png 650w,
/static/44cecdd9aaf79cc7888b1c70230566ed/e548f/image-2.png 975w,
/static/44cecdd9aaf79cc7888b1c70230566ed/3c492/image-2.png 1300w,
/static/44cecdd9aaf79cc7888b1c70230566ed/95e59/image-2.png 1460w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;발생할-수-있는-문제-3&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%9C%EC%83%9D%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%AC%B8%EC%A0%9C-3&quot; aria-label=&quot;발생할 수 있는 문제 3 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;발생할 수 있는 문제&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;다른 트랜잭션이 현재 트랜잭션에 해당되는 데이터에 대한 조회 및 수정 모두 불가능 (데드락 위험)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mangchhe.github.io/db/2021/12/30/TransactionIsolationLevel/&quot;&gt;https://mangchhe.github.io/db/2021/12/30/TransactionIsolationLevel/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/transaction-isolation-level/&quot;&gt;https://hudi.blog/transaction-isolation-level/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[페이징과 세그먼테이션은 어떻게 가상 메모리를 관리할까?]]></title><description><![CDATA[💡 본 포스팅은 CS 기술 면접 스터디에도 작성된 글 입니다. 겉보기에 프로세스들은 특별한 내부 동작없이 단순하게 메모리내에서 연속적으로 배치되는 구조처럼 보일 수 있다. 하지만, 프로세스는 연속적으로 메모리에 배치될 때 스와핑(Swapping…]]></description><link>https://haon.site/cs/os/page-segmentation/</link><guid isPermaLink="false">https://haon.site/cs/os/page-segmentation/</guid><pubDate>Mon, 12 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 본 포스팅은 &lt;a href=&quot;https://github.com/kakaotech-25/cs-plant-interview.git&quot;&gt;CS 기술 면접 스터디&lt;/a&gt;에도 작성된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;겉보기에 프로세스들은 특별한 내부 동작없이 단순하게 메모리내에서 연속적으로 배치되는 구조처럼 보일 수 있다. 하지만, 프로세스는 연속적으로 메모리에 배치될 때 스와핑(Swapping), 단편화(Fragmentation) 이라는 고려사항들을 신경쓰며 배치된다.&lt;/p&gt;
&lt;h2 id=&quot;스와핑swapping&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%EC%99%80%ED%95%91swapping&quot; aria-label=&quot;스와핑swapping permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스와핑(Swapping)&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0636f50b5ece76976c3a24815d15b15a/536c7/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.963190184049076%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABkElEQVR42qWSW2sTURSF+2N9EMGfIvRFVDRaBO+NSC2+NQ9NKdqYpjaXzhxLMi3p5EwuZg7J3C+fJ9p4aRtDccF5OJvNx1p77xWuoSgB10v5Nk1RfkqSXu5ZuVjI8pxcv4tKshzTcjntTuhJn9MzD3sULAf+pT/gaZqzXh9w79DlqQgoGD4H8icwXwTUiXhdKdFzjHO3GcQxKEXm+awbikc1SbHhUGi6VGV4NXBeCDXgZuEOdbNM12kxUDbDzgmjvU84LYO17SZvP5co7mzw3mhSWwScS3vhtgY2j8t40ZQw9n40R3p+UZKxqZ1VRBvrpMOu1edwUeR5IdYObz1+wJvSR97t1DnoSOyepCq6fLVsnlcstloDGkLywVTsL4s8c3jjySq7jT2O5YT+JCSJI5IkJghDinWHF4bLS6F4Jabs95YsJUoz7m48o2NVz5ec/T6nLNPAIatfFPdFxMMjn5rt/xs4O5GJqwiC+FLj7CPOFGWzz/aRpNIe406Ta97hFRoPB7SFyXg0+mXiv4DL9B03DJViZ45w4QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/0636f50b5ece76976c3a24815d15b15a/a6d36/image.png&quot;
        srcset=&quot;/static/0636f50b5ece76976c3a24815d15b15a/222b7/image.png 163w,
/static/0636f50b5ece76976c3a24815d15b15a/ff46a/image.png 325w,
/static/0636f50b5ece76976c3a24815d15b15a/a6d36/image.png 650w,
/static/0636f50b5ece76976c3a24815d15b15a/e548f/image.png 975w,
/static/0636f50b5ece76976c3a24815d15b15a/3c492/image.png 1300w,
/static/0636f50b5ece76976c3a24815d15b15a/536c7/image.png 1480w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;메모리에 적재된 프로세스들 중에는 현재 실행되지 않는 프로세스가 존재할 수 있다. 입출력 작업을 위해 대기 상태에 빠진 프로세스라던지, 오랫동안 사용되지 않는 프로세스가 이런 프로세스들에 속한다.  이렇듯 현재 (당장에) 실행되지 않는 프로세스들을 잠시 보조기억장치 일부 영역으로 쫓아내고, 그렇게 생긴 메모리상의 빈 공간에 또 다른 프로세스들을 가져와서 실행하는 방식을 &lt;code class=&quot;language-text&quot;&gt;스와핑(Swapping)&lt;/code&gt; 이라고 한다.&lt;/p&gt;
&lt;p&gt;스와핑을 이용하면 프로세스들이 요구하는 메모리 주소 공간의 크기가 실제 메모리 크기보다 큰 경우에도 프로세들을 동시에 실행 가능하다.&lt;/p&gt;
&lt;h2 id=&quot;메모리-할당&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%94%EB%AA%A8%EB%A6%AC-%ED%95%A0%EB%8B%B9&quot; aria-label=&quot;메모리 할당 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메모리 할당&lt;/h2&gt;
&lt;p&gt;스와핑 구조를 통해 메모리내에 새로운 빈 공간에 생기고, 그렇게 생긴 빈 공간이 여러개라면 프로세스를 어디에 배치해야할까? 이에 대한 3가지 기법이 있다.&lt;/p&gt;
&lt;p&gt;아래와 같은 메모리 공간에 2칸의 공간을 차지하는 데이터는 저장하는 상황을 가정해보자.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d782d0de1ed0f0466d970eaeb6c18bf7/2a333/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 12.269938650306749%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAYAAABYBvyLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAi0lEQVR42i2MywrCMBRE+/9fISi4EQoKbgVBVLC2O91YUIt9GJKa3ITujvGxG2bOmWR16hjvKhZ5hfeBvu8ZgufSGGZZjdIm9oJz7ruJeAbvOJSK0eZGmjd06seICMn63DLdVyyLe5QEbQxBHGWtSY+PCGsknllrMXGzMQf3IiufTLZX5kVN+2c+/htc6pMSqDAHOQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d782d0de1ed0f0466d970eaeb6c18bf7/a6d36/image-1.png&quot;
        srcset=&quot;/static/d782d0de1ed0f0466d970eaeb6c18bf7/222b7/image-1.png 163w,
/static/d782d0de1ed0f0466d970eaeb6c18bf7/ff46a/image-1.png 325w,
/static/d782d0de1ed0f0466d970eaeb6c18bf7/a6d36/image-1.png 650w,
/static/d782d0de1ed0f0466d970eaeb6c18bf7/e548f/image-1.png 975w,
/static/d782d0de1ed0f0466d970eaeb6c18bf7/3c492/image-1.png 1300w,
/static/d782d0de1ed0f0466d970eaeb6c18bf7/2a333/image-1.png 1484w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;first-fit&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#first-fit&quot; aria-label=&quot;first fit permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;First Fit&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c1175fe70b0420569152c44ed3fadee9/b1ffc/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 11.042944785276074%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAYAAABYBvyLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAkUlEQVR42h2MPQuCUAAA/cnNQWs/pCEaJIKCCBpLEDOpMEKcKhS13nu9D3G7yu04jvP8Xco0qlhHB1w2QpwHdM2eW+GYx0+kVFhr0FqjlMJYS2s+BFnNJLizOpW8hfg1Fusc3iK44sc122OCy8fIdEj3CsnKlmVSIJXqY2NMP/xzazVh3jALH2wuFULK3jvn+AID6pAYWEMNNwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/c1175fe70b0420569152c44ed3fadee9/a6d36/image-2.png&quot;
        srcset=&quot;/static/c1175fe70b0420569152c44ed3fadee9/222b7/image-2.png 163w,
/static/c1175fe70b0420569152c44ed3fadee9/ff46a/image-2.png 325w,
/static/c1175fe70b0420569152c44ed3fadee9/a6d36/image-2.png 650w,
/static/c1175fe70b0420569152c44ed3fadee9/e548f/image-2.png 975w,
/static/c1175fe70b0420569152c44ed3fadee9/3c492/image-2.png 1300w,
/static/c1175fe70b0420569152c44ed3fadee9/b1ffc/image-2.png 1492w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;최초 적합(First Fit) 은 OS 가 메모리내의 빈 공간을 앞에서부터 &lt;code class=&quot;language-text&quot;&gt;순차대로&lt;/code&gt; 검색하다가 적재할 수 있는 공간을 발견하면 그 공간에 프로세스를 배치하는 방식이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;장점 : 프로세스가 적재될 수 있는 공간을 발견하는 즉시 메모리를 할당하므로, 검색을 최소화할 수 있고 결과적으로 빠른 할당이 가능하다.&lt;/li&gt;
&lt;li&gt;단점 : Best Fit 방식에 비해 메모리 공간에 낭비가 심해질 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;best-fit&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#best-fit&quot; aria-label=&quot;best fit permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Best Fit&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c29e4449a04ffb9c6b10ff6fc9d8c7db/e4ee8/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 15.950920245398773%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAtklEQVR42j2OO27CUBBFvbVsgSVRZAUpqNKwAiQkypCSIiAkilQGBAT5Gft9ZsaQNCdPBlEczdXVPdIUdTB2VeBYB1QVEelvGxKHOpKSPPuenC1Ttan3TnWLNN9Ys+FmjmKy/OF1VjL6KPtxjIlbp6y3FW/zA2d3wezee+9JovxdlenqxHC65f1zhVsMsK8XZD+m8HLl3CRcK1m0JzGLldf7R49OH7nLNFGzJzgf0bDDQslv5/kHrJnesKMa62UAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/c29e4449a04ffb9c6b10ff6fc9d8c7db/a6d36/image-3.png&quot;
        srcset=&quot;/static/c29e4449a04ffb9c6b10ff6fc9d8c7db/222b7/image-3.png 163w,
/static/c29e4449a04ffb9c6b10ff6fc9d8c7db/ff46a/image-3.png 325w,
/static/c29e4449a04ffb9c6b10ff6fc9d8c7db/a6d36/image-3.png 650w,
/static/c29e4449a04ffb9c6b10ff6fc9d8c7db/e548f/image-3.png 975w,
/static/c29e4449a04ffb9c6b10ff6fc9d8c7db/3c492/image-3.png 1300w,
/static/c29e4449a04ffb9c6b10ff6fc9d8c7db/e4ee8/image-3.png 1496w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;최적 적합(Best Fit) 은 빈 공간을 모두 검색해 본 후, 프로세스가 적재될 수 있는 공간 중 &lt;code class=&quot;language-text&quot;&gt;가장 작은 공간&lt;/code&gt;에 프로세스를 배치하는 방식이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;장점 : 메모리 낭비가 가장 적다.&lt;/li&gt;
&lt;li&gt;단점 : 처음부터 끝까지 모두 탐색하여 최적의 공간을 찾아내야 하므로, 시간이 오래걸린다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;worst-fit&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#worst-fit&quot; aria-label=&quot;worst fit permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Worst Fit&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e495a277b10f2475e6f05917c50cbab3/6f278/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 11.65644171779141%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAYAAABYBvyLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlElEQVR42h3JuwqCUACAYV+3Z+gBeoK2lrYarKFFwiVCCClKJMqCFFM65OXcdP67rN/nuNuYaZCzCDOU1tR1TWcUybNhtrsjoiE6HiCzOU3boZTEqpbgKphsHrj7AvGu0FphrMXxwgur0ws/Lv8gpaS3hlRIvCinSsbY2whdrpG6xxjzfc0xrVgeCvyzoG5a7M/7ng/gmo9vFr8xPQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e495a277b10f2475e6f05917c50cbab3/a6d36/image-4.png&quot;
        srcset=&quot;/static/e495a277b10f2475e6f05917c50cbab3/222b7/image-4.png 163w,
/static/e495a277b10f2475e6f05917c50cbab3/ff46a/image-4.png 325w,
/static/e495a277b10f2475e6f05917c50cbab3/a6d36/image-4.png 650w,
/static/e495a277b10f2475e6f05917c50cbab3/e548f/image-4.png 975w,
/static/e495a277b10f2475e6f05917c50cbab3/3c492/image-4.png 1300w,
/static/e495a277b10f2475e6f05917c50cbab3/6f278/image-4.png 1488w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;최악 적합(Worst Fit) 은 OS 가 빈 공간을 모두 검색해 본 후, 프로세스가 적재될 수 있는 공간 중 가장 큰 공간에 프로세스를 배치하는 방식이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;단점 : Best Fit 방식처럼 처음부터 끝까지 모두 탐색해야 하므로 시간이 오래걸린다. 또한 메모리 낭비까지 심하기 떄문에 가장 사용되지 않는 방식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;외부-단편화-exteral-fragmentation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%B8%EB%B6%80-%EB%8B%A8%ED%8E%B8%ED%99%94-exteral-fragmentation&quot; aria-label=&quot;외부 단편화 exteral fragmentation permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;외부 단편화 (Exteral Fragmentation)&lt;/h2&gt;
&lt;p&gt;프로세스를 메모리에 연속적으로 배치하는 연속 메모리 할당은 사실 효율적으로 사용하는 방법이 아니다. 연속 메모리 할당은 &lt;code class=&quot;language-text&quot;&gt;외부 단편화&lt;/code&gt; 라는 문제를 내포하고 있기 때문이다. 외부 단편화란 &lt;strong&gt;프로세스를 할당하기 어려울만큼 작은 메모리 공간들로 인해 메모리가 낭비되는 현상&lt;/strong&gt;을 의미한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/43a26bd116fc96ea0f8fed9e9ce9ce14/133ae/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.87116564417178%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACRElEQVR42pWS30tTYRjHz0V0U/9CF90E3QQSUVFRCUZRSITd5EUIZV1kiEY/iIKITHOuldoPKpLUgrqIhEpWNnVITt10m8sCCSfqtuPW5rbjOZtn59OrzuFNMR/4vOd53/fw5Xm+zytN1xQhX9uKfHM7s7f3MHtrJ/KNAkLVmwl31LMURoZ8Qwo2nSFce4ywqYRI42killOE608I8SKiX16sXfDP2HNi3gaBmZj7HjFPPbHRB0TdtSSme7K/GfkLqt2boEvC6F4PfRvBvgFs68AqofqqlqUMPX/BhLOE1MButMH9goOCA2hDgv4ClN9NLDZrrEUwqWVQF2A+zdJ3NUlVR9eX/TOEj//GWNWyls65JMshYtFo7jKuaPhnAkTm4vlXOJdQSc6nSCgpYiJf2Sui5Iii4hz8gPy1g4HgZ2z+Fnom2+j2vxK00jvZzjf/S36EHLnhSVefeDhnclFhGaaq2UNlo5sL94cpqxnkrT0AMxOMv2nmkecstcLnhqEjWFzFmF1HMTkPc8exl45fjSyanV7QkIqqetlW1sWu8zYKK+3sq+hhR7mNLaVW6trHsq3HeeYux+Q6xMOR4zz1ltLsOYllpJg6ZyGfxh+jxHWCoQCSzRmksz8gmKHTEVzC6gjwsW+Ksal5tNAEP83Xeee7S5uvmnbfZVpHq0V+ide+K7R4L2KfeJ8bnKSlFrIbg7SmoIuyVyKp6Xz3Wpkdsuf9tiVFTZMRYnrGIBKJEhMTFSkZsSTFYGRxFo0nsu/gfywL/gXGxJ34GpK7sQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/43a26bd116fc96ea0f8fed9e9ce9ce14/a6d36/image-5.png&quot;
        srcset=&quot;/static/43a26bd116fc96ea0f8fed9e9ce9ce14/222b7/image-5.png 163w,
/static/43a26bd116fc96ea0f8fed9e9ce9ce14/ff46a/image-5.png 325w,
/static/43a26bd116fc96ea0f8fed9e9ce9ce14/a6d36/image-5.png 650w,
/static/43a26bd116fc96ea0f8fed9e9ce9ce14/e548f/image-5.png 975w,
/static/43a26bd116fc96ea0f8fed9e9ce9ce14/3c492/image-5.png 1300w,
/static/43a26bd116fc96ea0f8fed9e9ce9ce14/133ae/image-5.png 1424w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위 상황에선 프로세스 C가 스와핑이 되어 메모리에 적재되어야 하지만, 공간 여유가 마땅치않아 적재되지 못하고 있는 상황이다. 빈 공간 2개는 더했을 때 총 100MB 로, 프로세스 C가 80MB 로 더 작음에도 불구하고 할당되지 못하고 있다. 이렇듯 외부 단편화 문제는 이곳저곳에 작게 흩어진 빈 공간들이 많아져서, 낭비되는 공간이 많아지는 현상을 야기한다.&lt;/p&gt;
&lt;h3 id=&quot;해결안1-메모리-압축-compaction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B4%EA%B2%B0%EC%95%881-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%95%95%EC%B6%95-compaction&quot; aria-label=&quot;해결안1 메모리 압축 compaction permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;해결안1. 메모리 압축 (Compaction)&lt;/h3&gt;
&lt;p&gt;외부 단편화를 해결할 수 있는 대표적인 방안으로 메모리를 압축하는 방법이 있다. 압축이란 여기저기 흩어져 있는 작은 빈 공간들을 하나의 공간으로 모으는(압축하는) 방식을 뜻한다.&lt;/p&gt;
&lt;p&gt;하지만 압축 방식은 단점이 많다. 작은 빈 공간들을 하나로 모으는 동안 시스템은 하던 일을 중지해야하고, 메모리에 있는 내용을 옮기는 작업은 많은 오버헤드를 야기한다. 또한 애당초애 어떤 프로세스를 어떻게 움직여야 오버헤드를 최소화하며 압축할 수 있는지에 대한 방법도 명확하지 않는다.&lt;/p&gt;
&lt;h3 id=&quot;해결안2-페이징paging&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B4%EA%B2%B0%EC%95%882-%ED%8E%98%EC%9D%B4%EC%A7%95paging&quot; aria-label=&quot;해결안2 페이징paging permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;해결안2. 페이징(Paging)&lt;/h3&gt;
&lt;p&gt;메모리 압축 방법도 비효율적이기 떄문에, 외부 단편화 해결을 위한 더 효율적인 방안이 고안되었다. 그것은 바로 &lt;code class=&quot;language-text&quot;&gt;페이징(Paging)&lt;/code&gt; 이라는 기법이다. 페이징이란  오늘날에 가장 효율적으로 많이 사용되는 가상 메모리 기법이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;페이징paging&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8E%98%EC%9D%B4%EC%A7%95paging&quot; aria-label=&quot;페이징paging permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;페이징(Paging)&lt;/h2&gt;
&lt;p&gt;연속적인 메모리 할당 구조, 즉 프로세스를 메모리에 연속적으로 할당하는 방식은 2가지 문제점을 내포하고 있다. 우선 앞서 말했던 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 외부 단편화 문제이며, &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 물리 메모리보다 큰 프로세스를 실행할 수 없다는 점이다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;가상 메모리&lt;/code&gt; 는 실행하고자 하는 프로그램을 일부만 메모리에 적재하여 실제 물리 메모리 크기보다 더 큰 프로세스를 실행할 수 있게 하는 기법이다. 이를 가능케 하는 가상 메모리 관리 기법으로는 &lt;code class=&quot;language-text&quot;&gt;페이징&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;세그먼테이션&lt;/code&gt; 이 있는데, 현대 대부분의 OS 가 사용하는 기법은 페이징이다. 페이징 기법을 사용하면 큰 프로세스를 실행할 수 있을 뿐만 아니라, 외부 단편화 문제도 해결할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;페이징이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8E%98%EC%9D%B4%EC%A7%95%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;페이징이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;페이징이란&lt;/h3&gt;
&lt;p&gt;연속 메모리 할당 방식에서 외부 단편화가 생긴 근본적인 이유는, 각기 다른 크기의 프로세스가 메모리에 연속적으로 할당되었기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d154c69b426b1e44f7e545a72e2b4ece/61946/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.306748466257666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABc0lEQVR42n2SCW+CQBCF+/9/W2+t2HoCitSrCHsD+/oWQ2pa7SQvbDaz38yb4c57j15t23YSQkBp05091UefI5Xqctr2521QiLvLizzPUSULfEUDiNkYOs/QOAenNQxleVa7TxTvQ5w+RtDrGDXv+mJXgeVyCjmNICcRTvECWkkYduSMhrMOIo0hoheo8QBmOSGwvg4MVpI0QTn/QDF8QhW9Qq5iWGNgpISmQodyzZzBPU6UjQms/wGmaYpiOYMgtJqOUbBDJQW7M531tibweED1mUFtc8j9Du6W5QCM4wTV/B3l8BFy9AzFDkOab2p4dhJy9H4LQauChXWW3gYGlWUJddxDsnroQBz23fysVt1igv1guXi9px6g6OQmMMwiSRLObYmSG1TcsszXMNbCWnO2zQ7ddgP59gRBuWR6G9g0DbIsg6QNuZhQnONmBU2gIczyGx5XnOEpW6HkLyV2279Lwa/wl/L+j2oWtvVZjuce1Mc3T/oDzF+0+9sAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d154c69b426b1e44f7e545a72e2b4ece/a6d36/image-6.png&quot;
        srcset=&quot;/static/d154c69b426b1e44f7e545a72e2b4ece/222b7/image-6.png 163w,
/static/d154c69b426b1e44f7e545a72e2b4ece/ff46a/image-6.png 325w,
/static/d154c69b426b1e44f7e545a72e2b4ece/a6d36/image-6.png 650w,
/static/d154c69b426b1e44f7e545a72e2b4ece/e548f/image-6.png 975w,
/static/d154c69b426b1e44f7e545a72e2b4ece/3c492/image-6.png 1300w,
/static/d154c69b426b1e44f7e545a72e2b4ece/61946/image-6.png 1546w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;만약 메모리와 프로세스를 &lt;code class=&quot;language-text&quot;&gt;일정한 단위&lt;/code&gt; 로 자르고, 이를 메모리에 불연속적으로도 할당할 수만 있다면 외부 단편화는 발생하지 않는다. 이에 대한 기법이 바로 페이징이다. &lt;strong&gt;페이징은 프로세스의 논리 주소 공간을 페이지(Page) 라는 동일한 크기의 일정한 단위로 자른 뒤, 페이지를 프레임에 할당하는 가상 메모리 관리 기법이다.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;페이징 핵심 요약&lt;/h4&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;프로세스의 논리 주소공간을 페이지라는 일정한 단위로 자른다.&lt;/li&gt;
&lt;li&gt;메모리의 물리 주소공간을 프레임이라는, 페이지와 동일하고 일정한 단위로 자른다.&lt;/li&gt;
&lt;li&gt;페이지를 프레임에 할당하는 가상 메모리 관리 기법이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;페이징에서의-스와핑swapping--page-in-page-out&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8E%98%EC%9D%B4%EC%A7%95%EC%97%90%EC%84%9C%EC%9D%98-%EC%8A%A4%EC%99%80%ED%95%91swapping--page-in-page-out&quot; aria-label=&quot;페이징에서의 스와핑swapping  page in page out permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;페이징에서의 스와핑(Swapping) : Page In, Page Out&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/183578ed68ed68c7872ae006a953a4d1/1134b/image-7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.239263803680984%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABgElEQVR42oVS/UsCQRD1//9H+iWKCCyVTk3TIuqggrA0v+7y7jz3uA/vvM/XzOYKgdDCMMzuzpv3ZqYGOmVVIctzhFGEvChQliUq5fdW0nvFRn/5FPSeZRlyuuPD92w1DuIwhLBWWJsGwrWDJI7lJy6ScBIlK6AkSRDS/5j+bLdbREQiCAL4vi8JSMDA+sa418Z0cAv7VcfmRYeon2E87ONNa+KDvHlzBa/TkCrSNJXMFEuOGZjvJKBhGGi2WtC0Nu6G93CWC6SzCeK1jcR1EDoWwvkU8XIumck2EZvdbneQzKBc4Jeh2MD8HMGefcGeThCRBBbo+wGE50lWCSUziAJkb5omXNeVsQKXgD5Jfu+0MCLZC/0R1tMDVucn0HtdPHfb0AcDTOoXEJeniKhv3E/VO48KMrM/gCz5utEgyRp6/T5cx6aJpFJGxkZ9Kigho8an5JmdEAKO40jPMdthKJwgJ7afGsfHTrVfDzUMtTaFWjG1NmqH1OV/drTYfg9/AL92r7qb/xj4AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/183578ed68ed68c7872ae006a953a4d1/a6d36/image-7.png&quot;
        srcset=&quot;/static/183578ed68ed68c7872ae006a953a4d1/222b7/image-7.png 163w,
/static/183578ed68ed68c7872ae006a953a4d1/ff46a/image-7.png 325w,
/static/183578ed68ed68c7872ae006a953a4d1/a6d36/image-7.png 650w,
/static/183578ed68ed68c7872ae006a953a4d1/e548f/image-7.png 975w,
/static/183578ed68ed68c7872ae006a953a4d1/3c492/image-7.png 1300w,
/static/183578ed68ed68c7872ae006a953a4d1/1134b/image-7.png 1470w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;페이징에서도 스와핑을 사용할 수 있다. 페이징을 사용하는 시스템에선 프로세스 전체가 &lt;code class=&quot;language-text&quot;&gt;스왑 아웃(Swap Out)&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;스왑 인(Swap In)&lt;/code&gt; 된다. 즉, 메모리에 적재될 필요가 없는 페이지들은 보조기억장치로 Swap Out 되고, 실행에 필요한 페이지들은 메모리로 Swap In 된다. 페이징 시스템에서의 Swap Out 은 &lt;code class=&quot;language-text&quot;&gt;페이지 아웃(Page Out)&lt;/code&gt;, Swap In 은 &lt;code class=&quot;language-text&quot;&gt;페이지 인(Page In)&lt;/code&gt; 이라고 부르기도 한다.&lt;/p&gt;
&lt;p&gt;메모리에 적재될 필요가 없는 페이지들을 내쫓고, 실행에 필요한 페이지들을 메모리내에 유입한다. 또한 &lt;strong&gt;프로세스를 실행하기 위해, 모든 페이지가 꼭 메모리에 적재되어야 할 필요는 없다.&lt;/strong&gt; 프로세스를 이루는 페이지 중 실행에 필요한 일부 페이지만을 메모리에 적재하고, 당장 실행에 필요하지 않은 페이지들은 보조기억장치에 남겨둘 수 있다. 이를 통해 물리 메모리보다도 큰 프로세스도 실행될 수 있다. 정말 큰 프로세스라고 한들, 해당 프로세스를 구성하는 각 페이지를 메모리에 적재하여 실행 가능하기 때문이다.&lt;/p&gt;
&lt;h3 id=&quot;페이지-테이블-page-table&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8E%98%EC%9D%B4%EC%A7%80-%ED%85%8C%EC%9D%B4%EB%B8%94-page-table&quot; aria-label=&quot;페이지 테이블 page table permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;페이지 테이블 (Page Table)&lt;/h3&gt;
&lt;p&gt;그런데 문제가 있다. 프로세스가 메모리에 불연속적으로 배치되어 있다면 CPU 입장에서 이를 순차적으로 실행할 수가 없다. 프로세스를 이루는 페이지가 어느 프레임에 적재되어 있는지 CPU 가 모두 알고 있기란 어렵기 때문이다. 즉, 프로세스가 메모리에 불연속적으로 배치되면 CPU 는 &quot;다음에 실행할 명령어 위치&quot; 를 찾기가 어려워진다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c4eacdc4f5d7815ce2d972d3269d0a57/07a9c/image-8.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.306748466257666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABOklEQVR42p2T3W6DMAyFeey+RJ9pN+suJq3crBNRJVh/aCH8poF0EtCz2FoYq7pJXaQjEsv+jg3Eu1wu+E1uXceHYbi5J3l/AbuuY00N6CyEwHK5ZAVBwDFn7N1yodX3PXa7HeI4HqEOmCQJpJSjTqcTiqJgjucSp0CCURHBwjDEZrPhmDNqWoPOPnsLMOczlFKcz8AoijCfz+H7/gimorquUZYlJ5LcWH3fQW0j5OsAVbiGtnulau6UgUIEmM1mWCwWXEBBkjEGTdOgqiqGjx1asFr5iJ8eIJ8foV5foGxOnuffI1+/QzprrRlGiWmajh0OFtzmEq1M8FFk0PLII/8A3vo1pkaj4RewSY/Qhz2a5ACdxFB2giybAO8RAU28RSZWyMUb2v07TNtyl/8Cuk7pCw9kQNNNLsAn1EIFffYc+qcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/c4eacdc4f5d7815ce2d972d3269d0a57/a6d36/image-8.png&quot;
        srcset=&quot;/static/c4eacdc4f5d7815ce2d972d3269d0a57/222b7/image-8.png 163w,
/static/c4eacdc4f5d7815ce2d972d3269d0a57/ff46a/image-8.png 325w,
/static/c4eacdc4f5d7815ce2d972d3269d0a57/a6d36/image-8.png 650w,
/static/c4eacdc4f5d7815ce2d972d3269d0a57/e548f/image-8.png 975w,
/static/c4eacdc4f5d7815ce2d972d3269d0a57/3c492/image-8.png 1300w,
/static/c4eacdc4f5d7815ce2d972d3269d0a57/07a9c/image-8.png 1440w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이를 해결하기 위해 페이징 시스템은 (실제 메모리 내의 주소인) 물리 주소에 불연속적으로 배치되더라도 (CPU 가 바라보는 주소인) 논리 주소에는 연속적으로 배치되도록 &lt;code class=&quot;language-text&quot;&gt;페이지 테이블(Page Table)&lt;/code&gt; 을 이용한다. 페이지 테이블이란 페이지 번호와 프레임 번호를 매핑하여 짝 지어주는 일종의 이정표다. 즉, &lt;strong&gt;페이지 테이블은 현재 어떤 페이지가 어떤 프레임에 할당되었는지를 알려준다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/17dfb6631808bb935c2e9609f841a0e8/67a79/image-9.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.21472392638037%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB2klEQVR42o1S247aQAzl/z+IJ6SiiksXLQiSFgpJIBcChJA7Cbmf2rMLTfeplqzReOxj+5zpXa9XsNV1jcfjgaIoxL1t25c/7WusaRrh3bee4zgikBPY+XRCHEVoKemZ0JBzM/YuYEh5hmlCUVU4NFRG9Wy9q+siT1P4lglr+xvB0ULkXFDnOdqqQlWWOB2POJLXdGd7ZBns/R6KLEOev8PSVERB8AHoujfk9zs8U4cqLQnYQHQ5oaBYRYV54OOw3WJPAGUco6J4SsUR5dm/ZOze3xDqGrIo/DthnaVIdBW2vES83yGxTZS0Ql0WtHIjeGV+G1q7oNzsnsBQdljNfuDt+xDqTwmh730C0v4VreL5NImuCz6SJHkR3xWF+WTwjKaML2dcNEVsFZ9tpHH0Aeh5HlyacjKZYDAYYDqdQpIkZLQuN2JAPlkUBiuJU1aWm1rEq6ppCMIQKTX55NAVxbZtY71e40RK3243UZyTMHdKNElNwzCQkngMzud8Pke/3xf+bTgUdQKQi0PqsFqtMBqNxKkoimjCgAEJwPfD4SDAmUt+04me2WyG8XiMzWYDnyj7B3C5XIrHxWIhEriQp+H1eE2euPsPORaT6tyQG/P9Jcr/Wleor/60Pw/+Q8aPUdGMAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/17dfb6631808bb935c2e9609f841a0e8/a6d36/image-9.png&quot;
        srcset=&quot;/static/17dfb6631808bb935c2e9609f841a0e8/222b7/image-9.png 163w,
/static/17dfb6631808bb935c2e9609f841a0e8/ff46a/image-9.png 325w,
/static/17dfb6631808bb935c2e9609f841a0e8/a6d36/image-9.png 650w,
/static/17dfb6631808bb935c2e9609f841a0e8/e548f/image-9.png 975w,
/static/17dfb6631808bb935c2e9609f841a0e8/3c492/image-9.png 1300w,
/static/17dfb6631808bb935c2e9609f841a0e8/67a79/image-9.png 1408w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이 방식을 통해, &lt;strong&gt;비록 물리 주소상에서는 프로세스들이 분산되어 저장되어 있더라도 CPU 입장에서 바라본 논리 주소는 연속적으로 보이게끔 할 수 있다.&lt;/strong&gt; 즉, 프로세스들이 메모리에 분산되어 저장되어 있더라도, CPU 는 논리 주소를 그저 순차적으로 실행하면 된다.&lt;/p&gt;
&lt;h3 id=&quot;페이징시-발생하는-내부-단편화internal-fragmentation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8E%98%EC%9D%B4%EC%A7%95%EC%8B%9C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EB%82%B4%EB%B6%80-%EB%8B%A8%ED%8E%B8%ED%99%94internal-fragmentation&quot; aria-label=&quot;페이징시 발생하는 내부 단편화internal fragmentation permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;페이징시 발생하는 내부 단편화(Internal Fragmentation)&lt;/h3&gt;
&lt;p&gt;페이징은 외부 단편화 문제를 해결할 수 있지만, 내부 단편화라는 문제를 야기할 수 있다. 페이징은 프로세스의 논리 주소 공간을 페이지라는 일정한 크기 단위로 자른다. 그런데 모든 프로세스가 페이지 크기에 딱 맞게 잘리는 것은 아니다. 즉, 페이지 내부에서 아주 작은 빈 공간이 발생할 수 있다.&lt;/p&gt;
&lt;p&gt;내부 단편화는 하나의 페이지보다 작은 크기로 발생한다. 따라서 하나의 페이지 크기가 작다면 발생하는 내부 단편화 크기는 작아질 가능성이 크다. 하지만, 그렇다고 한 페이지의 크기를 너무 작게 설정해도 그마늠 페이지 테이블의 크기도 커지기 때문에, 페이지 테이블이 차지하는 공간이 낭비된다. 따라서 내부 단편화를 적당히 방지하면서, 너무 크지 않은 페이지 테이블이 만들어지도록 페이지의 크기를 조정하는 것이 바람직하다.&lt;/p&gt;
&lt;h2 id=&quot;페이징에서의-주소-변환&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8E%98%EC%9D%B4%EC%A7%95%EC%97%90%EC%84%9C%EC%9D%98-%EC%A3%BC%EC%86%8C-%EB%B3%80%ED%99%98&quot; aria-label=&quot;페이징에서의 주소 변환 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;페이징에서의 주소 변환&lt;/h2&gt;
&lt;p&gt;그렇다면, 페이징을 활용하는 구조에서 특정 주소에 접근하려면 어떤 정보가 필요할까? 아래 2가지 정보가 필요하다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Page Number(페이지 번호) : 어떤 페이지, 프레이메 접근하고 싶은지 (접근하려는 특정 페이지의 시작 주소)&lt;/li&gt;
&lt;li&gt;Offset : 접근하려는 주소가 해당 페이지 또는 프레임 시작 주소로부터 얼마나 떨어져 있는지&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c141940e75479b2f2c716da6ebe4eaf8/cf8e5/image-10.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.78527607361963%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABRElEQVR42o1R7WqDQBD0gfNGeZBAfkSK0CqCNTQGhQpNatTG75qExI9Mbw4ES5q2C8vt7e7Nzs0qp9MJ9+x6vd5xoO065EWBXlz6UU05Ho+oqgppmiLLMul5nqNpmh/BOgGURSGycIeP9w2KOEIqYuLQFIJtt1sEQYAkSRBFETYb0SimDyBj6/sehecgW5ooHRv12sbnq4s4jmVN4ZfJhqCu66Kua1wuF8mExpjT2Xc+n3E4HLBf2QgNDXvLQPpsoPTWsk8yHKhOp1NMJhPB7k2CMc/HHMCBzNH5MI92KMMAlTjrOEQhJGBNajgsxbIszGYzlGUpC23bSuc3xgvi3V4u8aBpeNR1POkGXlarW8D/bpqApmliPp9DVVWoi4Ukw/w3QCaG5G/OHt/34TiO1NzzPHm/YfgX0NgpBbXkkqgv44HhF/dqsLohfD53AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/c141940e75479b2f2c716da6ebe4eaf8/a6d36/image-10.png&quot;
        srcset=&quot;/static/c141940e75479b2f2c716da6ebe4eaf8/222b7/image-10.png 163w,
/static/c141940e75479b2f2c716da6ebe4eaf8/ff46a/image-10.png 325w,
/static/c141940e75479b2f2c716da6ebe4eaf8/a6d36/image-10.png 650w,
/static/c141940e75479b2f2c716da6ebe4eaf8/e548f/image-10.png 975w,
/static/c141940e75479b2f2c716da6ebe4eaf8/3c492/image-10.png 1300w,
/static/c141940e75479b2f2c716da6ebe4eaf8/cf8e5/image-10.png 1402w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;페이지 번호는 말 그대로 접근하고자 하는 페이지의 번호로, 해당 페이지의 시작주소가 된다. 페이지 테이블에서 해당 페이지 번호를 찾으면 어떤 프레임에 할당되었는지를 알 수 있다. offset 은 접근하려는 주소가 프레임의 시작 번지(주소) 로 부터 얼만큼 떨어져있는지를 알기 위한 추가 주소값이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8e4242877ec4947f44dcd31307f5c163/3c492/image-11.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.49079754601227%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA80lEQVR42o1S2WrDMBD0/z/5pfmrhibQl1Kw5MhHjY9a8oHxMfWIyiSxC10YpB3toVnJW5YFxDRNFs53cPbMOxhj0Pf95nu7RIdfjsFVVaFt241zK4uFYYgoirainjt8u1xwPr+ikAHizw+oNSiOYwshBIJA4Ot2Q/h+hZICQkqURYEsyyyGYXgseDq9wPd9dEajynN81zXKskS9rvnqM4l7o2s0TWNBnzzPdzfUWtuAe5vn2XKUnCQJuq57kExfKYU0Te3+eIZ3A2dBSuH8eINxHLdGNPJylb6boUsmjl7SFXjm2YAPQ/nuh3h/fYf/4KjJD1I3bOU7hSQoAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/8e4242877ec4947f44dcd31307f5c163/a6d36/image-11.png&quot;
        srcset=&quot;/static/8e4242877ec4947f44dcd31307f5c163/222b7/image-11.png 163w,
/static/8e4242877ec4947f44dcd31307f5c163/ff46a/image-11.png 325w,
/static/8e4242877ec4947f44dcd31307f5c163/a6d36/image-11.png 650w,
/static/8e4242877ec4947f44dcd31307f5c163/e548f/image-11.png 975w,
/static/8e4242877ec4947f44dcd31307f5c163/3c492/image-11.png 1300w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;즉, &amp;#x3C;Page Number, Offset&gt; 를 가진 논리 주소는 위처럼 페이지 테이블을 거쳐서 &amp;#x3C;Frame Number, Offset&gt; 으로 변환된다.&lt;/p&gt;
&lt;p&gt;참고로 페이지의 offset 과 프레임의 offset 값은 항상 같다. 페이지와 프레임의 크기는 항상 같기 떄문이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a85a6c9baf843aeb1a4936a056af83e8/248b0/image-12.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.34969325153374%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABR0lEQVR42n2Ti46CUAxE+W/3B9w/JOERlKcCovJSwJrT5LJIcJuUaO/c6XQo1uv1kq0kvp0tc5omTfPfWhcfj4dcr1dJ01TO57MMwyDjOMrlcpEkSSTLMsVSr+ta62TXdX+Ey4Asz3M5Ho9SFIUCm6aRIAjE8zxxHEcx1GkIWVVVcrvdtJHFw3VdvUBwAAiFXOz7flZNA6OGLMtScSRNlfD5fMruZyf7370S3u93vRTH8QchZKfTSVVxGUJw1LBhJuRh27aqXI7s+76SAKSpIeQM79q21RrWRFGkk+H1h4eQm9HWhKiBDL8gJDk/HA4ShuEnIT/Mm4aQS2ZklDAyoxq/sIU65+B4+9RmwmUChJBRUIWHAM2KoATFKAdnVmn2cGtZUWRUsG/LJTYW0QgMCkmwXwmX8d9XgnLethlXF1s24hv5GrMVbxOinypqQa/vAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/a85a6c9baf843aeb1a4936a056af83e8/a6d36/image-12.png&quot;
        srcset=&quot;/static/a85a6c9baf843aeb1a4936a056af83e8/222b7/image-12.png 163w,
/static/a85a6c9baf843aeb1a4936a056af83e8/ff46a/image-12.png 325w,
/static/a85a6c9baf843aeb1a4936a056af83e8/a6d36/image-12.png 650w,
/static/a85a6c9baf843aeb1a4936a056af83e8/e548f/image-12.png 975w,
/static/a85a6c9baf843aeb1a4936a056af83e8/3c492/image-12.png 1300w,
/static/a85a6c9baf843aeb1a4936a056af83e8/248b0/image-12.png 1316w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;예를들어 논리주소, 즉 &amp;#x3C;페이지 주소, offset&gt; 이 &amp;#x3C;5, 2&gt; 인 곳에 접근하려고 한다. 이때 5번 페이지 번호에서 2만큼 떨어진 곳을 찾으면 될 것이다. 5번 페이지를 확인해보면, 프레임 번호 1과 매핑된다. 즉, 1번 프레임의 시작 주소값은 8번지 이므로, 이에 offset 값인 2를 더한 10번지에 바로 CPU 가 접근하게 될 물리주소값이 된다.&lt;/p&gt;
&lt;h2 id=&quot;페이지-테이블-엔트리-pte&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8E%98%EC%9D%B4%EC%A7%80-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%97%94%ED%8A%B8%EB%A6%AC-pte&quot; aria-label=&quot;페이지 테이블 엔트리 pte permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;페이지 테이블 엔트리 (PTE)&lt;/h2&gt;
&lt;p&gt;페이지 테이블에 담기는 정보는 페이지 번호, 프레임 번호 이외에도 부가적인 정보가 조금 더 감긴다.&lt;/p&gt;
&lt;h3 id=&quot;유효-비트-valid-bit&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9C%A0%ED%9A%A8-%EB%B9%84%ED%8A%B8-valid-bit&quot; aria-label=&quot;유효 비트 valid bit permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;유효 비트 (Valid Bit)&lt;/h3&gt;
&lt;p&gt;현재 해당 페이지에 접근 가능한지 여부를 알려주는 정보다. 유효 비트가 1이라면 메모리에 적재되어 있는 페이지이고, 유효비트가 0이라면 적재되지 않은 페이지를 의미(스왑 영역에 존재) 한다.&lt;/p&gt;
&lt;h3 id=&quot;보호-비트-protection-bit&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B3%B4%ED%98%B8-%EB%B9%84%ED%8A%B8-protection-bit&quot; aria-label=&quot;보호 비트 protection bit permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;보호 비트 (Protection Bit)&lt;/h3&gt;
&lt;p&gt;페이지 보호를 위해 존재하는 비트다. 보호 비트를 통해 해당 페이지가 읽고 쓰기(Read and Write) 가 모두 가능한지, 또는 읽기만 가능한 페이지인지를 나타낼 수 있다. 보호 비트가 0이면 읽기 전용, 1이면 읽기 및 쓰기 전용으로 설정할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;참조-비트-reference-bit&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EC%A1%B0-%EB%B9%84%ED%8A%B8-reference-bit&quot; aria-label=&quot;참조 비트 reference bit permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참조 비트 (Reference Bit)&lt;/h3&gt;
&lt;p&gt;CPU가 이 페이지를 접근한 적이 있는 지의 여부를 기록한다.&lt;/p&gt;
&lt;h3 id=&quot;수정-비트-dirty-bit&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%98%EC%A0%95-%EB%B9%84%ED%8A%B8-dirty-bit&quot; aria-label=&quot;수정 비트 dirty bit permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;수정 비트 (Dirty Bit)&lt;/h3&gt;
&lt;p&gt;CPU 가 이 페이지에 데이터를 쓴 적이 있는 지의 여부를 기록한다.&lt;/p&gt;
&lt;p&gt;수정 비트가 존재하는 이유는 스와핑과 관련이 있다. 페이지가 메모리에서 사라질 때, 보조 기억장치에 쓰기를 해야할 지 말 지 여부를 판단한다. 만약 쓰기 작업 했다면 수정된 페이지가 스왑 아웃될 때 보조 기억장치에도 쓰기 작업을 해야한다. 이를 수정 비트를 확인하여 판단한다.&lt;/p&gt;
&lt;h2 id=&quot;세그먼테이션segmentation-vs-paging&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B8%EA%B7%B8%EB%A8%BC%ED%85%8C%EC%9D%B4%EC%85%98segmentation-vs-paging&quot; aria-label=&quot;세그먼테이션segmentation vs paging permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;세그먼테이션(Segmentation) vs Paging&lt;/h2&gt;
&lt;p&gt;지금까지 살펴본 페이징 방식은 프로세스를 일정한 크기의 페이지 단위로 분할하여 메모리에 적재하는 방식이었다. 반면 &lt;code class=&quot;language-text&quot;&gt;세그먼테이션(Segmentaion)&lt;/code&gt; 방식은 가상 메모리를 서로 크기가 다른 논리적 단위로 분할한 것을 뜻한다. 세그먼테이션은 프로세스를 물리적 단위인 페이지가 아닌, 논리적 단위인 &lt;code class=&quot;language-text&quot;&gt;세그먼트(Segment)&lt;/code&gt; 로 분할해서 메모리에 적재하는 방식이다.&lt;/p&gt;
&lt;p&gt;페이징은 한 프로세스를 페이지라는 같은 크기로 분할해서 보관하는 방식이라면, 세그먼테이션은 부위 별로 잘라서 보관한다고 이해하면 된다. &lt;strong&gt;세그먼트는 의미가 같지 않은 논리적 내용을 기준으로 프로그램을 분할하기 때문에  분할된 각 세그먼트의 크기가 같지 않다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;장단점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EB%8B%A8%EC%A0%90&quot; aria-label=&quot;장단점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장단점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;장점 : 내부 단편화 문제가 해소된다. 또한 보호화 공유 기능을 수행할 수 있다. 프로그램의 중요한 부분과 중요하지 않은 부분을 분리하여 저장할 수 있고, 같은 토드 영역은 한 번에 저장할 수 있다.&lt;/li&gt;
&lt;li&gt;단점 : 외부 단편화 문제가 발생할 수 있다.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[데드락(Deadlock) 의 발생조건과 이를 해결하기 위한 4가지 방안(예방, 회피, 검출, 회복)]]></title><description><![CDATA[💡 본 포스팅은  CS 기술 면접 스터디에도 작성된 글 입니다. 데드락(DeadLock…]]></description><link>https://haon.site/cs/os/deadlock/</link><guid isPermaLink="false">https://haon.site/cs/os/deadlock/</guid><pubDate>Fri, 09 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 본 포스팅은  &lt;a href=&quot;https://github.com/kakaotech-25/cs-plant-interview&quot;&gt;CS 기술 면접 스터디&lt;/a&gt;에도 작성된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;데드락deadlock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EB%93%9C%EB%9D%BDdeadlock&quot; aria-label=&quot;데드락deadlock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데드락(DeadLock)&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ac4ba0cdcfcf2898269fc8b8697ecb2f/9cea8/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.62576687116564%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABCUlEQVR42o2S2YqDQBBF/f8fEnwUFeIScUUh+CD4EBW3uCVq7lANPQzSYVJQFFQ3p2/dagmneL/fEMWn/jkkUXPfdyzLwioH8ToMAyzLQt/3woeks4IoiqCqKqqqQtu2mKaJgbuug+M4LO/3O3twnuf/gU3TwPM8BjUMA2VZIkkSyLIM0zTxeDzYvW3bvh+ZgkbKsgx5njMwjUnVdV3ouo6iKIS+CoE0zjiOOI6DKYrjGJqmMQterxds24aiKAiCAGEYYl3Xz0ACUdJItIDr9Qrf95lf1CdPSRllXde43W7sjNsmnb8G32yaprhcLmwBz+fz95zUkB0c8rWHpI4WIjKfwNTnSv8CfwBmB7ZRPFxdzgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/ac4ba0cdcfcf2898269fc8b8697ecb2f/a6d36/image.png&quot;
        srcset=&quot;/static/ac4ba0cdcfcf2898269fc8b8697ecb2f/222b7/image.png 163w,
/static/ac4ba0cdcfcf2898269fc8b8697ecb2f/ff46a/image.png 325w,
/static/ac4ba0cdcfcf2898269fc8b8697ecb2f/a6d36/image.png 650w,
/static/ac4ba0cdcfcf2898269fc8b8697ecb2f/e548f/image.png 975w,
/static/ac4ba0cdcfcf2898269fc8b8697ecb2f/9cea8/image.png 1278w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위처럼 도심 속 도로에서 차가 꽉 막혀 이도저도 못하고 대기하는 상황을 간혹 본적이 있을 것이다. 이렇게 교통이 마비되어 버리면 복구되기까지 오랜 시간이 걸릴것이다. 운영체제내 프로세스 간에도 이와 비슷한 문제가 발생한다. 이를 &lt;code class=&quot;language-text&quot;&gt;데드락(DeadLock)&lt;/code&gt; 이라고 하며, &lt;code class=&quot;language-text&quot;&gt;교착상태&lt;/code&gt; 로 번역된다. 2개 이상의 여러 프로세스가 자원을 획득하기 위해 무기한 대기하는 상황으로, 이 데드락에 빠진 프로세스들은 그 누구도 빠져나오지 못하고 다른 작업을 진행할 수 없게된다.&lt;/p&gt;
&lt;h3 id=&quot;식사하는-철학자-문제dining-phliosophers-problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9D%EC%82%AC%ED%95%98%EB%8A%94-%EC%B2%A0%ED%95%99%EC%9E%90-%EB%AC%B8%EC%A0%9Cdining-phliosophers-problem&quot; aria-label=&quot;식사하는 철학자 문제dining phliosophers problem permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;식사하는 철학자 문제(Dining Phliosophers Problem)&lt;/h3&gt;
&lt;p&gt;식사-철학자 문제는 데드락이 발생하는 상황을 설명하기 위한 아주 고전적이고 재밌는 문제이다. 아래처럼 철학자 4명이 식탁에 앉아있고, 식사를 하는 상황을 가정해보자. 각 철학자들은 본인의 양측에 있는 젓가락을 손에 집어서 식사를 할 것이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c15951806e285073a0a821bfe2023e42/e996b/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 86.50306748466258%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAADSElEQVR42nWUWUxUZxTHL7g3jWl8aWKbvvSlb320idGmSuOClrTW0DRxiYgizliqtMG2NlIjYGuqKFUfKqK41LjEJYi0UErLjIzIDJ1gwxRUbC0MMGWbyyxw7/35zTd3xhnALznfzT055/+d7X8UJhzD0IUY6EIip7ndi/WYnZKfXAz6g1Kn67oUw7RJPIoJE739vaCFiZkFQ2PsOG4ju9RORmEd5+o8JqBp4e8RTvoUgOZL2n9uVHcVgdC4jDAYDrG7vJmsI04+3G/nyh8dETj6BlUeOW7hdVwl0T8OGAt9xD9Ky8+XsF88TG+fV+qcnsdsOnCWbypuMqKOSp2n4RJtNWfo7Pr3eSlHH9E0jUgCercbw9fJjd9PkbP3DTJ3vcjHn71E/sG3cf9tE7X4H0Mbi/rokXpPBWh+dbMmF6sPsHijwrtbUli5fYaQ6byTpbDaMpf7DxxJthHgmL8SS987oHLhVw/OjgG8/Y/4IG8e6bnTWCWAVmxLZVlOCu9Z57B0s8LuQ8ukT829x5wXjfINB4iVTkY4Pq6J4jtYvbeWdQfv8e2Zw2RYU1iRO511X7xGduGbFBxKY33BfNK2KGz66lUqb9vILGkUPnXsP++MZykBfUMBco408nl5C1vL3Gwt+Zo1n6SyfNs0CWgpeoud3y0ke8/rLMlWyNrzCoUVVViOO9l+7C6fnmgiHB5LTvlsrUfM2i8UnHTR5G4SEb7AKstMVoq003NnCPBUMnbMIU0A5hUvoKtnCMsPNj4qrqf6bpdZUyOxywa9oo5qIMqG0nNWFm0QTbDOIt0yW4DPlrVcKgAbmi9LG3U0KNgTmHpsYlSTtxYkJFhTdiFPNGWW6KwiolTIzH+Zanul6OB9wsNeOWIk0HTSYA8MDtH2Vzt/Xiuj/0Gr1D38p42iU0c5ceU0wyO9Uud2NNBYWUyL/TcS/ScBqiNDPKmv4Em7i+FAWMZbfruT9/fdYW1RE/WtPZJ6oXEDzd9P0HUZY3QgiX5Jy4ExVRC+Oz6kIdG5PLEc8n9sZuP3jZRebU2KyFB9GGbUEwCfwcrVpWvxjXLd9pDlX9awZl8tro6+Z9vGMJKWwqSmkEjqpF+Dbp8/zoYJxgmkjQI+BaTchVjtPV1yAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/c15951806e285073a0a821bfe2023e42/a6d36/image-1.png&quot;
        srcset=&quot;/static/c15951806e285073a0a821bfe2023e42/222b7/image-1.png 163w,
/static/c15951806e285073a0a821bfe2023e42/ff46a/image-1.png 325w,
/static/c15951806e285073a0a821bfe2023e42/a6d36/image-1.png 650w,
/static/c15951806e285073a0a821bfe2023e42/e548f/image-1.png 975w,
/static/c15951806e285073a0a821bfe2023e42/e996b/image-1.png 1050w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;하지만 문제가 발생한다. 만약 철학자1이 젓가락1을 손에 집었다면, 철학자 4는 본인의 왼쪽 젓가락인 젓가락1을 획득하기 까지 대기해야한다. 즉, 모든 철학자가 동시에 포크를 집어 식사를 하면 그 어떤 철학자도 식사를 할 수 없고, 젓가락을 획득하기 위해 영원히 대기해야한다. 다시 말해, 모든 철학자는 다른 철학자가 포크를 내려놓을 때 까지 대기해야하는 상황에 빠진다. &lt;strong&gt;이렇듯 공유 자원을 본인이 이미 획득한  자원을 내려놓지 못하고, 다른 프로세스가 획득한 자원을 얻기 위해 서로가 무한정 대기하고 있는 상태가 바로 데드락이다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;rag자원-할당-그래프&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rag%EC%9E%90%EC%9B%90-%ED%95%A0%EB%8B%B9-%EA%B7%B8%EB%9E%98%ED%94%84&quot; aria-label=&quot;rag자원 할당 그래프 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RAG(자원 할당 그래프)&lt;/h3&gt;
&lt;p&gt;데드락이 발생하는 상황은 어떻게 표현할까? 이를 위해 RAG 라는 그래프 개념이 등장했고, 그래프를 그림으로 표현하여 언제 데드락이 발생하는지를 손 쉽게 표현할 수 있다. 즉, RAG 라는 자원 할당 그래프란 어떤 프로세스가 어떤 자원을 사용중에 있고, 또 어떤 프로세스가 어떤 자원을 획득하기 위해 대기중인지 표현하는 그래프다.&lt;/p&gt;
&lt;p&gt;자원 할당 그래프는 아래와 같은 규칙으로 표현된다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 프로세스는 원으로, 자원의 종류는 사각형으로 표현한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 사용할 수 있는 자원의 개수는 자원 사각형 내에 점으로 표현한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 프로세스가 어떤 자원을 할당받아 사용 중이라면 자원에서 프로세스를 통해 화살표를 표시한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 프로세스가 어떤 자원을 획득하기 위해 대기 중 이라면 자원으로 화살표를 표시한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8c0951bbea3fb13ad984bf2ecf29c689/0d390/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.12883435582822%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2UlEQVR42pWR3XKDIBCFff9nqzbe9LptQEMVUET+VE7BpOnkxqZnBpZhdr89sAWSlmUBFwLbFvEfresKpRRi/K0r8nY+E5TlCSTFrPgkt21blNUJTdPc6iIKKSX0pKGmCWpUGMcxOd0OQblQpbxhGCDSy3rOobW+OqSd2A/bzRbtJeoPdgis3y8gX/wOz/pkPd5Ih8JYhznRs1MzzzDOwfhwCBTaQRuL4D18WtZaXDoOObvrH1La4KWswBh76PqXCKGoXus9PgwlTys7fBb0k+W92wcSQrjffwOFLyKvGQD/JAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/8c0951bbea3fb13ad984bf2ecf29c689/a6d36/image-2.png&quot;
        srcset=&quot;/static/8c0951bbea3fb13ad984bf2ecf29c689/222b7/image-2.png 163w,
/static/8c0951bbea3fb13ad984bf2ecf29c689/ff46a/image-2.png 325w,
/static/8c0951bbea3fb13ad984bf2ecf29c689/a6d36/image-2.png 650w,
/static/8c0951bbea3fb13ad984bf2ecf29c689/e548f/image-2.png 975w,
/static/8c0951bbea3fb13ad984bf2ecf29c689/3c492/image-2.png 1300w,
/static/8c0951bbea3fb13ad984bf2ecf29c689/0d390/image-2.png 1472w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위의 왼쪽 예시의 경우, 프로세스 T2 가 자원 R1 을 획득하기 위해 대기중이다. 또한 자원 R1 은 프로세스 T1 에게 할당되어 사용중에 있다.&lt;/p&gt;
&lt;h2 id=&quot;데드락의-발생-조건&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EB%93%9C%EB%9D%BD%EC%9D%98-%EB%B0%9C%EC%83%9D-%EC%A1%B0%EA%B1%B4&quot; aria-label=&quot;데드락의 발생 조건 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데드락의 발생 조건&lt;/h2&gt;
&lt;p&gt;당연하게도 데드락은 아무때나 발생하는 일이 아니다. 어떤 특정 조건을 만족했을 상황에만 발생하느데, 크게 4가지 조건(상황) 이 만족되면 발생한다.&lt;/p&gt;
&lt;h3 id=&quot;상호-배제mutual-exclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%81%ED%98%B8-%EB%B0%B0%EC%A0%9Cmutual-exclusion&quot; aria-label=&quot;상호 배제mutual exclusion permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;상호 배제(Mutual Exclusion)&lt;/h3&gt;
&lt;p&gt;한 프로세스가 사용중인 자원을 다른 프로세스가 아직 획득하지 못하고 사용할 수 없을 때, 즉 상호배제가 존재하는 상황이라면 데드락이 발생할 수도 있다.&lt;/p&gt;
&lt;h3 id=&quot;점유와-대기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%90%EC%9C%A0%EC%99%80-%EB%8C%80%EA%B8%B0&quot; aria-label=&quot;점유와 대기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;점유와 대기&lt;/h3&gt;
&lt;p&gt;식사-철학자 문제에서 그 누구도 식사를 못했던 이유는, &quot;왼쪽 포크를 들고&quot; 다른 철학자가 들고있는 포크를 획득하고자 대기했기 떄문이다. 다시말해, 한 자원을 보유한 채 다른 자원을 기다렸기 떄문이다. 프로세스도 마찬가지로, &lt;strong&gt;어떤 자원을 획득한 상태에서 다른 자원을 획득하고자 대기하는 상황&lt;/strong&gt;라면 데드락이 발생할 수 있다. 이런 상황을 &lt;code class=&quot;language-text&quot;&gt;점유와 대기&lt;/code&gt; 라고 한다.&lt;/p&gt;
&lt;h3 id=&quot;비선점-모드-non-preemption-mode&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EC%84%A0%EC%A0%90-%EB%AA%A8%EB%93%9C-non-preemption-mode&quot; aria-label=&quot;비선점 모드 non preemption mode permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비선점 모드 (Non-Preemption Mode)&lt;/h3&gt;
&lt;p&gt;선점 모드와 달리, 비선점 모드는 다른 프로세스가 획득한 자원을 강제로 빼앗지 못하고 획득하기 위해 계속 대기해야한다. 비선점 모드에선 내가 얻고자 하는 자원을 이용하는 다른 프로세스의 작업이 끝나야지만 비로소 이용할 수 있다. 즉, 어떤 프로세스도 강제로 자원을 빼앗지 못하므로 데드락이 발생할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;원형-대기circular-wait&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%ED%98%95-%EB%8C%80%EA%B8%B0circular-wait&quot; aria-label=&quot;원형 대기circular wait permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원형 대기(Circular Wait)&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1eb0a4cfafa87d371888338b6935bb50/a83dd/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.84662576687117%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAABS0lEQVR42q1U2W7CMBDk//+Np/alSOWhVFBCTsf3NexarQJqWkLpSqtYznqyOzPOCgsixEyZlpRidauAcTYHS+lonctefgTQuYDN3uBl7zAof3+HMUYYo6G0glIKQ99/jpxhnUc3SIxSwlpTam8CWmtxqhtU9QlCCHg/dcXrjj7A73p6GmN+B2RulPHUgaZRXcmcJ8ZSSmWPgbXWBXQW0IWMXeNx6Cx8iFgaiUY+9qac9TFPgJ1KWL9qbI8W9wSLvtlbrLfEtU4TINuhkRFCOchxRKAueTzOK0po/LJPXuJJmJ5eBrSSa/O8KJIUbLsWx1NFohD4hZLMnRADDtUHCdMSn/5nUfLFIQbtB4G6FVecsgisvFSSLCVpkvDN6LPGLspm9mQqNLw3DrvaQpNN+N1X/u2m+ISnN4NnSh/y43eZYzTUqfmnn8O9cQaQnp8ojIiVeQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/1eb0a4cfafa87d371888338b6935bb50/a6d36/image-3.png&quot;
        srcset=&quot;/static/1eb0a4cfafa87d371888338b6935bb50/222b7/image-3.png 163w,
/static/1eb0a4cfafa87d371888338b6935bb50/ff46a/image-3.png 325w,
/static/1eb0a4cfafa87d371888338b6935bb50/a6d36/image-3.png 650w,
/static/1eb0a4cfafa87d371888338b6935bb50/e548f/image-3.png 975w,
/static/1eb0a4cfafa87d371888338b6935bb50/3c492/image-3.png 1300w,
/static/1eb0a4cfafa87d371888338b6935bb50/a83dd/image-3.png 1482w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;RAG 그래프가 원형 형태로 그려지만 데드락이 발생할 수 있다. 프로세스들이 원의 형태로 대기하는 상황을 원형 대기라고 한다.&lt;/p&gt;
&lt;h2 id=&quot;데드락-해결방안-4가지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EB%93%9C%EB%9D%BD-%ED%95%B4%EA%B2%B0%EB%B0%A9%EC%95%88-4%EA%B0%80%EC%A7%80&quot; aria-label=&quot;데드락 해결방안 4가지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데드락 해결방안 4가지&lt;/h2&gt;
&lt;p&gt;운영체제는 애당초에 데드락이 발생하지 않도록 사전에 자원을 적절히 분배하여 &lt;code class=&quot;language-text&quot;&gt;예방(Prevention)&lt;/code&gt;할 수 있고, 데드락이 발생하지 않을 정도로 조금씩 자원을 할당하다가 데드락이 위험이 있으면 자원을 할당하지 않는 방식으로 &lt;code class=&quot;language-text&quot;&gt;회피(Avoidence)&lt;/code&gt; 할 수도 있다. 또한 자원을 제약 없이 마음껏 할당하다가 데드락이 &lt;code class=&quot;language-text&quot;&gt;탐지(Detection)&lt;/code&gt; 되면 그제서야 데드락을 &lt;code class=&quot;language-text&quot;&gt;회복(Recovery)&lt;/code&gt; 하는 방법도 취할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;예방prevention&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%88%EB%B0%A9prevention&quot; aria-label=&quot;예방prevention permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;예방(Prevention)&lt;/h3&gt;
&lt;p&gt;데드락을 어떻게 사전에 예방할까? 이는 생각보다 단순하다. 데드락이 발생하는 조건을 파악하고, 이 조건들이 발생하지 않도록 사전에 막아버리는 것이다. 이 조건들은 앞서 살펴본 &lt;code class=&quot;language-text&quot;&gt;상호배제&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;점유와 대기&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;비선점&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;원형 대기&lt;/code&gt; 이다. 이 4가지 발생조건이 일어나지 않도록 사전에 막아버리는 것이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&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;/blockquote&gt;
&lt;h4&gt;상호배제 제거&lt;/h4&gt;
&lt;p&gt;자원의 상호배제는 없앤다는 뜻은 곧, 여러 프로세스가 모든 자원을 공유하여 사용하게 만든다는 말과 똑같다. 이 방식은 이론적으로만 가능하지, 현실적으로는 모든 자원의 상호배제를 없애는 것은 어렵기 떄문에 현실적으로 사용하기란 무리가 있다.&lt;/p&gt;
&lt;h4&gt;점유와 대기 제거&lt;/h4&gt;
&lt;p&gt;마치 식사-철학자 문제에서 철학자들에게 한 손에 포크를 들고, 다른 포크를 기다리지 못하게 금지하는 것과 같다. 한 철학자가 포크 2개를 동시에 들게 만들거나, 아니면 아예 들지 못하게 하는 방식이다. 이는 프로세스 관점에서 다시 해석하자면, 특정 프로세스가 모든 자원을 획득하도록 허용하거나, 또는 아예 어떤 자원도 획득하지 못하도록 하는 것이다.&lt;/p&gt;
&lt;p&gt;이는 데드락을 해결하는 방법이긴 하지만, &lt;strong&gt;자원 이용률(Resource Utilization)&lt;/strong&gt; 이 낮아진다는 치명적인 단점이 존재한다. 점유와 대기를 금지사면 한 프로세스에 필요한 자원들을 몰아주고, 그 다음에 다른 프로세스에 필요한 자원들을 몰아줘야 한다. 즉, 여러 프로세스가 동시간대에 병렬적인 작업을 하지 못하고, 한 프로세스만 작업해야 하므로 효율성이 떨어진다.&lt;/p&gt;
&lt;h4&gt;비선점 모드 제거&lt;/h4&gt;
&lt;p&gt;비선점 모드가 없어지면, 즉 선점 모드가 되면 자원을 획득해서 사용중인 프로세스의 자원을 빼앗아서 자원을 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;비선점 모드와 선점 모드의 장단점은 상황에 따라 다르기 떄문에, 상황에 따라 이 방법을 택하는 것이 좋다. 가령 CPU 는 프로세스들이 선점할 수 있는 대표적인 자원인데, 한 프로세스가 CPU 를 이용하다가 타임아웃이 발생하여 다른 프로세스가 CPU 를 할당받아 사용할 수 있는 경우(일명 Context Switching)가 효율적인 경우가 있다.&lt;/p&gt;
&lt;p&gt;장단점이 모두 존재하기에, 다소 범용성이 떨어지는 방안이다.&lt;/p&gt;
&lt;h4&gt;원형 대기 제거&lt;/h4&gt;
&lt;p&gt;모든 자원에 번호를 붙이고, 오름차순으로 자원을 할당하면 원형 대기가 발생하지 않는다. 예를들어, 식사-철학자 문제에서 모든 포크크게 1~4번까지 번호를 붙이고, 철학자들이 번호가 낮은 포크부터 높은 포크 순으로 집어들게 하면 원형 대기가 발생하지 않는다.&lt;/p&gt;
&lt;p&gt;이 방식은 앞서 3가지 방식들보다 비교적 현실적이고 실용적이지만, 역시나 단점은 존재한다. 모든 컴퓨터 시스템내에 존재하는 수 많은 자원에 번호를 붙이는 일은 다소 번거로운 작업이며, 각 자원에 어떤 번호를 붙이는지에 따라 특정 자원에 이용률(utilization) 이 떨어질 수 있으므로 설계 측면에서 어렵다는 것이 단점이다.&lt;/p&gt;
&lt;h3 id=&quot;회피avoidence&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%9A%8C%ED%94%BCavoidence&quot; aria-label=&quot;회피avoidence permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;회피(Avoidence)&lt;/h3&gt;
&lt;p&gt;회피 기법은 데드락이 발생하지 않을 정도로만 조심스럽게 자원을 할당하는 방식이다. 이 기법에선 데드락을 한정된 자원의 무분별한 할당으로 인해 데드락이 발생한다고 가정하여, 마구잡이로 자원을 할당하지 않고 더욱 신중하게 자원을 할당하는 방식이다.&lt;/p&gt;
&lt;p&gt;회피 기법에선 아래 용어가 등장한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;안전 상태(Safe State) : 자원을 할당받고 종료될 수 있는 상태&lt;/li&gt;
&lt;li&gt;불안정 상태(Unsafe State) : 교착 상태가 발생할 수도 있는 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;안전 순서(Safe Sequence) : 데드락 발생없이 안전하게 프로세스들이 자원을 할당할 수 있는 순서&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;운양체게가 데드락을 회피하기 위해서는 시스템 상태가 &lt;code class=&quot;language-text&quot;&gt;안전 상태&lt;/code&gt; 에서 &lt;code class=&quot;language-text&quot;&gt;안전 상태&lt;/code&gt; 로 움직이는 경우에만 자원을 할당하면 된다. 즉, 데드락 회피 기법은 항상 안전 상태를 유지하도록 자원을 할당하는 방식이다.&lt;/p&gt;
&lt;h3 id=&quot;탐지detection-와-회복recovery&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%83%90%EC%A7%80detection-%EC%99%80-%ED%9A%8C%EB%B3%B5recovery&quot; aria-label=&quot;탐지detection 와 회복recovery permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;탐지(Detection) 와 회복(Recovery)&lt;/h3&gt;
&lt;p&gt;앞선 &lt;code class=&quot;language-text&quot;&gt;예방&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;회피&lt;/code&gt; 기법이 데드락을 사전에 최대한 막기위한 노력이라면, 데드락 탐지 &amp;#x26; 회복 기법은 데드락이 발생했을 때가 되어서야 그떄가서 조치를 취하는 방법이다.&lt;/p&gt;
&lt;p&gt;운영체제는 프로세스들이 자원을 요구할 때 마다 그떄그떄 매번 모두 할당해주며, 데드락 발생 여부를 주기적으로 검사한다. 검사 결과로 데드락이 발생했다고 탐지되면 그때서야 비로소 다음과 같은 방식으로 회복한다.&lt;/p&gt;
&lt;h4&gt;선점을 통한 회복&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;데드락이 해결될 때 까지 한 프로세스씩 자원을 몰아주는(일명 몰빵) 방식&lt;/strong&gt;이다. 데드락이 해결될 떄 까지 다른 프로세스로부터 자원을 강제로 빼앗아 버린뒤, 빼앗은 자원들을 한 프로세스에 할당한다.&lt;/p&gt;
&lt;h4&gt;프로세스 강제 종료를 통한 회복&lt;/h4&gt;
&lt;p&gt;프로세스를 강제로 종료시켜서 데드락을 회복할 수 있다. 가장 단순하면서 확실한 방식이다. 운영체제는 데드락에 빠진 프로세스들을 모두 강제 종료할 수도 있고, 데드락이 없어질 떄 까지 한 프로세스만 강제 종료할 수도 있다. 하지만 많은 프로세스들이 작업한 내역을 잃게 될 수 있다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[하모니 팀의 협업 프로세스를 소개합니다.]]></title><description><![CDATA[🌈 현재 포스트는 harmony 팀 기술 블로그 에 게시된 글 입니다. 우리 팀은 어떤 방식으로 협업 하는지를 소개하고자 한다. 큰 주제는 다음과 같다. 하모니 팀의 Git Flow 전략 KPT…]]></description><link>https://haon.site/github/harmony-strategy/</link><guid isPermaLink="false">https://haon.site/github/harmony-strategy/</guid><pubDate>Wed, 07 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;🌈 현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/project/git-flow-strategy/&quot;&gt;harmony 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;우리 팀은 어떤 방식으로 협업 하는지를 소개하고자 한다. 큰 주제는 다음과 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;하모니 팀의 Git Flow 전략&lt;/li&gt;
&lt;li&gt;KPT 및 회고 문화&lt;/li&gt;
&lt;li&gt;코드리뷰, 테스크 수행 문화&lt;/li&gt;
&lt;li&gt;글쓰기, 문서화 주도 문화&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;브랜치는-왜-필요한가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%8A%94-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%9C%EA%B0%80&quot; aria-label=&quot;브랜치는 왜 필요한가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;브랜치는 왜 필요한가?&lt;/h2&gt;
&lt;p&gt;Github 를 활용하여 작업을 진행하다보면 항상 독립적인 브랜치를 생성하고, 작업을 수행한뒤 PR 을 날려 팀원들의 검토를 받고 머지를 한다. 이 일련의 과정은 관례적으로 사용되는 브랜치 관리 전략인 Git Flow 전략 내에서 발생하는 흐름이다. 왜 우리는 브랜치를 분기하고, 작업을 진행할까? 디폴트 핵심 브랜치인 main 브랜치에서 여러명이 동시에 작업을 진행하고 커밋을 푸시하면 안될까?&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9b6c14e5f43904b8a47f226aa1c8b4cd/d74fe/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.03067484662577%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAvklEQVR42q3TsQ7CIBAG4L7/WxhfwMkHMHHRxEFRUBOHWo3p0EIF0yrwWwvWxUFbbgDuhg8OQoTAEX0vWzdKAptPfAYc5AXj86xZG2s7gOWpRtctmJYci2zfA3xwWMXaKr9LUBF3AX1oBSvmtWyalBVHxCp1m/0PvttOgOsSuxrbFknXR/GkP9mGTDEcDaDkDVrrPqCbszwHWRF3d8Z0B9vHEByM0YAg56BhQPsB6SbcCUXTMgsHvpCqqn76y0+dAAIH3r9lJgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/9b6c14e5f43904b8a47f226aa1c8b4cd/a6d36/image.png&quot;
        srcset=&quot;/static/9b6c14e5f43904b8a47f226aa1c8b4cd/222b7/image.png 163w,
/static/9b6c14e5f43904b8a47f226aa1c8b4cd/ff46a/image.png 325w,
/static/9b6c14e5f43904b8a47f226aa1c8b4cd/a6d36/image.png 650w,
/static/9b6c14e5f43904b8a47f226aa1c8b4cd/e548f/image.png 975w,
/static/9b6c14e5f43904b8a47f226aa1c8b4cd/d74fe/image.png 1164w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;main 브랜치는 이미 서비스가 출시되어 배포된 코드만을 모아두기 위한 브랜치이다. 만약 이 하나의 브랜치에 여러 사람들이 커밋을 푸시하면 어떻게될까? 이 하나의 브랜치에 여러 사람이 한꺼번에 작업을 진행하고 관리한다면, main 브랜치의 소스코드는 정말 불안정한 상태로 관리 될 것이다.&lt;/p&gt;
&lt;p&gt;다른 사람이 작업 또한 누군가 내 파일을 건들였을 경우 충돌이 발생할 가능성도 충분하다. 충돌을 만회하고자 뒤늦게 커밋을 롤백하자니, 내가 작업한 모든 내용을 삭제하고 처음부터 다시 작업을 진행해야 할 수도 있다. 브랜치에 작업한 내용을 PR 을 요청할 떄와 달리 내가 작업한 커밋이 충돌이 발생할지 체크할 방법도 존재하지 않기에, 커밋이 엉키고 꼬일 가능성이 충분하다.&lt;/p&gt;
&lt;p&gt;더 큰 규모의 조직에서 협업을 진행한다면 큰 문제가 발생할 것이다. 당연하게도 브랜치를 분기하지 않는다면, 각 사람이 main 브랜치로부터 독립적인 브랜치를 파생하여 동시간대에 작업을 진행하지 못하기에 작업 효율이 떨어진다. 오직 main 브랜치 하나에서 수 많은 개발자들에 협업한다면, 내가 작업중인 파일을 누군가 건드릴 수 있게된다. 이는 앞서 서술한 충돌 문제를 야기할 수 있다. 충돌을 만회할 수 있는 가장 안전한 방법은 다른 사람이 main 브랜치에 작업 내용을 푸시할 때 까지 대기하는 것인데, 작업 효율성이 매우 떨어지기에 좋은 방법이 아니다.&lt;/p&gt;
&lt;p&gt;또한 여러 기능을 개발하면서 남겨진 커밋 히스토리가 main 브랜치에 뒤죽박죽 섞여서 알아보기 힘든 로그가 쌓이게 된다. 엉켜있고 알아보기 힘든 커밋 히스토리가 쌓여있기에, 기획 변경으로 인해 특정 기능이 필요 없어졌을 때 또는 문제가 발생했을 떄 원하는 시점으로 롤백하기도 어려워진다. 반면 브랜치를 통해 관리했다면 원하는 시점을 가진 브랜치를 활용하여 작업 내용을 적절히 롤백시킬 수 있다.&lt;/p&gt;
&lt;p&gt;브랜치 기능을 사용하면 다른 브랜치에 영향을 받지 않는 독립적인 환경에서 기능을 개발할 수 있다. 또한 main 브랜치 하나에서 프로덕션 코드를 관리할 때와 달리, 프로덕션 코드의 안정성과 품질을 향상 시킬 수 있다. 독립적인 브랜치에 개발한 기능을 main 브랜치에 곧 바로 반영할 수 없기 떄문이다. main 브랜치에 내 코드를 기여하기 위해선, PR 을 먼저 요청해야한다. 팀 내 PR 가이드라인을 준수하여 팀원들의 리뷰와 검토를 받고, CI 를 통해 내 코드가 빌드에 성공했는지, 테스트 커버리지가 일정 수준 이상을 만족하여 프로덕션 코드에 반영해도 적절한지를 미리 검토할 수 있다. 또한 충돌이 발생하여 코드가 꼬일 가능성도 거의 0에 수렴한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;브랜치는-어떻게-관리하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;브랜치는 어떻게 관리하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;브랜치는 어떻게 관리하는가?&lt;/h2&gt;
&lt;p&gt;이렇게나 좋은 이점을 제공하는 브랜치 또한 일정한 규칙 없이 마구잡아로 사용한다면 엉키고 알아보기 힘들 수 밖에 없다. 브랜치 생성 및 관리에 대한 명확한 규칙이 없을 경우 &lt;strong&gt;&quot;이 브랜치는 어떤 기능을 개발하기 위해 생성된거지?, &quot;어떤 브랜치가 실제 프로덕션 및 배포 코드가 담긴 중요한 브랜치이지?&quot;, &quot;PR 은 어디에 요청해서 검토와 승인을 받아야히지?&quot;, &quot;이 커밋과 관련한 작업 내용이 담긴 브랜치가 어떤거였지?&quot;&lt;/strong&gt; 등 수 많은 혼동을 야기할 수 있다.&lt;/p&gt;
&lt;p&gt;이를 해결하고자 등장한 것이 Git 브랜치 전략이다. Git 브랜치 전략은 브랜치를 더 효과적으로 관리하기 위한 규칙이자 컨벤션이다. 우리 팀 또한 Git Flow 전략을 택했으며, 일부
팀 내 브랜치 규칙을 직접 만들어도 괜찮지만, 전셰게의 수 많은 팀 내에서 관례적으로 사용하고 있는 워크플로우일만큼 매우 효과적인 작업 효율을 보인다. 우리 팀 또한 Git Flow 브랜치 전략을 사용했다. 단, 대신 git-flow를 정석적으로 사용하지 않고 필요한 부분을 수정하여 반영했다. 이와 관련한 내용과 이유는 후술하도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;git-flow-전략&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#git-flow-%EC%A0%84%EB%9E%B5&quot; aria-label=&quot;git flow 전략 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Git Flow 전략&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cf268209b8c755cec06a7aa46fe34f38/9937c/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 119.6319018404908%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAYCAYAAAD6S912AAAACXBIWXMAAAsTAAALEwEAmpwYAAADqklEQVR42pVVf3OiSBDd7/9lbqtyt8nu3WVzxlWDu9GgEPwBAhEFEQQVRBTfdc8Gbk3FP26qptBh5vXr16+HD3gz9vs9giAQv3e7HY7HI/I8x3K5xOl0Ev8Ph4N4n2UZiqI4O/+h/FEeLgH5sO/72G63AqAETJNErPGIVyvktP9dQNu2q40clScDFMWpWuPBa+U0vQXWmw3W63V1VgAyo06nIxY4PWbEQGmyRBz5tHYQe6IoQhiGiOMYSZKiNhjjqdfD16+3UJT+f4AMYNuWYMGAe9KGsodtSLAmCmmVY0Xp8WQZWBYGjCkAs5vNHBGkAmT6k8kE/mIBw9Dx0GrCdRewjA7CYCo2ep6HVqsF7fkZCaUXRRSAAMsCnWnIi41GA/X6PUzLhNSWMBrrmFodBL6NdLcXgT7fXNO+b6JAzJIBOaNS07OijMdjOERd/65A6/ahyCqGWh2moaD+rYnHxx/wKIN+t4vJcCTOrOMIx7cMS2QGLE4F9AcFw6YM12BdHAKV0e8rQvSchPXNF9iqhpDAGvScux5mjiO0PQMcDAbID2RgfQazoyGer0hsFX25LYoSverFXuXiccpd6wXPmoY7qrJK2p4BPtMCm3SXZ5h0NSxNlzQiltGy6iDeyw2QpqlYS8iD/H9DT+6aM8DhcCgYrLh6zgLhPERPvoPvmeAt+32G6XSKdrstLCY0JMsw43c11HVdRFIVFaqmwiAb6SOFhA/JQi7q8i1kWcan6z/Ekwf7srRNVeVfU2Y73N3f4Ybs8V1qY+EvKT3q8eKI+6e/f1qFQDgwD+6aw6UqG7ohitJrPGJiGAjCFV5MmdZVRNQFzX4NlmlRyhLm87k4ExCBi4CsD9vG1WwxnaFNdiB7LGfifbP/DyzLQo96t7RI9F6nlIAc9Xg4It3vMFUNhLZHFc+pymtiuKKUb1+1KqpCxFSUi4Dcy/xyk2xh90YEuMT05Qc0tU2sFNiuIa4ovnVYO/ZiFAaXiyKMzYzSDeYDC6HlIcvpJnFeUKvVILUexO3CQOw9n/T71O1hpI/RJxmY0M9efgXkWySnaOPaE0I/oKIE1Ckzoe3Hj7/h96urqhicMhOZE1OTdJUkiS4P45yhqqpkhzWK7ICMOiEMI0jNKxhjGZ+//IWbL9fVnVdqGL3nw7cfKa50ed2HgQt37mDqTFFXbinVDEmaVK0Xr8LLgL9+K8qX9oTazFSFKmUgNjVP9uCfTz1kxwtfvf87TgTubbY4vgYvAf8FwhIffzZX4B8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/cf268209b8c755cec06a7aa46fe34f38/a6d36/image-1.png&quot;
        srcset=&quot;/static/cf268209b8c755cec06a7aa46fe34f38/222b7/image-1.png 163w,
/static/cf268209b8c755cec06a7aa46fe34f38/ff46a/image-1.png 325w,
/static/cf268209b8c755cec06a7aa46fe34f38/a6d36/image-1.png 650w,
/static/cf268209b8c755cec06a7aa46fe34f38/e548f/image-1.png 975w,
/static/cf268209b8c755cec06a7aa46fe34f38/9937c/image-1.png 1156w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Git Flow 는 총 5가지 종류의 브랜치로 관리하는 전략이다. 가장 중요한 &lt;strong&gt;디밸롭 서버, 프로덕션 서버 실제 코드가 담기고 병합되는 main 브랜치, develop 브랜치가 존재하며, 이 2개의 중요한 브랜치에 병합되기 이전에 각기 다른 목적으로 분기되어 작업을 진행하는 목적을 가진 보조 브랜치들인 feature 브랜치, release 브랜치, hotfix 브랜치&lt;/strong&gt; 가 존재한다.&lt;/p&gt;
&lt;p&gt;main 브랜치와 develop 브랜치는 개발 프로세스 전반에 걸쳐 가장 중요하게 관리되는 브랜치이자, 항상 삭제되지 않고 유지되는 브랜치이다. 이 두 브랜치에 코드를 기여하기 위해선 보조 브랜치를 통해 작업을 진행하고, PR 을 요청해서 승인을 받아야한다. 반면 보조 브랜치(feature, relaese, hotfix) 는 필요할 때 마다 동적으로 생성되는 브랜치로, 제 역할을 다하고 develop (또는 main) 브랜치에 코드를 기여한 뒤에 제 역할을 다했다면 삭제된다. 보조 브랜치 덕분에 팀이 병렬적으로 동시간대에 작업을 진행할 수 있게된다.&lt;/p&gt;
&lt;h3 id=&quot;main-브랜치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#main-%EB%B8%8C%EB%9E%9C%EC%B9%98&quot; aria-label=&quot;main 브랜치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;main 브랜치&lt;/h3&gt;
&lt;p&gt;실제 대중에 배포되어 운영되기 직전(또는 이미 운영되고 있는) 프로뎍션 코드들이 담겨있는 브랜치이다. main 브랜치는 develop 브랜치와 마찬가지로 프로젝트 시작부터 끝까지 사라지지 않고 관리되는 핵심 브랜치다. 실제 프로덕션 코드가 담긴 중요한 브랜치인만큼, 후술할 develop 브랜치의 디밸롭 환경에서 수 많은 테스트와 검증을 거친 뒤에서야 main 브랜치에 반영해야 한다.&lt;/p&gt;
&lt;h3 id=&quot;develop-브랜치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#develop-%EB%B8%8C%EB%9E%9C%EC%B9%98&quot; aria-label=&quot;develop 브랜치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;develop 브랜치&lt;/h3&gt;
&lt;p&gt;main 브랜치에 병합될 다음 버전 배포를 앞두고 있는 코드들을 담고 있는 핵심 브랜치다. 실제 프로덕션 서버에서 돌아가는 코드들이 main 브랜치에서 관리된다면, 프로덕션 환경과 유사하게 구축한 디밸롭(테스트) 서버내의 코드들은 develop 브랜치에서 관리된다. 즉, develop 브랜치에 기여된 코드들은 테스트(디밸롭) 환경에서 많은 테스트 및 검증을 거친 뒤에야 출시해도 문제가 없다고 판단되었을 때 main 브랜치로 병합된다.&lt;/p&gt;
&lt;h3 id=&quot;feature-브랜치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#feature-%EB%B8%8C%EB%9E%9C%EC%B9%98&quot; aria-label=&quot;feature 브랜치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;feature 브랜치&lt;/h3&gt;
&lt;p&gt;하나의 기능을 개발하기 위한 브랜치이다. develop 브랜치로 부터 분기하여 생성하며, 해당 feature 브랜치에서 기능 개발이 완료되면 PR 요청을 날리고, 다시 develop 브랜치로 병합된다.&lt;/p&gt;
&lt;p&gt;머지시 주의할 점은 fast-forward 로 머지하지 않고, merge commit 을 생성하여 머지 해줘야한다. 그래야만 히스토리가 특징 기능 단위로 묶이게 된다.&lt;/p&gt;
&lt;h3 id=&quot;hotfix-브랜치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hotfix-%EB%B8%8C%EB%9E%9C%EC%B9%98&quot; aria-label=&quot;hotfix 브랜치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;hotfix 브랜치&lt;/h3&gt;
&lt;p&gt;이름 그대로 특정 문제가 발생했을 때, 해당 문제를 해결하기 위해 분기한 브랜치다. 정의상으로는 이미 배포된 프로덕션에 문제가 발생했을 때, 해당 문제를 해결하고자 분기하는 브랜치이다. 따라서 develop 브랜치가 아닌 main 브랜치에서 생성하고, 문제 해결이 완료되면 main 과 develop 브랜치 모두에 머지한다.&lt;/p&gt;
&lt;p&gt;다만, 정의상은 main 브랜치로부터 파생되는 것이 맞지만, develop 브랜치로 부터 파생되어도 전혀 상관없다는 생각이다. hotfix 브랜치가 등장한 이유는 문제를 해결하기 위함에 있는데,  실제 출시 단계가 아닌, 개발 단계에서 이미 문제를 발견했다면 develop 브랜치로부터 파생하고 문제를 해결하면 될 것이다.&lt;/p&gt;
&lt;h3 id=&quot;release-브랜치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#release-%EB%B8%8C%EB%9E%9C%EC%B9%98&quot; aria-label=&quot;release 브랜치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;release 브랜치&lt;/h3&gt;
&lt;p&gt;소프트웨어 배포를 준비하기 위한 브랜치이다. develop 브랜치로부터 분기하며, 버전 이름 등의 소소한 데이터를 수정하거나 배포전 사소한 버그를 수정하기 위해 사용된다. 배포 준비가 완료되었다면 main 과 develop 브랜치 모두에 머지한다. 이때, main 브랜치에는 태그를 이용하여 버전을 표시한다. Release 브랜치를 따로 운용함으로써, 배포 업무와 관련없는 팀원들은 병렬적으로 Feature 브랜치에서 이어서 기능을 개발할 수 있게된다. 추가적으로 네이밍은 &lt;code class=&quot;language-text&quot;&gt;release/v1.1&lt;/code&gt; 과 같은 형태로 생성한다.&lt;/p&gt;
&lt;p&gt;다만, release 브랜치는 팀 내 규모에 따라 협의하여 꼭 도입할지 검토해봐도 좋다고한다. 우리 하모니 팀의 경우도 release 브랜치는 도입할 근거가 마땅치 않으며, 불필요한 비용과 복잡성이 높아질 것으로 판단되어 도입하지 않았다.&lt;/p&gt;
&lt;h2 id=&quot;github-flow-전략&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#github-flow-%EC%A0%84%EB%9E%B5&quot; aria-label=&quot;github flow 전략 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Github Flow 전략&lt;/h2&gt;
&lt;p&gt;앞서 서술한 Git Flow 에 비해 Github Flow 는 더 단순해진 구조를 가진다. 다만, Github Flow 전략은 단순함으로 인해 개발이 편하다는 입장을 가진 사람들도 많지만, 내 생각은 다르다. 개인적인 생각으로는 Github Flow 는 매우 단순한 브랜치 구조로 관라하기 떄문에 브랜치 관리가 되려 힘들 것이라는 생각이다. 그러면 Github Flow 에 대해 알아보자.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7bb7a4905b7cc90a8415ec0b7111bc26/8cdda/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.171779141104295%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABpUlEQVR42n1Sa4/aMBDM//8rVfupQtVJPVr1IOEOhIBQCJCEcJAXDyV2HpAHU68h1bXq1dJmrdg73plZBfdVFAXmszmMuQHbtjEZT+CsHfieD9d1EQQBPM+TOQxDmCsTG2eDKIqQpikCP8DlcoHSAF6vV9R1LfeWaUF76kqgqqrkY3RGmSLPc3x/bGMyGsv/VEv3KCv0ybKMEJFczvAThkMUY7xYgCcJyrJEWZQIWIyZYNDTerBEd85uB2e7xZ4z8Hs9LYWQiUItcixeNvYBTM/FZG0jTrgA5cizFAPLwg9VhSpiPp2hp+uw3B2W4q53OKAuqxsg3lk8ZtiZDs4sAdtHOIWv+DkcwZou4W7XOO69Rqw/6pSiLOD7vgzGuKBYSQmYoG1PDQw7z9C1IVaLEXzbgdHXsV2vcDq6IMlJU5Kl0V92SHQbUSnILSY6NE2h1WYDxjkCL0Qcx8iELFmW43g83fojU+7G/JNyc0BOtz638CoAbbH/9OEjzOVKjsjDlwd8+9pGKkx7W/NfQJo5TdV+z6La6UrzSI7BSx/TsX6bjr8AfwHGD6kVj9myPQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7bb7a4905b7cc90a8415ec0b7111bc26/a6d36/image-2.png&quot;
        srcset=&quot;/static/7bb7a4905b7cc90a8415ec0b7111bc26/222b7/image-2.png 163w,
/static/7bb7a4905b7cc90a8415ec0b7111bc26/ff46a/image-2.png 325w,
/static/7bb7a4905b7cc90a8415ec0b7111bc26/a6d36/image-2.png 650w,
/static/7bb7a4905b7cc90a8415ec0b7111bc26/e548f/image-2.png 975w,
/static/7bb7a4905b7cc90a8415ec0b7111bc26/8cdda/image-2.png 1168w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Github Flow 는 위와 같이 &lt;code class=&quot;language-text&quot;&gt;main&lt;/code&gt; 브랜치와 &lt;code class=&quot;language-text&quot;&gt;feature&lt;/code&gt; 브랜치 두 종류가 끝이다. 단순히 기능 개발을 하고 master 브랜치에 병합하는 매우 단순한 구조다.&lt;/p&gt;
&lt;h3 id=&quot;main-브랜치-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#main-%EB%B8%8C%EB%9E%9C%EC%B9%98-1&quot; aria-label=&quot;main 브랜치 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;main 브랜치&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;항상 Stable한 상태여야 한다.&lt;/strong&gt; 이때, Stable하다는 것은 Main의 모든 커밋은 언제 배포하든 문제 없어야하고, 언제든 브랜치를 새로 만들어도 문제가 없어야 한다. main 브랜치의 모든 커밋은 빌드가 되고, 테스트를 통과해야한다. 이것이 Github Flow가 강제하는 유일한 사항이다.&lt;/p&gt;
&lt;h3 id=&quot;feature-브랜치-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#feature-%EB%B8%8C%EB%9E%9C%EC%B9%98-1&quot; aria-label=&quot;feature 브랜치 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;feature 브랜치&lt;/h3&gt;
&lt;p&gt;앞서 살펴봤던 git flow 전략의 feature 브랜치와 동일한 역할을 수행하며, 여기서 hotfix 브랜치의 역할 또한 합쳐진 브랜치라고 생각하면 된다. 즉, 기능 개발과 버그 수정 모두 구분없이 동반되어야 하는 브랜치다. 또한 앞선 Git Flow 전략과 달리 Github Flow 에선 main, develop 가 구분되어 있지 않다. 따라서 main 브랜치에 병합되기 이전의 코드들은 언제든 실제 프로덕션 코드에 문제없이 기여될 수 있도록 상시로 완벽하게 준비되어야 한다.&lt;/p&gt;
&lt;h2 id=&quot;github-flow-가-과연-적합할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#github-flow-%EA%B0%80-%EA%B3%BC%EC%97%B0-%EC%A0%81%ED%95%A9%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;github flow 가 과연 적합할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Github Flow 가 과연 적합할까?&lt;/h2&gt;
&lt;p&gt;어떠한가? 과연 Github Flow 가 단순하다고 해서 좋은 전략일까? 몰론 매우 단순한 구조로 인해 깃허브를 갓 시작한 입문자들에게는 도움이 될 수 있겠다. 하지만 적어도 Git flow 전략처럼 프로덕션과 디밸롭 브랜치가 구분되어 있지도 않다. 또한 feature 브랜치가 버그 수정까지 모두 전담해야 하기에, 역할이 더 커진 구조이다.&lt;/p&gt;
&lt;p&gt;개발 팀이 매우 작은 소규모 애자일 팀이고, 제품을 정말 빠르게 출시해도 문제 없다면 Github Flow 를 채택해도 괜찮을 것이다. 하지만 많은 웹 애플리키이션은 테스트와 검토를 디밸롭 서버에서 충분히 검토해야 하기에 develop 브랜치는 꼭 필요하다. 또한 기능과 버그를 구분하지 않고 브랜치를 파생하고 관리하다보니, 관리가 다소 힘들어질 수 있다는 생각이다. 이 떄문에 우리 하모니 팀 또한 Github Flow 를 선택하지 않았다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;장기간 프로젝트가 존재하고, 유지보수를 위한 작업을 수행해야 하는 팀은 Git Flow 가 타당한다. 반면 상시로 정말 빠르게 배포해야 하는 팀은 Github Flow 가 적합하다. - Scott Chacon&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;하모니-팀에-알맞게-수정된-git-flow-전략&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%98%EB%AA%A8%EB%8B%88-%ED%8C%80%EC%97%90-%EC%95%8C%EB%A7%9E%EA%B2%8C-%EC%88%98%EC%A0%95%EB%90%9C-git-flow-%EC%A0%84%EB%9E%B5&quot; aria-label=&quot;하모니 팀에 알맞게 수정된 git flow 전략 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;하모니 팀에 알맞게 수정된 Git Flow 전략&lt;/h2&gt;
&lt;p&gt;Git FLow 의 부정적인 견해중 하나는, 이 전략은 빠르게 급변하는 웹 서비스에는 맞지 않은 브랜치 전략이라는 점이다. 관리해야 할 브랜치가 늘어나기 때문에 개발자들이 신경써야 할 포인트가 늘어난다.&lt;/p&gt;
&lt;p&gt;빈번한 배포로 인해 급작스러운 이슈가 발생할 수 있다. 즉 예상치 못한 롤백이 자주일어날 수 있다. 또한 웹 서비스의 특성상 다양한 릴리즈 버전을 유지할 필요가 없다. 이러한 특성들로 인해 웹 서비스에는 다소 보수적인 Git flow 전략은 맞지 않을 수 있다.&lt;/p&gt;
&lt;p&gt;그럼에도 우리 하모니 팀은 Git Flow 를 선택했다. 더 정확히는, 정석적인 Git Flow 전략을 일부 수정해서 사용중이다. 우리는 실제 운영할 수 있는 서비스를 개발하며 다양한 경험을 습득해야 한다. 또한 대부분의 팀원들이 git에 익숙하지 않았으며 다양한 시도를 통해 빠르게 학습해야 한다.&lt;/p&gt;
&lt;p&gt;결론적으로, 우리 팀은 &lt;strong&gt;현재 수준에서 develop에서 대부분의 빌드를 진행하기 때문에 release 브랜치의 필요성이 옅어졌다. 결국 release를 제외한 main, develop, feature, hotfix만 사용하기로 결정하였다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;kpt-및-회고-문화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#kpt-%EB%B0%8F-%ED%9A%8C%EA%B3%A0-%EB%AC%B8%ED%99%94&quot; aria-label=&quot;kpt 및 회고 문화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;KPT 및 회고 문화&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6dd69a54e8931846cab97f6afde83403/7e21b/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 101.22699386503066%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAACpklEQVR42o1VXU8TQRTltfAEpO1S+rEf/WA/y1agSNMX4E+JKPBX1B9iYmIiTyqIiQ8SI4hs10pLabfdLcc7A4sILbXJye5M5545994zs2MzMzMIMTs7i3QqhSRDMokUPR/C7dgQY+GLIAhIE4llWZyYjWOxGKLR6D9gc4OI7hHG43Goqoq1tTWsrq5ifX0dtm1DLRSgqRo0TYWma8jncnytEBeGE7L0JiYm8HRzE57XwdHxMb6fODj+WbuGQ3OncNw6vnw9xOs3b/Hi5aubTO4RsnpEIhFsbW2D/Xw/QLd/ie4l4NG4R6gf9nFRo7leAHSAg4PPPI4RJhKJuwpTGB+P4PmzbbSOgGajBedbG3XHRyvoo+X1UXM6OP3RxnknQJ82eP9xbzjhXYUdz0PrgohIScPz0SSS83OfVAf07iPwgQ97B6MJd3Z2OGEQBPCvU2VPz7/Eyacu2s0+2vQfusD+/oMpJynlcTzZ2ECv14PrunDPGoQm3N8N/u64DdR+EepnuPB7eLe7y+MGEoa2YZaorjxGtVpFpVLBcrmMysoK5ueLsIrWFSyTe1XTtNHGZjsatNDQdeTyeWSzWb47M/Tk5OQNpqamMD09PZpQFNN4ZBexVF5GmdQtLi6iVCpx5GmDAplcURS+/naaQwllScbCgk3KcshR+mGNQgJmZIbRRy9BhDEBkiWjtFSCaZjQKW0GwzB4zUzT5HWTJIlDFMWBKv8qpLOZVlLQizokkQIkkQcxhASZTIarDjEy5Uw6jUI+SzcKHX4hzucYmAP4hXA9DvEfTclgvmje1ImlNKz4/3V9zSky9DkVUbrzQlUMsVvjYcoGNiVrKzBtE0XzyrisKawZYVNYg5g3ZVnmdX24KbRzSiEfLrAuG/yyZdYJg0OM+gT8AUtPwjhT7DR3AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/6dd69a54e8931846cab97f6afde83403/a6d36/image-4.png&quot;
        srcset=&quot;/static/6dd69a54e8931846cab97f6afde83403/222b7/image-4.png 163w,
/static/6dd69a54e8931846cab97f6afde83403/ff46a/image-4.png 325w,
/static/6dd69a54e8931846cab97f6afde83403/a6d36/image-4.png 650w,
/static/6dd69a54e8931846cab97f6afde83403/e548f/image-4.png 975w,
/static/6dd69a54e8931846cab97f6afde83403/3c492/image-4.png 1300w,
/static/6dd69a54e8931846cab97f6afde83403/7e21b/image-4.png 1626w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이 외에도 우리 팀은 단순 Git Flow 전략을 포함하여 효율적인 협업을 위해 신경쓰는 요소들이 있다. 그 중 하나는 &lt;code class=&quot;language-text&quot;&gt;KPT&lt;/code&gt; 및 &lt;code class=&quot;language-text&quot;&gt;감정 회고&lt;/code&gt; 이다. 평일(월~금) 매일 팀 회의가 진행되며, 간단한 스크럼을 공유하고, 각 파트의 이슈 및 특이사항을 공유한다. 이후 하루 일과가 끝나갈 때인 오후 5시 30분에 다시 모여 작업 및 간단한 사항을 공유하고, 금일 내용에 관련하여 KPT 및 감정 회고를 진행한다.&lt;/p&gt;
&lt;p&gt;우리 팀이 KPT 회고를 하는 목적은, 짧은 시간에 모든 구성원의 생각을 공유하고, 실행 가능하고 측정 가능한 Action 을 도출해냄에 있다 Action 이란 다음 회고까지 실행하는 의견이다.&lt;/p&gt;
&lt;h3 id=&quot;코드리뷰-테스크-수행-문화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BD%94%EB%93%9C%EB%A6%AC%EB%B7%B0-%ED%85%8C%EC%8A%A4%ED%81%AC-%EC%88%98%ED%96%89-%EB%AC%B8%ED%99%94&quot; aria-label=&quot;코드리뷰 테스크 수행 문화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;코드리뷰, 테스크 수행 문화&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/65fa04cb6161da803a9cad46f5470c5e/c6162/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.48466257668711%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACB0lEQVR42n2SaVPbMBCG+diCHV/yId+3YyexkyYhTUthOsP//0tvdxUwBDp8eGallfSu9rjRLR/Mwg4gZQUZ1IhkgTztEMUlPD+FZUvYTvglrzo37wW7scJ07FD0HdYPz6g3E6I8+ZK4SBFmyf8FRZ7BpkumjOEUSxgygSY8aK5/DfuEP58tfPlZkGm6Ef16j4psnDUIopJsCxlX8MMCAZUgTGvlC5MaSd6h6SfYbgzN9D7/0BQh9j//4PT7L86Pzzien3A4P5KPOF38w3iggBtFScRFB8uNoH8UVKKWh3K5RVSuIZMSJl28MwRFd2d7uxD4ptm41R18122FbnnXKRsO/c5L4FB3Tb/Aws2gOxEWIiHiGcNNiFRheilsP4dFGOJdUzh34UUI0h7lcEJUjUiaHeJ6i7Cc4KUruMkAEfcwg2bGDjt42QZuuoYl2zdBrh2nIanY28MvpOUSflTMRNQcRiaVqrHhSLJSZWWIl7X4OIckysLjjzM14QntsMVqOqKmDnokGpKg6m7ezpYDvK5dmV8LaoRFNeQO3lMnd/cPWI1HtW9Xu0uA7REdrftxT8EO6Dd7Jcaj5ATZ9djc0cangzU94scD/W7cn2l/j83upIS4FHk9oBl2amT43kQlasiGaaM03n7IgjTEnCYLsBjDNWUf05IQC9ddfxnwlzIwnJ1mXlL+BwEcW+w8RhajAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/65fa04cb6161da803a9cad46f5470c5e/a6d36/image-3.png&quot;
        srcset=&quot;/static/65fa04cb6161da803a9cad46f5470c5e/222b7/image-3.png 163w,
/static/65fa04cb6161da803a9cad46f5470c5e/ff46a/image-3.png 325w,
/static/65fa04cb6161da803a9cad46f5470c5e/a6d36/image-3.png 650w,
/static/65fa04cb6161da803a9cad46f5470c5e/e548f/image-3.png 975w,
/static/65fa04cb6161da803a9cad46f5470c5e/3c492/image-3.png 1300w,
/static/65fa04cb6161da803a9cad46f5470c5e/c6162/image-3.png 2166w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;테스크는 Github 이슈와 Proejcts, 그리고 마일스톤을 활용하여 관리한다. Github 의 &lt;code class=&quot;language-text&quot;&gt;PR(Pull Requests)&lt;/code&gt; 를 활용하여 각자 작업한 내용에 대해 코드 리뷰를 주고받는다. 코드 리뷰가 끝나고, CI 가 정상적으로 통과되고, 리뷰어의 어프루브(Approve) 를 받으면 develop 브랜치에 병합할 수 있다.&lt;/p&gt;
&lt;h4&gt;커뮤니케이션 비용을 줄이기 위한 Pn 룰&lt;/h4&gt;
&lt;p&gt;코드 리뷰는 &lt;a href=&quot;https://blog.banksalad.com/tech/banksalad-code-review-culture/&quot;&gt;뱅크샐러드의 코드 리뷰 문화&lt;/a&gt;로 부터 큰 영감을 받았다. 우리 팀은 커뮤니케이션 비용을 줄이기 위한 Pn 룰을 적용하고 있다. 우리 팀은 코드 리뷰의 코멘트에 Pn 룰을 사용하여 리뷰어가 코멘트를 강조하고 싶은 정도를 표현한다.&lt;/p&gt;
&lt;h3 id=&quot;글쓰기-문서화-주도-문화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%80%EC%93%B0%EA%B8%B0-%EB%AC%B8%EC%84%9C%ED%99%94-%EC%A3%BC%EB%8F%84-%EB%AC%B8%ED%99%94&quot; aria-label=&quot;글쓰기 문서화 주도 문화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;글쓰기, 문서화 주도 문화&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6ca0017aa4669ec8bddb5e336721cbb7/8126d/image-6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.16564417177914%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB70lEQVR42o2TS47TQBCGcziOww04AJobIC7AsEcs0EhZDSs2RIntZEIexInj9zt2+/VTVaMY44iBlkpd7m5/VfVX90QphbquUVUV2FcVfTcVOgCZ/xmf3r3C8vtbmOYGmqYjCAJkaYK2a9C0tzZxXRe6pmGxWGC/36PrOrIWPOpSw8PH1zCN96hqhTRJEYahnPnbmJwsC7ufB4RxgiTL0DRNv1kUCrbtktehbVsJ9BJMgJ7nww9ClFQu2xB4ueQEtGmtFmMo77PPMtV1IzOv90DfJ6DvkYZKdBwClaoQxbGUGUUR4oSqSFNZL8tSNC9oZmgPzKjMmH7izTGQI/OP1wz+Va4AD4cDHMeRKGpUMgc4n20JmFJmCWdIxknwPMysB263WxyPRwGNgXme4+vjI1arFWazGXRdh2EYYvP5XKBd3aLK1W+g7bjwSMfrXRwC2Wf9eO/Pcq9+AffhDc73d+gu1TPQtY9wbIu0UjcZsnZ8T1kSz/PE+NvzXAqU0okAP77c4dv9B+pg/gyM/CdEoUXdTKQBYyBnyK+DZ9aSu81+URSSaZKU8JLBteErwKVebQj8v8HlD4BD2BjI2u12O5xOJ1j0otbrNZbLpaxtNhvp/E2XXwJyWdPpVICsnWmaYgznF1SoEqW5QPZk9H36BY37N8RD0mx4AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/6ca0017aa4669ec8bddb5e336721cbb7/a6d36/image-6.png&quot;
        srcset=&quot;/static/6ca0017aa4669ec8bddb5e336721cbb7/222b7/image-6.png 163w,
/static/6ca0017aa4669ec8bddb5e336721cbb7/ff46a/image-6.png 325w,
/static/6ca0017aa4669ec8bddb5e336721cbb7/a6d36/image-6.png 650w,
/static/6ca0017aa4669ec8bddb5e336721cbb7/e548f/image-6.png 975w,
/static/6ca0017aa4669ec8bddb5e336721cbb7/3c492/image-6.png 1300w,
/static/6ca0017aa4669ec8bddb5e336721cbb7/8126d/image-6.png 2316w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;서로가 테스트를 진행하며 학습하거나 경험한 내용을 팀원 모두에게 공유하기 위해 팀 기술 블로그를 운영하고 있다. 우리 팀이 강조하는 문화 중 소프트 스킬 역량 중 하나가 &lt;code class=&quot;language-text&quot;&gt;글쓰기&lt;/code&gt; 이다. 글쓰기를 통해 단순 개발 외적인 문서화 역량을 끌어올리고자 모두 노력한다. 또한 &lt;code class=&quot;language-text&quot;&gt;Github Wiki&lt;/code&gt; 내에 작업한 내용을 문서화시키고 있다.&lt;/p&gt;
&lt;p&gt;추가적으로 기술 블로그에 작성된 글을 토대로 자체 세미나를 운영하여 학습한 내용, 트러블 슈팅에 대해 공유하고 이야기를 나누는 활동 진행 예정에 있다.&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이렇게 우리 하모니 팀의 협업 프로세스에 대해 간단히 소개해봤다. 앞으로도 좋은 글쓰기 문화로, 선순환을 돕는 좋은 글들을 우리 팀 기술 블로그에 올려보고자 한다 😁&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Slf4j logback 이란 무엇이며, 왜 로깅을 해야할까? 💁‍♂️]]></title><description><![CDATA[…]]></description><link>https://haon.site/spring/logging-slf4j/</link><guid isPermaLink="false">https://haon.site/spring/logging-slf4j/</guid><pubDate>Tue, 06 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습-동기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5-%EB%8F%99%EA%B8%B0&quot; aria-label=&quot;학습 동기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습 동기&lt;/h2&gt;
&lt;p&gt;현재 카테부 하모니 팀에서 로깅의 필요성을 느끼지 못하여 제대로 도입을 하지 않았다. 이전까지는 테스트 코드를 제대로 짜는것에도 급급해서 로깅에 대해 신경쓰지 않았는데, 슬슬 로깅에도 신경써야 할 타이밍이 된 듯하다. 애플리케이션이 점차 커지고, 로직이 많아지다보니 간단한 출력문만으로는 힘들다는 것을 느꼈다. 테스트만큼이나 로깅에도 신경쓸 타이밍이 된 듯 하다.&lt;/p&gt;
&lt;p&gt;이번 포스트에선 로깅의 목적, Logback 과 Slf4j 등의 혼동되는 개념에 대해 생각을 정교화하고, 로그 레벨은 어떤것이 있으며 어떤 상황에서 어떻게 로그를 저장하고 추적해야하는지 다루어보고자 한다. 이미 알고있는 사소한 개념일지라도 &lt;code class=&quot;language-text&quot;&gt;메타인지&lt;/code&gt; 활성화를 위해 글을 적는다. 사실 second brain 에 개념을 정리해봤는데, 생각이 잘 정교화되지 않는 것 같아 블로그에 글을 발행한다. 특히 &lt;code class=&quot;language-text&quot;&gt;System.out.println()&lt;/code&gt; 과의 차이점, 그리고 이 단순 출력문대신 굳이 로깅을 해야하는 명확한 이유에 대해서도 정리해본다.&lt;/p&gt;
&lt;h4&gt;MDC 학습 배경&lt;/h4&gt;
&lt;p&gt;또한 로깅에 대해 학습하고 프로젝트에 도입하던 중 &lt;code class=&quot;language-text&quot;&gt;MDC(Mapped Diagnostic Context)&lt;/code&gt; 이라는 훌륭한 로깅 기법도 알게 되었다. 향후 MDC 에 대해서도 프로젝트에 도입을 고려하고 있기 때문에, MDC 에 대한 기초 개념 또한 간단히나마 다루고자 한다.&lt;/p&gt;
&lt;p&gt;다만 MDC 는 지금 당장 우리 팀에 도입할 생각은 없다. &lt;strong&gt;MDC 와 같은 효과적인 로그 추적 기법없이 Slf4j 기반 기본 로깅, 로그 파일을 저장하는 방식을 도입하여 MDC 의 필요성을 직접 체감한 후에 도입할 예정이다.&lt;/strong&gt; 그럼에도 지금 학습하는 이유는 MDC 의 특징을 사전에 이해하고 있어야만 이후 로깅 전략을 리팩토링시 좋은 방향성으로 나아갈 수 있을것이라는 생각 때문이다 🙂&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why&quot; aria-label=&quot;why permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why?&lt;/h2&gt;
&lt;p&gt;왜 로깅을 해야하는가? &quot;왜&quot; 라는 질문은 내가 새로운 기술을 학습할 때 가장 먼저 떠올리는 키워드이다.&lt;/p&gt;
&lt;p&gt;당연하게도 시스템이 작동될 떄 그 작동 상태의 기록이 보존되어야 하거나 이용자의 습성 분석을 위한 로그를 기록해 둘 필요가 있다. 하지만 보통 (나도 예전에 그랬고) JAVA 프로그래밍을 학습한지 얼마 안된 상태에선 &lt;code class=&quot;language-text&quot;&gt;System.out.println()&lt;/code&gt; 출력문으로 로그를 출력하면서 나름의 간단한 로깅을 학습한다. 왜 이런 간편하고 익숙한 출력문을 지양하고 Logging 을 하는가? 로깅의 이유는 System.out.println 과 비교하면 도입의 근거를 이해하기 수월해진다. 따라서 System.out.println 과의 비교를 통해 로깅을 학습해보자.&lt;/p&gt;
&lt;h3 id=&quot;1-성능에-부하를-줄일-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EC%84%B1%EB%8A%A5%EC%97%90-%EB%B6%80%ED%95%98%EB%A5%BC-%EC%A4%84%EC%9D%BC-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;1 성능에 부하를 줄일 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 성능에 부하를 줄일 수 있다.&lt;/h3&gt;
&lt;p&gt;System.out.println 은 리소스를 꽤나 많이 잡아먹는다. 리소르를 많이 사용하기 떄문에 성능에 부하를 준다. 반면 Logger 는 내부 버퍼링과 멀티 쓰레드를 지원해주기 떄문에 성능이 상대적으로 좋다. 그렇다면 왜 리소스를 많이 잡아먹는가?&lt;/p&gt;
&lt;p&gt;println 의 경우 아래와 같은 &lt;code class=&quot;language-text&quot;&gt;newLine()&lt;/code&gt; 이라는 메소드를 호출함으로써 출력이 수행된다. 우리가 유심히 관찰해야 할 키워드는 &lt;code class=&quot;language-text&quot;&gt;synchronized&lt;/code&gt; 이다. &lt;code class=&quot;language-text&quot;&gt;synchronized&lt;/code&gt; 는 &lt;a href=&quot;https://haon.blog/java/concurrency-keyword&quot;&gt;자바의 동시성 제어 키워드 : volatile 과 synchornized&lt;/a&gt; 에서도 다루었듯이, 한 임계영역에 대해 쓰레드를 하나씩 진입시켜 작업을 수행하는 방법으로, 베타적으로 실행된다. 하지만 이는 서비스에 트래픽이 발생할 경우 심각한 성능 저하가 발생할 수 있다는 주요 요인이 된다. 가령 트래픽이 많은 서비스에서 이 방법으로 로깅을 시도한다면 100만개의 쓰레드가 동시에 임계영역에 진입한다고 생각해보자. 운이 좋지 못한 쓰레드는 얼마가 지나서야 로그를 조회할 수 있겠는가? 🤔&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;newLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;ensureOpen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            textOut&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-다양한-상황에-알맞는-상세한-정보를-제공받을-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EB%8B%A4%EC%96%91%ED%95%9C-%EC%83%81%ED%99%A9%EC%97%90-%EC%95%8C%EB%A7%9E%EB%8A%94-%EC%83%81%EC%84%B8%ED%95%9C-%EC%A0%95%EB%B3%B4%EB%A5%BC-%EC%A0%9C%EA%B3%B5%EB%B0%9B%EC%9D%84-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;2 다양한 상황에 알맞는 상세한 정보를 제공받을 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 다양한 상황에 알맞는 상세한 정보를 제공받을 수 있다.&lt;/h3&gt;
&lt;p&gt;System.out.println 은 직접 코드를 작성해 우리가 원하는 정보를 만들어야 한다는 매우 번거로운 단점이 존재한다. 반면 &lt;strong&gt;Logger 는 클래스의 이름, 시간, 레벨 등의 다양한 정보를 제공해준다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이를 다르게 해석해보면, System.out.println 를 사용헀을 떄 유의미한 정보를 제공받기가 힘들다. 우리가 로깅을 해야하는 근본적인 이유는 서버에 장애가 발생했을 떄, 또는 개발 도중 중요한 로직을 처리후 매우 상세한 정보를 제공받길 원하여 사용한다. 또한 반드시 유익한 정보만을 로그로 남기고, 필요없는 로그를 최소화하여 불필요한 용량 낭비를 최소화시켜야 한다. 이런면에서 단순 출력문은 적합하다고 할 수 없다.&lt;/p&gt;
&lt;p&gt;반면 Slf4j 와 같은 Logging 라이브러리를 활용하면 &lt;code class=&quot;language-text&quot;&gt;TRACE&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;DEBUF&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;INFO&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;WARN&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ERROR&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;FATAL&lt;/code&gt; 과 같은 다양한 타입의 로깅 레벨을 알맞게 제공받을 수 있다. 또한 앞서 언급했듯이 시간, 이름에 대한 상세한 정보를 제공해준다. 단순 출력문으로 항상 동일한, 무의미한 System.out.println 에 비해 Logger 는 다양한 상황에 알맞게 적절한 로그를 출력하고, 저장할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;3-로그를-기록할-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%EB%A1%9C%EA%B7%B8%EB%A5%BC-%EA%B8%B0%EB%A1%9D%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;3 로그를 기록할 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 로그를 기록할 수 있다.&lt;/h3&gt;
&lt;p&gt;로그의 내용을 콘솔 밖에서 관리하고자 한다면 코드를 통해 로그 파일을 만들어야한다. 반면 Logger 는 &lt;code class=&quot;language-text&quot;&gt;appender&lt;/code&gt; 를 통해서 쉽게, 또 원하는 조건, 원하는 형태에 따라 로그를 파일로 남길 수 있다.&lt;/p&gt;
&lt;p&gt;반면 System.out.println 를 로그로 남긴다면 기록할 수 있겠는가? 이는 단순히 파라미로 넘겨주는 문자열만을 출력하는 기능이다. 만일 가능한다고 한들, System.out.println 로 얻는 이점이 존재하는가? 우리가 로깅을 해야하는 이유에 대해 다시금 짚어보자. 로그를 출력하고, &quot;저장하는&quot; 이유는 유의미한 정보를 조회하여 발생한 장애를 해결하는데에 있다. 하지만 단순 출력문 만으로는 어떤 애러인지, 어떻게 해결해야하는지 파악하기 힘들다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;logging-framework&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#logging-framework&quot; aria-label=&quot;logging framework permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Logging Framework&lt;/h2&gt;
&lt;p&gt;이 정도면 로깅을 해야하는 이유에 대해 파악했다고 생각한다. 자바 진영에서 제공하는 Logging 기법, 라이브러리는 무엇이 있을까?&lt;/p&gt;
&lt;p&gt;Logging 관련 프레임워크는 대표적으로 &lt;code class=&quot;language-text&quot;&gt;log4j&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;logback&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;log4j2&lt;/code&gt;, 그리고 그것을 &lt;strong&gt;통합해서 인터페이스로 제공하는 Slf4j 라이브러리가 있다.&lt;/strong&gt; 역사적으로 log4j, logback, log4j2 순서대로 등장했으며,logback 과 log4j 는 둘 다 log4j 를 기반으로 하고 있기 때문에 사용 방법이 꽤 유사하다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 log4j 는 2015년 이후로 deprecated 되었다. 따라서 이에 대한 설명은 제외한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;logback&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#logback&quot; aria-label=&quot;logback permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;logback&lt;/h3&gt;
&lt;p&gt;logbaack 은 log4j 이후에 출시되어, 보편적으로 사용되고 있는 JAVA 진영의 Logging 프헤임워크다. &lt;code class=&quot;language-text&quot;&gt;Slf4j&lt;/code&gt; &lt;strong&gt;인터페이스의 구현체로써 동작한다는&lt;/strong&gt; 것이 가장 큰 특징이다. (내가 Slf4j 와 logback 의 차이가 다소 혼동되었는데, 간단히 인터페이스와 구현체의 차이였다.)&lt;/p&gt;
&lt;p&gt;slf4j 의 구현체로 동작하기 떄문에 spring-boot-starter-web 안에 spring-bott-starter-logging 이 logback 이 기본적으로 포함되어 있어서 별다른 의존성 추가 없이 사용 가능하다. 우리가 일반적으로 &lt;code class=&quot;language-text&quot;&gt;LoggerFactory&lt;/code&gt; 를 직접 선언하거나, 또는 &lt;code class=&quot;language-text&quot;&gt;@Slf4j&lt;/code&gt; 어노테이션을 통해 로깅을 구현할텐데, 이에 대한 구현체는 logback 이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;로깅-레벨의-종류와-적절한-사용-단계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EA%B9%85-%EB%A0%88%EB%B2%A8%EC%9D%98-%EC%A2%85%EB%A5%98%EC%99%80-%EC%A0%81%EC%A0%88%ED%95%9C-%EC%82%AC%EC%9A%A9-%EB%8B%A8%EA%B3%84&quot; aria-label=&quot;로깅 레벨의 종류와 적절한 사용 단계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로깅 레벨의 종류와 적절한 사용 단계&lt;/h2&gt;
&lt;p&gt;앞서 설명했듯이 Logging 시 5가지의 레벨이 존재하며, 로그를 섲어할 떄 레벨을 설정하여 원하는 로그만을 출력할 수 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;error : 사용자 요청을 처리하는 중 발생한 문제 (예상치 못한 에러가 발생했을 때)&lt;/li&gt;
&lt;li&gt;warn : 처리 가능한 문제이지만, 향후 시스템 에러의 원인이 될 수 있는 문제 (예상한 예외 상황이며, 당장 문제가 발생하진 않아도 문제가 우려되는 경우. 가령 트래픽이 순간적으로 몰리는 상황)&lt;/li&gt;
&lt;li&gt;info : 로그인이나 상태 변경과 같은 정보성 메시지 (중요한 비즈니스 로직 처리 결과)&lt;/li&gt;
&lt;li&gt;debug : 개발시 디버깅 목적으로 출력하는 메시지 (개발 단계에서 주로 사용. SQL 로깅에 적합)&lt;/li&gt;
&lt;li&gt;trace : debug 보다 좀 더 상세한 메시지 (마찬가지로 개발 단계에서 사용. 특정 로직을 상세하게 추적하고 싶을 경우 활용한다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;프로젝트마다 어떤 상황에서 어떤 레벨까지 도입해야할지는 상이하다. 다만 현재 우리 프로젝트에선 3가지 단계 &lt;code class=&quot;language-text&quot;&gt;error&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;warn&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;info&lt;/code&gt; 만을 도입해도 충분할 것이라고 판단된다. 가령 &lt;code class=&quot;language-text&quot;&gt;error&lt;/code&gt; 는 ControllerAdvice 와 같은 예기치 못한 예외처리 상황에 로깅하기에 매우 적합해보인다. (이미 활용중이기도 하고.) warn 또한 정의 그대로 우려되는 문제에 대해 로깅이 필요할 것으로 판단되며, 당연하게도 비즈니스 로직에 대한 로깅시 &lt;code class=&quot;language-text&quot;&gt;info&lt;/code&gt; 가 유용하게 쓰일 것으로 생각된다.&lt;/p&gt;
&lt;p&gt;한편 아직까진 debug 와 trace 레벨까지 파고들어 의미있는 정보를 추출해낼 필요성을 느끼지 못했기에, 향후 필요성을 느낀다면 도입을 고려해야겠다.&lt;/p&gt;
&lt;h3 id=&quot;최소-로그-레벨-지정하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B5%9C%EC%86%8C-%EB%A1%9C%EA%B7%B8-%EB%A0%88%EB%B2%A8-%EC%A7%80%EC%A0%95%ED%95%98%EA%B8%B0&quot; aria-label=&quot;최소 로그 레벨 지정하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;최소 로그 레벨 지정하기&lt;/h3&gt;
&lt;p&gt;로그를 작성할 때 레벨을 설정해 원하는 로그 레벨들만을 출력할 수 있다. 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;application.yml&lt;/code&gt; 에 로깅 최소 레벨을 지정하면 해당 레벨 까지만 로깅이 되며, 그 이상은 로깅이되지 않는다. 예를들어 아래처럼 INFO 로 설정시 TRACE, DEBUG 레벨은 로깅 대상에서 제외된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;logging&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  level&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    root&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; info&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;간단한 로깅 설정이라면 위처럼 application.yml 에서 설정이 가능하다. 하지만 상세한 설정대로 동작하길 기대한다면 .xml 파일을 작성해야한다. xml 파일은 &lt;code class=&quot;language-text&quot;&gt;src/main/resources&lt;/code&gt; 에 생성하도록 하자.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;MDC 에 대한 내용을 함꼐 발행하고자 했으나, 따로 주제를 구분하여 포스트를 발행하는 것이 더 낫겠다는 생각이 들었다. 곧 MDC 에 대해서도 정리해보겠다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dkswnkk.tistory.com/445&quot;&gt;https://dkswnkk.tistory.com/445&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cjw-awdsd.tistory.com/56&quot;&gt;https://cjw-awdsd.tistory.com/56&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/do-not-use-system-out-println-for-logging/&quot;&gt;https://hudi.blog/do-not-use-system-out-println-for-logging/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[OAuth 2.0 의 등장배경과 동작 메커니즘]]></title><description><![CDATA[💡 현재 포스트는 harmony 팀 기술 블로그 에 게시된 글 입니다. OAuth…]]></description><link>https://haon.site/spring/oauth/</link><guid isPermaLink="false">https://haon.site/spring/oauth/</guid><pubDate>Tue, 06 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/oauth&quot;&gt;harmony 팀 기술 블로그&lt;/a&gt; 에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;oauth-와-소셜-로그인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#oauth-%EC%99%80-%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8&quot; aria-label=&quot;oauth 와 소셜 로그인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OAuth 와 소셜 로그인&lt;/h2&gt;
&lt;p&gt;웹 서핑을 하다보면 구글, 페이스북, 카카오, 트위터등의 외부 소셜 계정을 기반으로 간편한 회원가입 및 로그인할 수 있는 서비스를 찾아볼 수 있다. 클릭 한번으로 간편히 로그인할 수 있고, 연동되는 외부 소셜 서비스(카카오, 페이스북 등)에서 제공하는 기능을 간편하게 연동하고 사용할 수 있다는 장점이 있다. 예를들어, 카카오 게임즈내 게임 서비스를 사용하고 싶다면, 간편히 카카오 소셜 로그인하여 연동된 카카오 계정의 친구 목록, 프로필 정보를 가져와 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;이러한 소셜 로그인에 사용되는 프로토콜이 바로 OAuth 다. OAuth 는 아래와 같이 정의하고 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;OAuth 는 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹 사이트 상의 자신들의 정보에 대해 웹 사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로써 사용되는, 접근 위임을 위한 개방형 표준이다. - 위키백과 -&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8429ceb6ea765901e13cee6ebb7905e1/d43b4/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 130.67484662576686%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAaCAYAAAC3g3x9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB8UlEQVR42q1VXW+bQBAk+SX9wZXyENVRHyPlZySV+tSHKgas2CaPDgXVHxiwsWMbuMkNGOecgg+rOWm03HlvvDu3t2dAM4QQWCwWCMMQeZ7r3GGgxRiPxxiNRkjTtD0hI6kgZ0U0Fcq1Y586HAirCUdOMpH9k7bq0yTNEWE5z2D//Iph91ZqNka3a8JxnFbaNRDm+P1whadf3+V8gyAIsVqt2sjclDK1SxDFPNUISZI0pvxxrZZQiATb5Q84z88YDAZwXVernSbC8pvlsdlssFwui/qj3W63RxvpwwyyLNMR5oVjFEWYTCaYTqeYz+dFcZNkt9sVltryjxoJqwU68GSHwyF6vR5s24ZpmgdrWVax3u/3EcexPmWWSDCbyRMOMJNgytxI8HsmfyMYofZQVG3W6/UhdVqmzBTVwfS1GvIwPM+D7/uFZaQko46hJFflaUVYzdU7/F9l0+Sou8+tCT89wk8gFGfiXA1rt5/RbZA6sh5MCbtEasnrswe/q/UKIqkj3DdQsYbwv0C8GMjdS8AzEDwZ8B4N+BJ/LQPCJS5KK/3E6/2eMK2LUBbp6g5IriVuJDrIFh3sog7SuATX3vFNbvkDNaiTGooTmp7UkC7vr1d6QBGxClFa1YeRHb16TU8i7yibQtUYaNlhCLXZfsQbSXrq3My9sRIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/8429ceb6ea765901e13cee6ebb7905e1/a6d36/image.png&quot;
        srcset=&quot;/static/8429ceb6ea765901e13cee6ebb7905e1/222b7/image.png 163w,
/static/8429ceb6ea765901e13cee6ebb7905e1/ff46a/image.png 325w,
/static/8429ceb6ea765901e13cee6ebb7905e1/a6d36/image.png 650w,
/static/8429ceb6ea765901e13cee6ebb7905e1/e548f/image.png 975w,
/static/8429ceb6ea765901e13cee6ebb7905e1/d43b4/image.png 1202w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;우리 하모니 팀이 개발중인 moheng 서버스를 기반으로 비유해보겠다. 외부 애플리케이션(moheng) 은 사용자 인증을 위해 카카오등의 사용자 인증 방식을 사용한다. 이때, OAuth 를 바탕으로 제 3자 서비스(카카오) 의 특정 자원(친구 목록, 생년월일 등) 을 접근 및 사용할 수 있는 권한을 인가받게 된다.&lt;/p&gt;
&lt;h2 id=&quot;oauth-의-등장배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#oauth-%EC%9D%98-%EB%93%B1%EC%9E%A5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;oauth 의 등장배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OAuth 의 등장배경&lt;/h2&gt;
&lt;p&gt;그런데 소셜 로그인을 사용하면서도 항상 신기한점이 하나 있다. 일반 회원가입을 할 때와 달리, 소셜 로그인은 서비스 이용자로부터 아이디, 비밀번호를 요구하지 않는다. 단순히 버튼 클릭만으로 로그인에 성공하고, 회원 정보를 불러온다. 이런 방식에서 어떻게 쉽게 로그인하여 권한을 인증받고, 사용자 정보를 불러올 수 있을까? 이를 이해하려면 OAuth 의 등장배경을 이해해야한다. 또한 이 등장배경을 이해하면, 지금까지 우리는 어떻게 OAuth 프로토콜을 통해 안전한 인증 방법으로 서비스를 사용했는지 이해할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;oauth-10-의-등장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#oauth-10-%EC%9D%98-%EB%93%B1%EC%9E%A5&quot; aria-label=&quot;oauth 10 의 등장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OAuth 1.0 의 등장&lt;/h3&gt;
&lt;p&gt;앞서 서술했듯이, OAuth 등장 이전 소셜 로그인의 필요성은 자명했다. 구글, 카카오, 페이스북등 다양한 소셜 서비스에서 제공하는 서비스와 연동하여 사용할 수 있는 외부 서비스들이 등장하기 시작했고, 이 떄문에 소셜 제공처가 보유한 자원을 사용할 수 있는 인가(Authorziation) 를 받는 과정이 필요해졌다.&lt;/p&gt;
&lt;h3 id=&quot;제-3자에게-민감한-정보를-내주기란-쉽지-않다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%9C-3%EC%9E%90%EC%97%90%EA%B2%8C-%EB%AF%BC%EA%B0%90%ED%95%9C-%EC%A0%95%EB%B3%B4%EB%A5%BC-%EB%82%B4%EC%A3%BC%EA%B8%B0%EB%9E%80-%EC%89%BD%EC%A7%80-%EC%95%8A%EB%8B%A4&quot; aria-label=&quot;제 3자에게 민감한 정보를 내주기란 쉽지 않다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제 3자에게 민감한 정보를 내주기란 쉽지 않다&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/763a80e9758fea3c04d6b4f31366c9f0/cdf95/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.46625766871166%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABc0lEQVR42pWS20rDQBCG40P4bvXCV/BSwQfxWgSF0kfwzjsRvLHSILVpm6Zt2DSH3SSb4/7uodUerODAsGln+Oafn7FwNFpAlPKRKerjbRBbCViHdVPgjCDzX8GDd6RkgDRlyPNclsU694EmLFExKYAZJVtBggDefA5nPMZ0OoXneSCEbEHUEgXa/A2isCG4vVZYLUGJjZoTUJZhNnNlzpAkiW4IJFip4Zxr6FgOGI1G8n8Db5JnVO4N2mxogGrq5dU1oiiRa6Ua5rouVqsVyrLCw30PURijbRtdWywW+s0ypgH58AJ+7wTl8MEAHcfBWacDR041FhqPVHjeEt27LuzBh/49lxb4vq+VKj+1wpdzFLenaCePBqga+v2+VBgZW9p2B0opRdM0+juOY4RhqG1QwLquUdA5+OQJRfCpnbX2VW2CMaZTDVSQjaLvo5KD1QDKUtCsQEKZHmD9nMEuUKlQMPWqVTcb/N7/1x0enKXAf+ILgGD/bpfew2kAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/763a80e9758fea3c04d6b4f31366c9f0/a6d36/image-2.png&quot;
        srcset=&quot;/static/763a80e9758fea3c04d6b4f31366c9f0/222b7/image-2.png 163w,
/static/763a80e9758fea3c04d6b4f31366c9f0/ff46a/image-2.png 325w,
/static/763a80e9758fea3c04d6b4f31366c9f0/a6d36/image-2.png 650w,
/static/763a80e9758fea3c04d6b4f31366c9f0/e548f/image-2.png 975w,
/static/763a80e9758fea3c04d6b4f31366c9f0/3c492/image-2.png 1300w,
/static/763a80e9758fea3c04d6b4f31366c9f0/cdf95/image-2.png 2106w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이를 위해 가장 쉬운 방법은, 우리의 서비스에서 사용자로부터 소셜 계정(카카오 등) 아이디, 비밀번호를 입력받고, 카카오 서버에 이 카카오 계정 정보를 넘겨서 소셜 자원(카카오 친구 목록 등) 을 넘겨받는 것이다.&lt;/p&gt;
&lt;p&gt;하지만, 이런 방법은 과연 안전할까? 사용자들은 처음보는 우리 서비스를 믿고 카카오 아이디, 비밀번호를 넘겨주는 것이 바람직한가? 우리가 개발한 사이트가 보안에 취약하다면 어떻게 하는가? 또한 만든 서비스가 일반 개발자가 아닌, 해커가 만든 서비스일 수도 있다.&lt;/p&gt;
&lt;p&gt;이는 소셜 로그인 제공처(카카오, 구글 등) 에게도 큰 부담이다. 어떤 서비스인지 모르는 출처에게 사용자의 민감한 정보를 내주기란 큰 부담이 될 것이다. 즉, 소셜 제공처는 사용자의 민감한 정보를 제 3자인 우리 서비스에게 함부로 제공해주기란 난처하다.&lt;/p&gt;
&lt;p&gt;이 떄문에 OAuth 프로토콜이 등장했다. 더 정확히는 OAuth 1.0 이 표준화되기 이전의 각 회사에서 개발한 프로토콜이 등장했다. 구글에서는 AuthSub 라는 프로토콜을 개발하기도 했다. 하지만 이 방식은 표준화되지 않은 방식이므로, 소셜 제공처와 연동하는 제 3자 서비스들에게 불편함을 주었다. 구글과 연동하는 서비스를 위해선 AuthSub 프토로콜을, 카카오와 연동하는 서비스를 위해선 또 다른 프로토콜을 사용해야했다. 이는 유지보수 비용이 높아지는 문제를 야기했다.&lt;/p&gt;
&lt;p&gt;이를 위해 OAuth 1.0 프로토콜이 등장했다. OAuth 1.0 프로토콜은 각 소셜 제공처에서 각기 다르게 제공하는 프토토콜을 하나의 표준으로 통합한 표준 프토토콜이다. 1.0 이후 1.0a 버전이 등장했으나 모바일 앱에서 보안에 취약한 문제점이 있었다. 이를 또 보완하고, 기존 버전보다 더 단순화시켜 등장한 버전이 바로 현재 우리가 사용중인 OAuth 2.0 프로토콜이다.&lt;/p&gt;
&lt;h2 id=&quot;oauth-20-참여자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#oauth-20-%EC%B0%B8%EC%97%AC%EC%9E%90&quot; aria-label=&quot;oauth 20 참여자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OAuth 2.0 참여자&lt;/h2&gt;
&lt;p&gt;OAuth 프로토콜 사이에서 사용하는 협력하는 참여자들을 일반화시킨 용어가 존재한다. 예를들어 앞선 카카오, 페이스북 등의 소셜 제공처를 일반화된 용어로는 Authorization Server 라고 부르는데, 이에 대해 학습해보도록 한다.&lt;/p&gt;
&lt;h3 id=&quot;resource-owner&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#resource-owner&quot; aria-label=&quot;resource owner permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Resource Owner&lt;/h3&gt;
&lt;p&gt;말그대로 리소스(자원) 보유한 사용자다. 예를들어, 카카오톡을 사용한다면 카카오톡 대화 내용(리소스) 를 보유한 일반 사용자들이 이에 해당한다. 즉, 우리의 서비스와 연동할 외부 소셜 제공처(카카오) 등의 플랫폼에서 데이터(자원)를 보유하고 있는 소유자를 뜻한다.&lt;/p&gt;
&lt;h3 id=&quot;client&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#client&quot; aria-label=&quot;client permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Client&lt;/h3&gt;
&lt;p&gt;우리가 개발하고 있는 서비스를 뜻한다. 클라이언트라는 용어가 등장하니 Resource Owner 와 햇갈릴 수 있다. 다시 설명하자면, Resource Owner 는 우리가 개발한 서비스의 사용자이며, Client 는 우리가 개발한 서비스 자체를 뜻한다.&lt;/p&gt;
&lt;h3 id=&quot;resource-server-authorization-server&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#resource-server-authorization-server&quot; aria-label=&quot;resource server authorization server permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Resource Server (Authorization Server)&lt;/h3&gt;
&lt;p&gt;Resource Server 란 카카오, 구글 등 소셜 로그인 제공처가 해당된다. 즉, Resource Owner 가 사용하고 있는 해당 서비스의 서버를 뜻한다. Resource Server 는 Resource Owner 를 인증하고, 우리 서비스에게 엑세스 토큰을 발급해준다.&lt;/p&gt;
&lt;p&gt;Resource Server 는 Authorization Server 라고도 불린다. 사실 둘은 공식 정의상에 따르면 별개로 구분짓기 하나, 보통의 경우는 같은 맥락이자 동일한 객체로 취급해도 된다.&lt;/p&gt;
&lt;p&gt;엄밀한 정의로는 Authorization Server 는 Client(우리 서비스) 에게 엑세스 토큰을 발급해주는 서버이며, Resource Sever 는 카카오, 구글과 같이 리소스를 가지고 있는 서비스를 뜻한다.&lt;/p&gt;
&lt;h2 id=&quot;oauth-20-동작-플로우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#oauth-20-%EB%8F%99%EC%9E%91-%ED%94%8C%EB%A1%9C%EC%9A%B0&quot; aria-label=&quot;oauth 20 동작 플로우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OAuth 2.0 동작 플로우&lt;/h2&gt;
&lt;p&gt;서술한 객체들간에 어떻게 협력하여, OAuth 2.0 프로토콜 기능을 원활히 제공하는지 동작을 알아보자. 또한 우리 하모니 팀의 경우 카카오 로그인에 기반하여 OAuth 프로토콜을 사용했으며, Resource Server 를 카카오로 비유하면 이해하기 쉬울 것 같아 카카오를 기준으로 설명해보겠다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a6a8496301f935f36acc09b0799c2099/b5c8e/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.44171779141104%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABcElEQVR42pWS6W7CQAyE8/4Pxg8gCAQJlyhHICRc4b5hms9qKC2t1K5kre3dnR2P7QRBoGKxqHq9Ltd1FYahWN1uV6VSyfxer2d+pVIxw99sNvaGuFqtWm6/38vZbreK41hRFGk0GokPZrOZxePxWJw3m035vq/BYKDpdGp2Pp+1Wq2MAO/JXS4XOfpYPNztdlqv13YxSRKzVqtlYBgffl+Hw0HH4/ERPwAzsH6/b+XCEkBKY5/P51oul7rdbi+AGOt+v38F5CfY8RAQyl4sFkpSAzBMJeDRnxnCCg2Hw6E8z1O73Va5XJZbyJv4QZr/ieGvgLChEZPJxPSCKblGoyE/7Wj0H4Y05Xq9Pg7wg7SrXq2mQj6vXC5nhsboit4YVZ1Op0/A7EfazgiwMwaAwTLrdjYiSML4sDOfNJARegFEdLSjEZT21ukYOEwpCRaUz04OQ09YIhc4xM5zic/6cIhxmc8AIn6+gw97tKYC9HwHOttFPVorjKwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/a6a8496301f935f36acc09b0799c2099/a6d36/image-5.png&quot;
        srcset=&quot;/static/a6a8496301f935f36acc09b0799c2099/222b7/image-5.png 163w,
/static/a6a8496301f935f36acc09b0799c2099/ff46a/image-5.png 325w,
/static/a6a8496301f935f36acc09b0799c2099/a6d36/image-5.png 650w,
/static/a6a8496301f935f36acc09b0799c2099/e548f/image-5.png 975w,
/static/a6a8496301f935f36acc09b0799c2099/3c492/image-5.png 1300w,
/static/a6a8496301f935f36acc09b0799c2099/b5c8e/image-5.png 2194w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;13-로그인-시도-및-요청&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#13-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%8B%9C%EB%8F%84-%EB%B0%8F-%EC%9A%94%EC%B2%AD&quot; aria-label=&quot;13 로그인 시도 및 요청 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1~3. 로그인 시도 및 요청&lt;/h3&gt;
&lt;p&gt;우리 서비스 사용자인 Resource Owner 가 &quot;카카오 로그인하기&quot; 등의 버튼을 클릭하여 로그인을 시도한다. Client(우리 서비스) 는 해당 요청을 처리하기 위해, 사용자의 브라우저를 카카오 로그인 서버  페이지로 렌더링시킨다. 즉, 사용자 브라우저를 Authorization Server 로 보낸다.&lt;/p&gt;
&lt;p&gt;당연하지만, Authorization Server 의 URI 는 각 Resource Server 마다 정해져있다. 이 URI 를 Authorization Server 의 URI 라고 하여, &lt;code class=&quot;language-text&quot;&gt;Authorization URI&lt;/code&gt; 라고 부른다. 카카오의 경우는 &lt;code class=&quot;language-text&quot;&gt;kauth.kakao.com&lt;/code&gt; 이다. 이 URI 로 Resource Owner 가 이동하도록 유도해야하며, 이 URI 에는 다음과 같은 추가 정보를 쿼리 스트링으로 함께 포함시켜서 이동해야한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;client_id&lt;/code&gt; : 애플리케이션을 생성했을 때 발급받은 Client ID. 카카오의 경우 Kakao Devlopers 에서 생성 및 발급 가능하다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;redirect_uri&lt;/code&gt; : 카카오 로그인 성공 후 사용자를 리다이렉션 시킬 URI. Kakao Developers 에 &lt;code class=&quot;language-text&quot;&gt;Redirect URI&lt;/code&gt; 사전 등록 해줘야 한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;response_type&lt;/code&gt; : 항상 &lt;code class=&quot;language-text&quot;&gt;code&lt;/code&gt; 라는 값으로 고정시킨다. 이는 카카오 로그인 시도 후 응답받는 데이터 타입이 &lt;code class=&quot;language-text&quot;&gt;인가 코드(Authorization Code)&lt;/code&gt; 임을 표현하는 것이다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;scope&lt;/code&gt; : Client(우리 서비스)가 부여받은 리소스 접근 권한으로, 카카오 서버내에 등록된 사용자의 추가 정보(생년월일, 친구목록 등) 이 리소스에 해당한다. 참고로 수집 가능한 리소스는 사용자(Resource Owner) 가 카카오 로그인시 정보 수집에 동의한 항목만 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;아래는 Authorization URI 예시이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;https&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;kauth&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;kakao&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;auth&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;response_type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;code
&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;client_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;29352735982374239857&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;redirect_uri&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;https&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;example&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;callback
&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;생략&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;4-카카오-id-pw-입력&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-%EC%B9%B4%EC%B9%B4%EC%98%A4-id-pw-%EC%9E%85%EB%A0%A5&quot; aria-label=&quot;4 카카오 id pw 입력 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 카카오 ID, PW 입력&lt;/h3&gt;
&lt;p&gt;앞선 과정을 통해 Client(우리 서비스) 가 사용자를 Authorization URI (+ 추가 쿼리 스트링) 로 이동시켰다. 이 단계에선 Resource Owner(사용자) 에게 카카오 로그인 화면이 보일것이니, 사용자는 카카오 계정 아이디, 비밀번호를 입력하여 로그인을 시도한다.&lt;/p&gt;
&lt;h3 id=&quot;56-인가-코드-발급-및-우리-서비스-내-redirect-uri-로-리다이렉트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#56-%EC%9D%B8%EA%B0%80-%EC%BD%94%EB%93%9C-%EB%B0%9C%EA%B8%89-%EB%B0%8F-%EC%9A%B0%EB%A6%AC-%EC%84%9C%EB%B9%84%EC%8A%A4-%EB%82%B4-redirect-uri-%EB%A1%9C-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%ED%8A%B8&quot; aria-label=&quot;56 인가 코드 발급 및 우리 서비스 내 redirect uri 로 리다이렉트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5~6. 인가 코드 발급 및 우리 서비스 내 Redirect URI 로 리다이렉트&lt;/h3&gt;
&lt;p&gt;카카오 계정 ID, PW 를 입력하여 로그인에 성공했다면, Authorization Server 는 우리가 등록해둔 Redirect URI 로 사용자를 리다이렉션 시킨다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 Redirect URI 를 Authorization Server 가 알고 있어야지 해당 URI 로 사용자를 리다이렉션 시켜주므로, 이 URI 또한 Kakao Developers 에 미리 등록되어 있어야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이때, Redirect URI 에 그냥 리다이렉션 시키는 것이 아니라, Redrirect URI 에 쿼리 스트링으로 &lt;code class=&quot;language-text&quot;&gt;인가 코드(Authorization Code)&lt;/code&gt; 를 포함하여 사용자를 리다이렉션 시킨다. 인가 코드란 Client(우리 서비스) 가 Authorization Server 로 부터 AccessToken 을 획득하기 위해 사용하는 임시 코드다. 수명이 보통 1~10분 정도로 매우 짧다는 것이 특징이다.&lt;/p&gt;
&lt;h3 id=&quot;78-발급받은-인가-코드와-accesstoken-을-교환&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#78-%EB%B0%9C%EA%B8%89%EB%B0%9B%EC%9D%80-%EC%9D%B8%EA%B0%80-%EC%BD%94%EB%93%9C%EC%99%80-accesstoken-%EC%9D%84-%EA%B5%90%ED%99%98&quot; aria-label=&quot;78 발급받은 인가 코드와 accesstoken 을 교환 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7~8. 발급받은 인가 코드와 AccessToken 을 교환&lt;/h3&gt;
&lt;p&gt;Client(우리 서비스) 는 Authorization Server 에게 인가 코드를 전송하고, 응답으로 AccessToken 를 전달받는다. Client 는 이 발급받은 AccessToken 으로 Resource Server 내의 사용자 정보를 조회할 권한을 얻는다. 즉, 발급받은 AccessToken 을 카카오 Resource Server 에 전송하여 카카오 사용자 정보(이메일, 생년월일, 친구 목록 등) 를 가져올 수 있게된다.&lt;/p&gt;
&lt;h3 id=&quot;910-accesstoken-과-사용자-정보를-교환&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#910-accesstoken-%EA%B3%BC-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%A0%95%EB%B3%B4%EB%A5%BC-%EA%B5%90%ED%99%98&quot; aria-label=&quot;910 accesstoken 과 사용자 정보를 교환 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;9~10. AccessToken 과 사용자 정보를 교환&lt;/h3&gt;
&lt;p&gt;Client 는 발급받은 AccessToken 을 Resource Owner 에 요청하여, 사용자 정보를 조회할 수 있다. 이렇게 조회한 사용자 정보를 우리 서비스내 데이터베이스에 저장하면 될 것이다. 또한 데이터베이스에 저장했다면, 우리 서비스내 JWT 토큰(엑세스, 리프레시) 토큰 또한 자체적으로 발급 및 저장하여 인증/인가 시스템을 구축하면 된다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 엑세스 토큰, 리프레시 토큰에 대한 이론은 &lt;a href=&quot;https://haon.blog/spring/access-refresh-token/&quot;&gt;RefeshToken 은 AccessToken 의 문제점을 해결하기위해 등장했다&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;11-로그인-성공-및-메인-페이지로-이동&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#11-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%84%B1%EA%B3%B5-%EB%B0%8F-%EB%A9%94%EC%9D%B8-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%A1%9C-%EC%9D%B4%EB%8F%99&quot; aria-label=&quot;11 로그인 성공 및 메인 페이지로 이동 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;11. 로그인 성공 및 메인 페이지로 이동&lt;/h3&gt;
&lt;p&gt;모든 과정을 성공적으로 마쳤다면 사용자의 브라우저를 우리 서비스내 메인 페이지로 이동시켜주자. 이렇게 OAuth 2.0 프로토콜 기반 플로우는 끝이난다.&lt;/p&gt;
&lt;h2 id=&quot;authorization-code-는-왜-필요할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#authorization-code-%EB%8A%94-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;authorization code 는 왜 필요할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Authorization Code 는 왜 필요할까?&lt;/h2&gt;
&lt;p&gt;앞선 플로우에서 봤듯이, Rediect URI 의 쿼리 스트링으로 인가 코드를 전달받고, 이 인가 코드로 엑세스 토큰을 발급받아야 한다. 인가 코드는 왜 필요할까? 인가 코드 발급 과정을 생략하고, 곧 바로 엑세스 토큰을 발급받아서 카카오 회원 정보를 불러오면 안될까?&lt;/p&gt;
&lt;p&gt;이 과정이 생략되면, Authorization Server 가 엑세스 토큰을 Client(우리 서비스) 에게 전달하기 위해서 URI 의 쿼리 스트링에 엑세스 토큰 값을 실어서 전달해야 한다. &lt;strong&gt;브라우저의 URI 에 중요한 정보인 엑세스 토큰이 노출되는 것은 보안상 매우 위험하다.&lt;/strong&gt; (이때, Redirect URI 를 통해 데이터를 전달할 방법은 아쉽게도 URI 자체에 쿼리 스트링으로 담아서 전송하는 방법밖에 존재하지 않는다.) 이런 보안 위험 떄문에 인가 코드를 사용한다.&lt;/p&gt;
&lt;h2 id=&quot;authorization-uri-는-누가-생성하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#authorization-uri-%EB%8A%94-%EB%88%84%EA%B0%80-%EC%83%9D%EC%84%B1%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;authorization uri 는 누가 생성하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Authorization URI 는 누가 생성하는가?&lt;/h2&gt;
&lt;p&gt;추가적으로 프론트엔드와 백엔드의 역할을 정리해보겠다. 우선 앞선 1~3 과정에 학습했듯이, Authorization URI 에는 추가 정보를 실어서 새로운 Authorizaion URI 를 생성해야한다. 이 URI 는 어디서 생성해도 괜찮다. 우리 하모니 팀의 경우, 한쪽에서 처리하는게 편리하다고 판단 했기 떄문에 백엔드에서 처리하도록 구현했다.&lt;/p&gt;
&lt;h2 id=&quot;redirect-uri-는-어디로-설정하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redirect-uri-%EB%8A%94-%EC%96%B4%EB%94%94%EB%A1%9C-%EC%84%A4%EC%A0%95%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;redirect uri 는 어디로 설정하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redirect URI 는 어디로 설정하는가?&lt;/h2&gt;
&lt;p&gt;다음은 Redirect URI 이다. 사용자는 로그인을 마치면 Redirect URI 로 리다이렉션 될텐데, 이 리다이렉션 될 URI 는 백엔드가 아닌 프론트엔드로 설정한다. 이후 프론트엔드는 백엔드에게 인가 코드를 전송한다. 백엔드는 전달받은 인가 코드로 엑세스 토큰을 발급받고, 이어서 Resource Server 로 부터 소셜 계정 정보(이메일, 생년월일, 친구목록 등) 을 제공받는다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=BotXDfBPvDA&quot;&gt;https://www.youtube.com/watch?v=BotXDfBPvDA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-07-10-understanding-oauth/&quot;&gt;https://tecoble.techcourse.co.kr/post/2021-07-10-understanding-oauth/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://haesummy.tistory.com/entry/OAuth-20%EC%9D%B4%EB%9E%80-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D%EC%9D%80&quot;&gt;https://haesummy.tistory.com/entry/OAuth-20%EC%9D%B4%EB%9E%80-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D%EC%9D%80&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[RefeshToken 은 AccessToken 의 문제점을 해결하기위해 등장했다]]></title><description><![CDATA[AccessToken 의 한계 Access Token 만을 활용한 인증(Authentication) 방식으로는 제 3자에게 토큰이 탈취 당할경우 보안에 정말 취약해진다는 점이다. AccessToken…]]></description><link>https://haon.site/spring/access-refresh-token/</link><guid isPermaLink="false">https://haon.site/spring/access-refresh-token/</guid><pubDate>Mon, 05 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;accesstoken-의-한계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#accesstoken-%EC%9D%98-%ED%95%9C%EA%B3%84&quot; aria-label=&quot;accesstoken 의 한계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AccessToken 의 한계&lt;/h2&gt;
&lt;p&gt;Access Token 만을 활용한 인증(Authentication) 방식으로는 제 3자에게 토큰이 탈취 당할경우 보안에 정말 취약해진다는 점이다. AccessToken 이 발급된 이후 해당 토큰이 과연 탈취를 안당할 가능성이 없을까? 우리는 토큰 기반 인증방식을 구현할 때 탈취의 가능성과 그로 인한 보안 위험을 염두해두고 개발해야한다.&lt;/p&gt;
&lt;p&gt;AccessToken 하나만을 사용하여 인증/인가를 구축한다면, 우선 앞서 설명했듯이 탈취 당했을 경우의 위험성이 매우 크다. 토큰을 탈취한 해커는 해당 토큰으로 얼마든지 서비스에 접속하여 유저 정보를 탈취할 수 있기 떄문이다.&lt;/p&gt;
&lt;p&gt;JWT 토큰의 Payload 를 조정하여 엑세스 토큰의 유효기간을 짧게 설정할 수 있다. 즉, 유효기간을 짧게 설정하여 만료기간이 지났을 경우 해커가 해당 토큰을 탈취하더라도 사용 불가능하게 만드는 것이다. 하지만 이 또한 문제점이 있다. 만약 엑세스 토큰의 유효기간이 30분 정도로 짧게 설정했다면? 사용자는 매번 30분마다 자동 로그아웃이 되고, 30분 주기로 서비스를 이용하고자 매번 재로그인을 시도해야한다. 이는 보안 문제를 해결할 수 있지만, 사용자에게 매우 큰 불편함을 야기할 수 있다.&lt;/p&gt;
&lt;p&gt;정리하자면, AccessToken 만으로 인증 시스템을 구현한다면 보안 상의 문제가 발생하거나 또는 자주 로그아웃되는 사용자 불편함 둘 중 하나를 감수해야한다.&lt;/p&gt;
&lt;h2 id=&quot;refreshtoken-의-등장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#refreshtoken-%EC%9D%98-%EB%93%B1%EC%9E%A5&quot; aria-label=&quot;refreshtoken 의 등장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RefreshToken 의 등장&lt;/h2&gt;
&lt;p&gt;하나의 토큰(엑세스 토큰) 만으로는 인증 시스템을 구축하기 힘들기 떄문에, 이를 보안하고자 리프레시 토큰이 등장했다. 리프레시 토큰은 엑세스 토큰의 유효 기간을 짧게 두고, 자주 재발급 하도록 만들어서 보안을 강화면서도 사용자에게 자주 로그아웃되는 경험을 주지 않도록 하기 위함에 있다.&lt;/p&gt;
&lt;h3 id=&quot;어떤-방식으로-동작하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%96%A4-%EB%B0%A9%EC%8B%9D%EC%9C%BC%EB%A1%9C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;어떤 방식으로 동작하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어떤 방식으로 동작하는가?&lt;/h3&gt;
&lt;p&gt;뒤이어서 더 자세히 후술하겠지만, 리프레시 토큰을 통해 새로운 엑세스 토큰을 발급받는 방식으로 동작한다. 즉, 유효기간이 매우 긴 리프레시 토큰을 통해 짧은 유효기간 주기를 가진 새로운 액세스 토큰을 갱신함으로써, 액세스 토큰이 탈취 당했을 경우의 보안 위험성을 낮춘 방식이다. 보통 액세스 토큰의 유효기간은 30분 정도로 설정하는데, 해커가 이 짧은 시간동안 액세스 토큰을 탈취해서 서비스 내 중요한 자원을 탈취하는 기간을 최소화하는 것이다.&lt;/p&gt;
&lt;p&gt;다시 정리하자면, 엑세스 토큰은 클라이언트와 서버 송.수신에 직접 관연하는 토큰이고, 리프레시 토큰은 새로운 액세스 토큰 재발급에 관연하는 토큰이다.&lt;/p&gt;
&lt;h3 id=&quot;적절한-토큰-유효기간&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%81%EC%A0%88%ED%95%9C-%ED%86%A0%ED%81%B0-%EC%9C%A0%ED%9A%A8%EA%B8%B0%EA%B0%84&quot; aria-label=&quot;적절한 토큰 유효기간 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;적절한 토큰 유효기간&lt;/h3&gt;
&lt;p&gt;보통의 경우 리프레시 토큰의 유효기간을 약 2주, 엑세스 토큰은 30분 정도로 설정한다. 우리 하모니 팀 프로젝트의 경우도 이와 같은 방식으로 유효기간을 설정했다. 당연하게도 유효기간은 각 팀의 서비스마다 적절한 유효기간을 논의하여 설정하는 것이 바람직하다.&lt;/p&gt;
&lt;h2 id=&quot;accesstoken-과-refreshtoken-의-인증인가-요청-플로우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#accesstoken-%EA%B3%BC-refreshtoken-%EC%9D%98-%EC%9D%B8%EC%A6%9D%EC%9D%B8%EA%B0%80-%EC%9A%94%EC%B2%AD-%ED%94%8C%EB%A1%9C%EC%9A%B0&quot; aria-label=&quot;accesstoken 과 refreshtoken 의 인증인가 요청 플로우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AccessToken 과 RefreshToken 의 인증/인가 요청 플로우&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e3f61e5604cc80342ca3880f3af3af63/c0566/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.668711656441715%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACg0lEQVR42m2T2U8TURTG+bd5MFg1Jj4QQVEffNAYH0wMigrUosDQVlpK6b6gXaZT6EYXutC9Mz/PnbIkxpucnMnNmW++5czCzJpxU+pYlsVkMmEyHjObze9UH0+mds1M83ZG3eu5HKWzM27OQqi0z/H5TxJVH9PphFarRTqdIplKkS8U5MURB4E0T99ss/LWiT+SoWgUCAaDpJJJHjscrC4vMxYCNuBhYRN3foNweZ92p00ykaTZbDLqdhk0GjDs4/REubfykftrn3BqQeLxGJV6navRiL3dXYKBwB1Dn7GNR/9KpHIgQA0ikQiRaBTj9ylnyRR1Q2dLC+N4vs7DF59xusPU8jkuZK6ZTlMvFukPBv8CfiFU1hgM+ugFA10k6YZBRvxpt1tsH4RZerbOAwF0uUP0LpuMOx2mvR4tYaoU3QIeFV34ik6iJY12o04sFCIdjxMVj+KpNKPxEJcnxqNXGzx5vcnWXoCzYoHqxQXVWo2aVF2eJam7UOISSLyoMRNmloRhhsN2n6lBYejcO2H1nYu19z/QfInbbfjfWTi8lnxS0bB6XRAwslmoVDD1Aog817XkJZHs3T+GQp6ZSLVkxiqXQXykXLJZ2h56JRQb8LKFRCiAGcj8wcxIx+S7N4ZDefhyA694SLeDOZ2CWhVVkrZdiqHfcPJL/0ao6sG6qMHREfh90v2YOy44TeHaDeIQdioUr18kW+Yc8KZkyZGFtwGVXC23TqC8i9msg8cNug7n55hqD80pmwK4uPyBRdlFbUc+FglhSmgk4nNFPrkT/23AcjdPqZOl1j/HuurNvRB/ELaWWgdhYFQaBGJZqRzlalP9i1hq9xQzxXA4nD8DfwGjhmCeIuK1oQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e3f61e5604cc80342ca3880f3af3af63/a6d36/image.png&quot;
        srcset=&quot;/static/e3f61e5604cc80342ca3880f3af3af63/222b7/image.png 163w,
/static/e3f61e5604cc80342ca3880f3af3af63/ff46a/image.png 325w,
/static/e3f61e5604cc80342ca3880f3af3af63/a6d36/image.png 650w,
/static/e3f61e5604cc80342ca3880f3af3af63/e548f/image.png 975w,
/static/e3f61e5604cc80342ca3880f3af3af63/3c492/image.png 1300w,
/static/e3f61e5604cc80342ca3880f3af3af63/c0566/image.png 1544w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;토큰을 발급받고 만료하는 모든 동작 플로우를 나타냈다. 참고로 아래에 필자가 서술한 번호는 위 그림에 표현된 번호가 다르다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; : 사용자가 로그인을 한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; : 서버는 데이터베이스에 저장된 회원 정보와 일치하는지 확인한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; : 회원 정보가 일치한다면(로그인에 성공했다면) 엑세스 토큰과 리프레시 토큰을 모두 클라이언트에게 발급한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; : 이때, 서버는 리프레시 토큰을 데이터베이스에 저장한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; : 앞으로 클라이언트는 서버에게 자원을 요청할 떄 마다 엑세스 토큰을 실어서 요청한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(6)&lt;/code&gt; : 서버는 매번 요청에 유입된 엑세스 토큰이 유효성(변조 여부, 만료 여부 등) 을 체크한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(7)&lt;/code&gt; : 서버는 요청받은 엑세스 토큰이 만료되었다면, 클라이언트에게 엑세스 토큰이 만료되었다는 신호를 응답한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(8)&lt;/code&gt; : 클라이언트는 앞서 &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 에서 발급받은 리프레시 토큰을 전송하여 새로운 엑세스 토큰 재발급(갱신) 을 시도한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(9)&lt;/code&gt; : 서버는 클라이언트로부터 전달받은 리프레시 토큰과, 앞서 &lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 과정에서 데이터베이스에 저장해놓은 리프레시 토큰을 서로 비교하여 토큰 변조 여부를 체크한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(10)&lt;/code&gt; : &lt;code class=&quot;language-text&quot;&gt;(9)&lt;/code&gt; 번과 동시간대 발생하는 일인데, 당연하지만 리프레시 토큰의 만료여부도 체크한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(11)&lt;/code&gt; : &lt;code class=&quot;language-text&quot;&gt;(9)&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;(10)&lt;/code&gt; 과정을 통해 전달받은 리프레시 토큰이 유효하다고 판단되면, 새로운 엑세스 토큰을 클라이언트에게 발급해준다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;(12)&lt;/code&gt; : 반대로 리프레시 토큰이 유효하지 않다고 판단되면, 즉 리프레시 토큰도 만료기간이 지났다면 클라이언트에게 다시 로그인을 해야한다고 신호를 응답한다. (즉, 로그아웃된 상태임을 알린다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;그럼에도-refreshtoken-또한-한계가-존재한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%9F%BC%EC%97%90%EB%8F%84-refreshtoken-%EB%98%90%ED%95%9C-%ED%95%9C%EA%B3%84%EA%B0%80-%EC%A1%B4%EC%9E%AC%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;그럼에도 refreshtoken 또한 한계가 존재한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그럼에도 RefreshToken 또한 한계가 존재한다&lt;/h2&gt;
&lt;h3 id=&quot;accesstoken-의-유효기간이-짧다고-해서-탈취-가능성은-여전하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#accesstoken-%EC%9D%98-%EC%9C%A0%ED%9A%A8%EA%B8%B0%EA%B0%84%EC%9D%B4-%EC%A7%A7%EB%8B%A4%EA%B3%A0-%ED%95%B4%EC%84%9C-%ED%83%88%EC%B7%A8-%EA%B0%80%EB%8A%A5%EC%84%B1%EC%9D%80-%EC%97%AC%EC%A0%84%ED%95%98%EB%8B%A4&quot; aria-label=&quot;accesstoken 의 유효기간이 짧다고 해서 탈취 가능성은 여전하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AccessToken 의 유효기간이 짧다고 해서 탈취 가능성은 여전하다&lt;/h3&gt;
&lt;p&gt;RefreshToken 을 도입하여 AccessToken 의 유효기간을 짧게 설정했다고 한들, &quot;탈취 여부&quot; 그 자체로는 문제점이 여전하다. 즉, 유효기간이 짧다고 해서 엑세스 토큰이 탈취가 안되는 것이 아니다. &lt;strong&gt;탈취된 엑세스 토큰은 짧은 그 유효기간 동안에 탈취 당하여 악용될 수 있다는 문제점은 여전하다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;refreshtoken-을-탈취-당할-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#refreshtoken-%EC%9D%84-%ED%83%88%EC%B7%A8-%EB%8B%B9%ED%95%A0-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;refreshtoken 을 탈취 당할 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RefreshToken 을 탈취 당할 경우&lt;/h3&gt;
&lt;p&gt;리프레시 토큰 또한 엑세스 토큰에 비해 서버-클라이언트 송.수신에 덜 오고 갈 뿐이지, 이 또한 탈취 당할 가능성은 존재한다. 이 리프레시 토큰을 탈취 당하면 해커는 마음껏 엑세스 토큰을 재발급하여 장기간동안 해킹할 수 있다. 서버 데이터베이스 내에 리프레시 토큰을 저장하여 직접 추적하여 피해를 최소화할 수 있겠지만, 그 피해가 발생하기 전까진 탈취 여부를 알 수 없다.&lt;/p&gt;
&lt;h2 id=&quot;rtr-refresh-token-rotation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rtr-refresh-token-rotation&quot; aria-label=&quot;rtr refresh token rotation permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RTR (Refresh Token Rotation)&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7e28a09b823225a21f828c23d567a150/dd507/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.19631901840491%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABHUlEQVR42q1Q2XKDMAzk/z+wnZJMQnMAIcb4wCc2W9md9CmP3RmP5LW1K6lJKYELhYlxzFzA+wCl15oLqSEp3/edcgW+yBrX1cIlA+EZdBCQgUNHiYJGrwb9MKEfJ0i1IoSA9tjh/H3DODEwEtnI9LM9obvcMYxPLIsGswM6+YWLOuJJuU/2V7A4OufwQogR4+OJbUskHhHJ0FqHoXIbPBlG4oWZYLyhu0POGWnLNTaMRlto5D3vKOOX4vZwgtYaznuYtoVaBA7njkbVsI64hWO8foAJgXnmtCJNbysiNdPgDcrOPIkVwcAYsjHI9NmQQOFy6ZLNlbNSwpLgC00pxnvVGiLnsKcz3O0OT6fyxbDv4a7Xym1K/dU0+Gf8AIAiHoXRwCQZAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7e28a09b823225a21f828c23d567a150/a6d36/image-1.png&quot;
        srcset=&quot;/static/7e28a09b823225a21f828c23d567a150/222b7/image-1.png 163w,
/static/7e28a09b823225a21f828c23d567a150/ff46a/image-1.png 325w,
/static/7e28a09b823225a21f828c23d567a150/a6d36/image-1.png 650w,
/static/7e28a09b823225a21f828c23d567a150/e548f/image-1.png 975w,
/static/7e28a09b823225a21f828c23d567a150/3c492/image-1.png 1300w,
/static/7e28a09b823225a21f828c23d567a150/dd507/image-1.png 1528w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;리프레시 토큰을 단 1번만 사용할 수 있게(One Time Use Only) 만드는 방법이 있다. 이 방법을 &lt;code class=&quot;language-text&quot;&gt;RTR(Refresh Token Rotation)&lt;/code&gt; 이라고 한다. 한 번 사용된 리프레시 토큰은 탈취된 것이라 가정하고, &lt;strong&gt;리프레시 토큰을 사용하여 새로운 엑세스 토큰을 발급받을 때 해당 기존 리프레시 토큰을 폐기하고, 새로운 리프레시 토큰도 발급받는 방식이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이 방식으로 이미 사용된 리프레시 토큰을 사용하게 돠면 서비스측에서 탈취를 확인하게 되어 조치할 수 있다. 하자미나, 사용되지 않은 리프레시 토큰을 훔쳐 사용하거나, 그냥 지속적으로 엑세스 토큰만을 사용한다면 이 또한 막을 수 없다.&lt;/p&gt;
&lt;h2 id=&quot;accesstoken-을-db-에-저장한다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#accesstoken-%EC%9D%84-db-%EC%97%90-%EC%A0%80%EC%9E%A5%ED%95%9C%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;accesstoken 을 db 에 저장한다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AccessToken 을 DB 에 저장한다면?&lt;/h2&gt;
&lt;p&gt;앞서 리프레시 토큰을 서버 데이터베이스 또는 인메모리에 보관하고, 유입된 토큰과 비교하여 유효성을 검증한다고 했다. 이와 비슷한 원리로, 리프레시 토큰을 사용 안하고 서버에 직접 엑세스 토큰을 저장하여 유효성을 검증하면 안될까?&lt;/p&gt;
&lt;p&gt;하지만 이는 토큰 기반 인증의 장점을 잃어버리는 것이다. 토큰 기반 인증 시스템의 등장배경 중 하나는, 세션의 한계를 보완하고자 등장했다. 세션의 가장 큰 문제점은 서버가 여러개인 다중화 환경일 경우 각 서버마다 다른 세션 값을 보유하기 떄문에, 데이터 보유 일관성 불일치 문제가 발생한다는 점이다.&lt;/p&gt;
&lt;h2 id=&quot;더-학습해볼-주제-및-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%EC%A3%BC%EC%A0%9C-%EB%B0%8F-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 주제 및 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 주제 및 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;세션과 쿠키&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://skatpdnjs.tistory.com/60&quot;&gt;https://skatpdnjs.tistory.com/60&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-Access-Token-Refresh-Token-%EC%9B%90%EB%A6%AC-feat-JWT&quot;&gt;https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-Access-Token-Refresh-Token-%EC%9B%90%EB%A6%AC-feat-JWT&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[AWS EC2, Nginx 를 활용하여 스프링 애플리케이션 배포하기]]></title><description><![CDATA[시작에 앞서 localhost 로 돌리고 있던 본인의 스프링부트 프로젝트를 외부에 배포하고 싶을 때가 있습니다. 사이드 프로젝트를 진행하다보면 백엔드에서 개발한 API…]]></description><link>https://haon.site/haon/infra/deploy/ec2+nginx/</link><guid isPermaLink="false">https://haon.site/haon/infra/deploy/ec2+nginx/</guid><pubDate>Mon, 05 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;시작에-앞서&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%9E%91%EC%97%90-%EC%95%9E%EC%84%9C&quot; aria-label=&quot;시작에 앞서 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시작에 앞서&lt;/h2&gt;
&lt;p&gt;localhost 로 돌리고 있던 본인의 스프링부트 프로젝트를 외부에 배포하고 싶을 때가 있습니다. 사이드 프로젝트를 진행하다보면 백엔드에서 개발한 API 서버를 클라이언트에게 넘겨줘야 할때 어떻게 배포를 시켜야할지 정말 막막할 수 있는데, 이번 포스팅에서는 AWS EC2, Nginx 를 활용해서 어떻게 무중단 배포가 이루어지는지 알아보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;무중단-배포란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC%EB%9E%80&quot; aria-label=&quot;무중단 배포란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;무중단 배포란?&lt;/h2&gt;
&lt;p&gt;간단히 말해서비스를 중지하지 않고, 배포를 계속하는 것을 무중단 배포라고 합니다. 이때 매번 배포를 할때마다 본인의 컴퓨터 터미널을 종료시키면 서버가 꺼져버릴 수 있는데, 서비스가 죽지 않고 배포가 가능하도록 무중단 배포를 구축해보는 법을 알아봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;인스턴스-생성--elastic-ip-할당&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%83%9D%EC%84%B1--elastic-ip-%ED%95%A0%EB%8B%B9&quot; aria-label=&quot;인스턴스 생성  elastic ip 할당 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인스턴스 생성 + Elastic IP 할당&lt;/h2&gt;
&lt;h4&gt;EC2 인스턴스 생성&lt;/h4&gt;
&lt;p&gt;인스턴스를 생성한 결과는 아래와 같습니다. 또한 저는 Ubuntu 18.04 환경을 기반으로 인스턴스를 구축했음을 미리 알려드립니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1a57ff37-6fa5-4dcf-84bc-650d1144215d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;또한 보안그룹 설정은 아래와 같이 HTTP, SSH, HTTPS 를 생성했습니다. 이때 주의해야 할점은 SSH 접속방식은 오로지 본인만 접속 가능하도록 &quot;내 IP&quot; 로만 설정해줘야합니다. 그렇지 않으면 해킹에 당할 위험이 있으니, 꼭 유의하시길 바랍니다!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/b95a2dc2-83aa-478d-a3ec-094864403ea0/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Elastic IP 생성 및 인스턴스에 할당하기&lt;/h4&gt;
&lt;p&gt;인스턴스를 생성했다면 Elastic IP 를 할당해줘야 하는데, 아래와 같이 탄력적 IP 란에 들어가서 탄력적 IP 주소 할당을 클릭하여 하나 생성을 해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/9e3293c8-d008-477c-899e-d83488ac41e8/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그리고 생성한 탄력적 IP를 앞서 생성한 EC2 인스턴스에 연결해주면 됩니다. 탄력적 IP 주소 연결란에 들어가서 해당 탄력적 IP를 연결해줍시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a9e28829-f7ef-4ef5-9e52-0f99d309aa26/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ssh를-통해-ec2에-접속&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ssh%EB%A5%BC-%ED%86%B5%ED%95%B4-ec2%EC%97%90-%EC%A0%91%EC%86%8D&quot; aria-label=&quot;ssh를 통해 ec2에 접속 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SSH를 통해 EC2에 접속&lt;/h2&gt;
&lt;p&gt;다음으로 SSH 접속을 통해 직전에 생성했던 EC2 인스턴스 서버에 접속할 수 있습니다. 터미널에서 인스턴스 생성시 만들었던 키페어를 넣어주고, 아래와 같은 형태로 접속하시면 EC2 서버에 접속이 성공할겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;형태 : ssh -i &quot;키페어 값&quot; ubuntu@퍼블릭 IPv4 DNS&quot;

=&gt; 형태 예시 : ssh -i my-keypair ubuntu@ec2-5-256-91-790.compute-3.amazonaws.com&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;성공한다면 아래와 같이 접속되는 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1a7cecb4-33ce-4fcb-a971-c715332be6a2/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx&quot; aria-label=&quot;nginx permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx&lt;/h2&gt;
&lt;p&gt;이제 Nginx 를 가상서버에 설치할 차례입니다. 아래와 같이 Nginx 를 설치해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo apt install nginx     // nginx 설치
sudo serivce nginx start   // 설치한 Nginx 가 정상적으로 반영되도록 재시작
ps -ef | grep nginx        // 정상적으로 Nginx가 실행되었는지 확인&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c44989a4-c712-4b19-91fc-82f1b3f4390d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;springboot-프로젝트-서버에-git-clone-받기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#springboot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%84%9C%EB%B2%84%EC%97%90-git-clone-%EB%B0%9B%EA%B8%B0&quot; aria-label=&quot;springboot 프로젝트 서버에 git clone 받기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SpringBoot 프로젝트 서버에 git clone 받기&lt;/h2&gt;
&lt;h4&gt;html 파일 삭제&lt;/h4&gt;
&lt;p&gt;앞서 Nginx 가 정상적으로 설치 및 실행되고 있다면 /var/www/html 파일이 우분투 서버에 존재하게 됩니다. 이 html 파일을 삭제해줍시다. 만일 해당 디렉토리 및 파일이 존재하지 않는다는 결과를 받게된다면, Nginx 정상 설치 여부를 다시 확인해보시길 바랍니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;cd /var/www    // var/www 디렉토리로 이동
ls             // 해당 디렉토리에 html 파일이 존재하는지 여부 확인
rm -rf html    // 존재한다면 해당 파일을 삭제하기
ls             // html 파일이 삭제되었는지 확인&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e6dadce8-f0e4-4ae2-a389-22e7303fc563/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4&gt;SpringBoot 프로젝트 git clone 받기&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;이제 SpringBoot 프로젝트를 진행한 내용을 EC2 서버에 내려받고, build 를 실행하고 서버를 키는 과정을 진행해야합니다. 배포 시스템 이런거 모두 생략하고, 여러 방법중 git clone 을 받는 방법을 알아보겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c34ac9e4-cd96-4e13-b24b-49945a8c7b06/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위처럼 본인의 레포지토리로 이동합시다. 그러고 Code 버튼을 클릭하여 해당 레포지토리의 URL 를 카피해줍시다.&lt;/p&gt;
&lt;p&gt;그리고 다시 터미널로 돌아가, git clone 을 받아주시면 됩니다. 쉽게말해, 가상의 서버에다 본인의 프로젝트 내용을 클론받아서 build 를 해주고 이 서버에서 SpringBoot 를 실행시키기 위한 과정입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d0f557c9-1d38-4f87-b3fe-d6ed589b0323/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;git clone &quot;레포지토리 주소&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;java-설치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#java-%EC%84%A4%EC%B9%98&quot; aria-label=&quot;java 설치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JAVA 설치&lt;/h2&gt;
&lt;p&gt;현재 EC2 서버에는 자바가 설치되어 있지 않을겁니다. 자바 설치 여부를 확인해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;java --version&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만일 설치되어 있지 않다면 아래와 같이 자바를 서버에 설치해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;apt install default-jdk -y&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러고 다시 자바 버전을 확인하여 자바가 정상적으로 설치되었는지 확인해봅시다!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/dc35345f-8577-42e1-94ff-e945b2e1799b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx에서-가상서버-환경-설정하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx%EC%97%90%EC%84%9C-%EA%B0%80%EC%83%81%EC%84%9C%EB%B2%84-%ED%99%98%EA%B2%BD-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0&quot; aria-label=&quot;nginx에서 가상서버 환경 설정하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx에서 가상서버 환경 설정하기&lt;/h2&gt;
&lt;p&gt;다음으로 Nginx 에 관한 추가적인 설정이 필요합니다. sites-available 이란 디렉토리로 이동하여 vim 편집기를 통해 추가적인 설정을 진행해줘야 합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sites-available: 가상 서버 환경들에 대한 설정 파일들이 위치하는 부분입니다. 가상 서버를 사용하거나 사용하지 않던간에 그에 대한 설정 파일들이 위치하는 곳입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;vim /etc/nginx/sites-available/default&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;정상적으로 이동했다면 아래와 같은 vim 편집 화면이 나오게 될 겁니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d7e38b39-cfad-4d8b-a420-a5e8020ac56b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;아래로 내려시다보면 아래와 같이 Nginx 관련 설정을 그대로 진행해주시면 ㄷ굅니다. 이때 유의해야 할 점은 저는 따로 스프링부트 프로젝트를 9001번 포트로 localhost 에서 실행되도록 열어두었는데, 별도로 지정해두지 않으셨다면 proxy_pass 부분을 8080 포트로 진행해주시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/56fc0af8-3f7f-4102-8e17-ddcc41e3a627/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이제 Nginx에 문법이 제대로 되어있는지 확인하고 재시작을 해주면됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;systemctl restart nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;springboot-빌드-테스트하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#springboot-%EB%B9%8C%EB%93%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8%ED%95%98%EA%B8%B0&quot; aria-label=&quot;springboot 빌드 테스트하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SpringBoot 빌드 테스트하기&lt;/h2&gt;
&lt;p&gt;이제 앞서 클론받은 프로젝트를 빌드하고 실행시켜주면 끝입니다.
다시 &quot;/var/www/해당프로젝트&quot; 로 이동하여, 빌드가 정상적으로 수행되는지 테스트를 한번 돌려봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/20b5bdea-f0db-499d-8476-083e21c0ac4f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;./gradlew clean build&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 BUILD SUCCESSFUL 이 뜬다면 빌드 테스트에 성공한 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;springboot-실행하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#springboot-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0&quot; aria-label=&quot;springboot 실행하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SpringBoot 실행하기&lt;/h2&gt;
&lt;p&gt;거의 마지막까지 왔습니다. 이제 스프링부트 프로젝트의 스냅샵을 가지고 실행시켜주면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6c116499-9d9e-402a-b6f5-84918a8635c3/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;cd /build/libs
java -jar core-0.0.1-SNAPSHOT-plain.jar&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;아래와 같이 뜨면 서버가 정상적으로 실행된 것 입니다!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7ff77364-7cc9-4e6d-b7a3-b3041de7db8f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;무중단-배포&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC&quot; aria-label=&quot;무중단 배포 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;무중단 배포&lt;/h2&gt;
&lt;p&gt;그런데 사실 위와 같이 배포한다면, 본인의 터미널 창을 꺼버린다면 서버도 함께 종료가 됩니다. 따라서 말그래로 서버가중단되는 일 없이 &quot;무중단 배포&quot; 가 되도록 아래와 같이 nohup 을 통해 무중단 배포를 해줄 수 있습니다. nohup으로 실행해주면 프로세스를 죽이지 않는한 컴퓨터를 끄거나 터미널을 닫거나 해도 꺼지지 않습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;nohup java -jar 자신의 프로젝트.jar &amp;amp;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;참고로 아래와 같은 추가 명령어도 참고하시면 좋겠습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ ps -ef     // 서버를 종료&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ kill -9 {pid번호}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;커맨드를 통하여 프로세스를 조회한 후 백그라운드에서 돌아가고 있는 jar파일의 pid 번호를 확인하고 $ kill -9 {pid번호} 로 종료할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;결과-확인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EA%B3%BC-%ED%99%95%EC%9D%B8&quot; aria-label=&quot;결과 확인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결과 확인&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4ea76d3c-a4cc-48e2-a591-44058726e7da/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;배포한 IP 주소로 접속했을 떄 위와 같은 화면이 보인다면 성공한겁니다!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금까지 스프링부트를 Nginx 를 활용하여 EC2 서버에서 무중단 배포를 하는 방법에 대해 알아보았습니다. 사실 이 방법외에도 서버 구축 및 배포 방법은 개발자가 원하는 방법으로 하기 나름이라 정말 다양한 접근방식으로 배포가 가능할 겁니다. 그 중 제가 배포했던 방식을 설명드렸던 것이며, 배포에 대해 잘 모르셨던 분들에게 도움이 되셨으면 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dev-jwblog.tistory.com/42#1.%20%EB%AC%B4%EC%A4%91%EB%8B%A8%20%EB%B0%B0%ED%8F%AC%C2%A0&quot;&gt;https://dev-jwblog.tistory.com/42#1.%20%EB%AC%B4%EC%A4%91%EB%8B%A8%20%EB%B0%B0%ED%8F%AC%C2%A0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@hwsa1004/SpringBootAWSRDS-%EC%84%9C%EB%B2%84-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0-EC2%ED%8E%B8-&quot;&gt;https://velog.io/@hwsa1004/SpringBootAWSRDS-%EC%84%9C%EB%B2%84-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0-EC2%ED%8E%B8-&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rypro.tistory.com/218#recentComments&quot;&gt;https://rypro.tistory.com/218#recentComments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realsalmon.tistory.com/3&quot;&gt;https://realsalmon.tistory.com/3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://suyeoniii.tistory.com/52&quot;&gt;https://suyeoniii.tistory.com/52&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@bbodela/AWS-EC2&quot;&gt;https://velog.io/@bbodela/AWS-EC2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Nginx 기반 HTTPS 프로토콜을 적용하여 서비스 배포하기]]></title><description><![CDATA[SSL 이란? SSL(Secure Socket Layer) 이라는 약자로, 443 포트를 사용하며, 모든 정보전송을 암호화프로토콜을 사용하여 안전하게 전송되도록 만드는 것입니다. 클라이언트와 서버(Nginx…]]></description><link>https://haon.site/haon/infra/nginx/https/</link><guid isPermaLink="false">https://haon.site/haon/infra/nginx/https/</guid><pubDate>Mon, 05 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;ssl-이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ssl-%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;ssl 이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SSL 이란?&lt;/h2&gt;
&lt;p&gt;SSL(Secure Socket Layer) 이라는 약자로, 443 포트를 사용하며, 모든 정보전송을 암호화프로토콜을 사용하여 안전하게 전송되도록 만드는 것입니다.&lt;/p&gt;
&lt;p&gt;클라이언트와 서버(Nginx) 간의 통신 내용이 노출되거나 변경되는 것을 방지할 수 있으며, 클라이언트가 접속하려는 서버가 신뢰할 수 있는 서버인지 확인이 가능합니다.&lt;/p&gt;
&lt;h3 id=&quot;ssl-송수신-절차&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ssl-%EC%86%A1%EC%88%98%EC%8B%A0-%EC%A0%88%EC%B0%A8&quot; aria-label=&quot;ssl 송수신 절차 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SSL 송수신 절차&lt;/h3&gt;
&lt;p&gt;SSL 에서는 암호화를 사용하는데, 암호/키를 통해 신뢰할 수 있는 서버인지를 다음 절차로 확인합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트가 서버에 접속&lt;/li&gt;
&lt;li&gt;서버는 클라이언트에게 인증서 정보전달(SSL 인증서를 발급하여 나 이만큼 신뢰성있는 서버야! 라고 클라이언트에게 인증서를 보내서 검증받으려고 하는것)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;클라이언트는 인증서 정보가 신뢰할 수 있는 것인지 검증&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;검증후 원하던 요청을 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;https&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#https&quot; aria-label=&quot;https permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTPS&lt;/h2&gt;
&lt;p&gt;HTTP, HTTPS 는 둘다 정말 많이 사용하는 프로토콜입니다. HTTPS 는 모든 요청과 응답 데이터가 네트워크로 보내지기 전에 암호화됩니다.&lt;/p&gt;
&lt;p&gt;특히 &lt;strong&gt;HTTPS 는 하부에 SSL 과 같은 보안계층을 제공하여 동작합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;lets-encrypt-발급기관-certbot&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lets-encrypt-%EB%B0%9C%EA%B8%89%EA%B8%B0%EA%B4%80-certbot&quot; aria-label=&quot;lets encrypt 발급기관 certbot permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Let&apos;s Encrypt 발급기관, Certbot&lt;/h2&gt;
&lt;p&gt;이제부터 본론 내용 시작입니다. 저희는 아래와 같은 환경에서 HTTPS 프로토콜에 기반에 통신을 할겁니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/91aa3602-ddaf-48da-9b4c-80625d54a231/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Nginx-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1-%ED%99%98%EA%B2%BD%EC%9D%84-%EA%B5%AC%EC%B6%95%ED%95%B4-%ED%8A%B8%EB%9E%98%ED%94%BD-%EB%B6%84%EC%82%B0%EC%8B%9C%ED%82%A4%EA%B8%B0-feat.-%EB%AC%B4%EC%A4%91%EB%8B%A8%EB%B0%B0%ED%8F%AC&quot;&gt;[Nginx] 로드밸런싱 환경을 구축해 트래픽 분산시키기 (feat. 무중단배포)&lt;/a&gt; 에서도 설명했듯이, WAS 서버는 뒷단에 감추어짐으로써 IP주소가 유출되지 않고 보안성이 강화되었습니다. 이어서 이번에는 &lt;strong&gt;Nginx 라는 프록시 서버에 SSL 인증서를 발급해둠으로써 HTTPS 를 적용&lt;/strong&gt;시켜볼겁니다. WAS 서버가 여러대로 확장되더라도 추가 SSL 인증서 발급이 필요하지 않으니 확장성이 유리합니다.&lt;/p&gt;
&lt;p&gt;또 무료 SSL 발급기관으로 Let&apos;s Encrypt 를 사용해서 인증서를 발급받을 것이며, Let&apos;s Encrypt 기관으로부터 간단한 SSL 발급 절차를 밟도록 Certbot 를 사용하겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;domain-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#domain-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;domain 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Domain 설정&lt;/h2&gt;
&lt;p&gt;SSL 를 적용시키기 위해선 도메인 하나를 셋팅해주고, 해당 도메인을 통해 인증서를 발급받고 HTTPS 를 적용시킬 수 있습니다. 저는 &lt;a href=&quot;https://www.gabia.com/?utm_source=google&amp;#x26;utm_medium=cpc&amp;#x26;utm_term=%25EA%25B0%2580%25EB%25B9%2584%25EC%2595%2584&amp;#x26;utm_campaign=%25EA%25B0%2580%25EB%25B9%2584%25EC%2595%2584&quot;&gt;Gabia(가비아)&lt;/a&gt; 에서 500원짜리 도메인을 구입하고, 인증서를 발급받는 절차를 진행하겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;domain-호스트-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#domain-%ED%98%B8%EC%8A%A4%ED%8A%B8-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;domain 호스트 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Domain 호스트 설정&lt;/h3&gt;
&lt;p&gt;도메인을 준비하셨다면 호스트 설정을 진행해주셔야합니다. 본인이 구매한 도메인에 대한 수정 페이지로 들어가서, 아래와 같이 호스트 설정을 꼭 진행해주시길 바랍니다. 이때 값/위치에는 Nginx 서버의 IP 주소를 기입해주시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e6d229e2-22ff-4a6b-abdd-8e4b5de6c252/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx--스프링부트-was-서버-환경구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx--%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-was-%EC%84%9C%EB%B2%84-%ED%99%98%EA%B2%BD%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;nginx  스프링부트 was 서버 환경구성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx &amp;#x26; 스프링부트 WAS 서버 환경구성&lt;/h2&gt;
&lt;h3 id=&quot;nginx-설정파일-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EC%84%A4%EC%A0%95%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;nginx 설정파일 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 설정파일 생성&lt;/h3&gt;
&lt;p&gt;지난번에 진행했던 로드밸런싱 환경과 동일합니다.
다만 지난번에는 /etc/nginx/sites-enabled 에 Nginx 로드밸런싱 관련 설정파일을 생성했으나, 기존 파일을 삭제한 후 이번에는 /etc/nginx/conf.d 에 새롭게 설정파일을 만들겠습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;쉽게말해, 그냥 /etc/nginx/conf.d 에 Nginx 설정파일을 새롭게 만들어 준다고 이해하시면 됩니다!&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ cd &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;sites&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;enabled
$ rm &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;rf myapp    &lt;span class=&quot;token comment&quot;&gt;// 이 명령들은 지난번 제 포스팅을 안따라하셨다면 무시하세요!&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// conf.d 에다 Nginx 설정파일 생성&lt;/span&gt;
$ cd &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;d
$ vim &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;conf&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;설정정보-구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%A4%EC%A0%95%EC%A0%95%EB%B3%B4-%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;설정정보 구성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;설정정보 구성&lt;/h3&gt;
&lt;p&gt;그리고 생성한 default.conf 설정파일에 아래와 같이 설정정보를 구성합시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;upstream backend&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        server &lt;span class=&quot;token number&quot;&gt;111.111&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.11&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        server &lt;span class=&quot;token number&quot;&gt;222.222&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.22&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        server &lt;span class=&quot;token number&quot;&gt;333.333&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.33&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.333&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

server&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		listen &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        server_name mymsung&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;store&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 도메인 주소&lt;/span&gt;

        location &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                proxy_pass http&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;backend&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;server_name 은 앞서 구매한 도메인 주소(SSL 적용할 도메인) 을 기입해줍시다. 추후 Certbot 이라는 것을 활용할것인데, Certbot은 server_name 옵션을 기준으로 Nginx 설정 파일을 찾고, 여기에 HTTPS 에 대한 설정을 자동으로 추가해주기 때문입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;certbot-설치--lets-encrypt로-부터-인증서-발급&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#certbot-%EC%84%A4%EC%B9%98--lets-encrypt%EB%A1%9C-%EB%B6%80%ED%84%B0-%EC%9D%B8%EC%A6%9D%EC%84%9C-%EB%B0%9C%EA%B8%89&quot; aria-label=&quot;certbot 설치  lets encrypt로 부터 인증서 발급 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Certbot 설치 &amp;#x26; Let&apos;s Encrypt로 부터 인증서 발급&lt;/h2&gt;
&lt;p&gt;Certbot 이란 앞서 말씀드렸듯이, L&lt;strong&gt;et&apos;s Encrypt 이라는 SSL 인증서 발급기관으로부터 손쉽게 SSL 인증서를 자동 발급할 수 있게 도와주는 도구&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;h3 id=&quot;certbot-설치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#certbot-%EC%84%A4%EC%B9%98&quot; aria-label=&quot;certbot 설치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Certbot 설치&lt;/h3&gt;
&lt;p&gt;Ubuntu 에서는 Certbot를 설치시 아래처럼 snap 이라는 패키지를 통해 설치하는 것을 권장합니다. 따라서 apt 가 아닌 snap 으로 설치해봅시다.&lt;/p&gt;
&lt;p&gt;이때 이메일 정보를 물어본 후 1번과 2번중 선택하라는 질문이 나오는데, &lt;strong&gt;2번을 선택해주시면 http 로 URL을 접근할 경우 자동으로 https 로 리다이렉트가 됩니다.&lt;/strong&gt; 가급적이면 보안성을 위해 2번으로 해주시는게 좋겠죠?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ sudo snap install certbot &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;classic&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;ssl-인증서-발급받기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ssl-%EC%9D%B8%EC%A6%9D%EC%84%9C-%EB%B0%9C%EA%B8%89%EB%B0%9B%EA%B8%B0&quot; aria-label=&quot;ssl 인증서 발급받기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SSL 인증서 발급받기&lt;/h3&gt;
&lt;p&gt;이어서 Nginx 서버에다 SSL 인증서를 발급받도록 합시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ sudo certbot --nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러면 아래와 같이 어떤 도메인에 대해 HTTPS 를 적용시킬 것인지를 물어봅니다. 저희는 질문에 대답이 적합하게 원하는 숫자를 기입해주면 됩니다. 이 상황에서는 1을 입력하면 되겠죠?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c9da2039-27c1-49df-b76a-1bee207ed789/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이 과정까지 끝난다면 Certbot 은 자동으로 Let&apos;s.Encrypt 발급처를 통해서 자동으로 SSL 를 인증서를 발급해옵니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;https-자동-설정-확인해보기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#https-%EC%9E%90%EB%8F%99-%EC%84%A4%EC%A0%95-%ED%99%95%EC%9D%B8%ED%95%B4%EB%B3%B4%EA%B8%B0&quot; aria-label=&quot;https 자동 설정 확인해보기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTPS 자동 설정 확인해보기&lt;/h2&gt;
&lt;p&gt;그리고 /etc/nginx/default.conf 파일을 다시 열여보면 HTTPS 에 대한 각종 설정 내용들이 자동으로 반영된 것을 볼 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;upstream backend&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         server &lt;span class=&quot;token number&quot;&gt;111.111&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.11&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
         server &lt;span class=&quot;token number&quot;&gt;222.222&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.22&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
         server &lt;span class=&quot;token number&quot;&gt;333.333&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.33&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.333&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

server&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        server_name mymsung&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;store&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        location &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                proxy_pass http&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;backend&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    listen &lt;span class=&quot;token number&quot;&gt;443&lt;/span&gt; ssl&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; # managed by &lt;span class=&quot;token class-name&quot;&gt;Certbot&lt;/span&gt;
    ssl_certificate &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;letsencrypt&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;live&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mymsung&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;store&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;fullchain&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; # managed by &lt;span class=&quot;token class-name&quot;&gt;Certbot&lt;/span&gt;
    ssl_certificate_key &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;letsencrypt&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;live&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mymsung&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;store&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;privkey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; # managed by &lt;span class=&quot;token class-name&quot;&gt;Certbot&lt;/span&gt;
    include &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;letsencrypt&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;options&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ssl&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;nginx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; # managed by &lt;span class=&quot;token class-name&quot;&gt;Certbot&lt;/span&gt;
    ssl_dhparam &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;letsencrypt&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ssl&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;dhparams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; # managed by &lt;span class=&quot;token class-name&quot;&gt;Certbot&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


server&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$host &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mymsung&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;store&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;301&lt;/span&gt; https&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;$host$request_uri&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; # managed by &lt;span class=&quot;token class-name&quot;&gt;Certbot&lt;/span&gt;

        listen &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        server_name mymsung&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;store&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; # managed by &lt;span class=&quot;token class-name&quot;&gt;Certbot&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞서 2번으로 설정시 http 로 들어온 요청이 https 로 자동으로 리다이렉션된다고 했었죠? 그 내용도 자동 추가되었습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;server&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$host &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mymsung&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;store&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;301&lt;/span&gt; https&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;$host$request_uri&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; # managed by &lt;span class=&quot;token class-name&quot;&gt;Certbot&lt;/span&gt;

        listen &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        server_name mymsung&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;store&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; # managed by &lt;span class=&quot;token class-name&quot;&gt;Certbot&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 일부분을 뜯어내서 자세히 보면, HTTP 요청으로 들어온 호스트(host) 가 mymsung.store 인 경우 301 상태코드를 활용하여 리다이렉션 시켜주는 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;실제로 리다이렉션이 잘 되는지를 확인해봅시다. 아래와 같이 그냥 http 요청을 보낸다면&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/bbff2f80-5f8b-45a3-9b29-fd7bc19b611a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;자동으로 https 로 리다이렉셕된 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/cc046c75-0919-4d77-aac8-fa95e253bd3d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ssl-자동갱신&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ssl-%EC%9E%90%EB%8F%99%EA%B0%B1%EC%8B%A0&quot; aria-label=&quot;ssl 자동갱신 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SSL 자동갱신&lt;/h2&gt;
&lt;p&gt;Let&apos;s Encrypt 에서 발급해주는 인증서는 90일짜리 단기 인증서로, 영구적이지 않습니다. 저희는 90일마다 직접 인증서를 다시 받아오는 귀찮은 작업을 하지말고, Crontab 이라는 것을 활용합시다.&lt;/p&gt;
&lt;p&gt;Crontab 이란 스캐쥴링 도구로써, 특정한 주기로 명령어를 수행할 떄 사용합니다. 아래처럼 crontab 에 접속해서 스캐큘링 설정정보를 추가해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ crontab &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;e&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;접속하셨다면 맨 아래줄에 아래 내용을 기입해주세요! 매월, 매일 0시0분에 certbot 을 실행하고 SSL 를 새롭게 발급받은 후 Nginx.conf 파일을 reload 해주는 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; certbot renew &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;post&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;hook &lt;span class=&quot;token string&quot;&gt;&quot;sudo service nginx reload&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;설정 구성이 끝났다면 CTRL + X 로 빠져나올 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이렇게 Nginx 서버에서 어떻게 SSL 를 자동 발급받고 HTTPS 프로토콜에 기반해 통신할 수 있을지에 대해 자세히 다루어봤습니다. Nginx 를 학습하시는 모든 분들에게 도움이 되셨으면 합니다 😉&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://eff-certbot.readthedocs.io/en/stable/&quot;&gt;Certbot Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://minholee93.tistory.com/entry/Nginx-Lets-Encrypt-SSL-Certificates&quot;&gt;[Nginx] Let&apos;s Encrypt - SSL Certificates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://junho85.pe.kr/2048&quot;&gt;https 적용하기 - certbot. Let&apos;s encrypt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://syudal.tistory.com/entry/Ubuntu-Nginx-Lets-Encrypt%EB%A1%9C-https-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot;&gt;Let&apos;s Encrypt 로 https 적용하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://linuxmadang.tistory.com/entry/linuxcrontab-e-%EC%97%90%EC%84%9C-%EB%B9%A0%EC%A0%B8%EB%82%98%EC%98%A4%EA%B8%B0&quot;&gt;[linux][crontab -e 에서 빠져나오기]&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gabia.com/&quot;&gt;gabia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[TDD(테스트 주도 개발) 와 BDD(행위 주도 개발)]]></title><description><![CDATA[학습배경 우리 하모니 팀에서 테스트 코드를 작성하면서 BDD 스타일로 코드를 작성하고 있는데, 아직 남에게 언제든 즉각 BDD 그리고 TDD 에 대한 설명할 수 있을만큼 생각이 완벽하게 정교화되지 않은 것 같다. 왜 TDD…]]></description><link>https://haon.site/test/tdd-bdd/</link><guid isPermaLink="false">https://haon.site/test/tdd-bdd/</guid><pubDate>Sat, 03 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;우리 하모니 팀에서 테스트 코드를 작성하면서 BDD 스타일로 코드를 작성하고 있는데, 아직 남에게 언제든 즉각 BDD 그리고 TDD 에 대한 설명할 수 있을만큼 생각이 완벽하게 정교화되지 않은 것 같다. 왜 TDD 방법론으로 개발하고 있는지 하모니 팀원에게 질문을 받은적이 있는데, 설득은 했지만 속마음으로는 다소 생각이 정교화되지 않은 느낌을 받았다. 또한 카카오테크 교육과정속에서 맥(mac) 코치님의 TDD, BDD 특강을 들은적이 있는데, 그때 이후로 개발하기에 급급하여 정교화된 학습을 미루어두고 있었다. 이번 기회에 TDD 와 BDD 에 대한 생각의 정교화를 위해 글을 작성하고, 학습해보도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;tdd-test-driven-development&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tdd-test-driven-development&quot; aria-label=&quot;tdd test driven development permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TDD (Test Driven Development)&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d681019a184fe9a655d067c864cd5802/07d37/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.78527607361963%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABNklEQVR42o2RvUsDQRDF88dY2FrZilhaBauAhaVpLAIptQoIQbCyCBaCdgpWiiISSKEoFhYGkYSQEHN4fuzlNre7t9nde15yEaLkzgwsA8PbH2/epJBUQRA16sI/P4Zu1n7NJ1UqEWgMTNjY7hbIwgxodgXao4nQRKCSEi37HaxUBFmahZdfg0e+IHw5nUMhBBhjw6eUQq9HIVUfgeDo31zDvHUw8EU+CUxg4oHByL7jOLBtG5ZlgXMGQlxwof4Gi0a7DupSjP+NXflHQLoC2c0rrObO0Hx1ogi0wn51BxuVDB4/7kZ6Ew8cwLSOBJX7NubTh5hbPsDJxctwZnkdrJfTyFwuovRUjO6WBBw/ni81Cnu3yG+XUX1uhJlK+NzHae0IhYccWrQ+3cqTynW7oJSGufJ/td/786sV/lP1MwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/d681019a184fe9a655d067c864cd5802/a6d36/image.png&quot;
        srcset=&quot;/static/d681019a184fe9a655d067c864cd5802/222b7/image.png 163w,
/static/d681019a184fe9a655d067c864cd5802/ff46a/image.png 325w,
/static/d681019a184fe9a655d067c864cd5802/a6d36/image.png 650w,
/static/d681019a184fe9a655d067c864cd5802/e548f/image.png 975w,
/static/d681019a184fe9a655d067c864cd5802/3c492/image.png 1300w,
/static/d681019a184fe9a655d067c864cd5802/07d37/image.png 1790w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;진짜 아무것도 몰랐던 시절에는 &lt;code class=&quot;language-text&quot;&gt;TDD = 테스트 코드 작성&lt;/code&gt; 인 줄 알았다. 하지만 이는 큰 오산이었다. TDD 를 소프트웨어 개발 &quot;방법론&quot; 이다.&lt;/p&gt;
&lt;p&gt;TDD 를 없이 무작정 테스트 코드를 작성하던 시절, 기존 테스트 코드 작성 방식은 프로덕션 코드를 먼저 모두 작성한 뒤에서야 테스트 코드를 작성했다. 하지만 TDD 는 이 순서가 반전된 것이다. &lt;strong&gt;TDD 는 프로덕션 보다 보다 일단 실패하는 테스트 코드를 먼저 작성한다. 이후에 실패한 테스트 코드를 통과시키기 위한 최소한의 프로덕션 코드를 작성하고, 점진적으로 리팩토링하는 방식&lt;/strong&gt;이다. 테스트가 통과했다면, 이후 다른 기능을 개발하기 위한 새로운 (실패하는) 테스트 코드를 작성하거나 또는 프로덕션 코드를 리팩토링한다.&lt;/p&gt;
&lt;h3 id=&quot;tdd-사이클&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tdd-%EC%82%AC%EC%9D%B4%ED%81%B4&quot; aria-label=&quot;tdd 사이클 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TDD 사이클&lt;/h3&gt;
&lt;p&gt;TDD 는 &lt;code class=&quot;language-text&quot;&gt;RED&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;GREEN&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;REFACTOR&lt;/code&gt; 이 3가지의 사이클을 계속 반복하는 워크플로우 방식으로 진행된다. 이 3가지는 무엇을 뜻할까?&lt;/p&gt;
&lt;h3 id=&quot;red-실패하는-테스트-코드를-우선-작성한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#red-%EC%8B%A4%ED%8C%A8%ED%95%98%EB%8A%94-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%9A%B0%EC%84%A0-%EC%9E%91%EC%84%B1%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;red 실패하는 테스트 코드를 우선 작성한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RED: 실패하는 테스트 코드를 우선 작성한다.&lt;/h3&gt;
&lt;p&gt;동작하는 프로뎍션 코드가 전혀 없는 상황에서 우선 테스트 코드를 작성한다. 테스트 코드는 향후 개발될 프로덕션 코드가 어떠한 &lt;code class=&quot;language-text&quot;&gt;기능&lt;/code&gt; 을 정상적으로 수행하는지를 중점으로 작성한다. 후술할 BDD 와 다른점은, TDD 는 &lt;code class=&quot;language-text&quot;&gt;기능 확인&lt;/code&gt;을 중점으로 작성한다면, BDD 는 작성한 시나리오에 대한 &lt;code class=&quot;language-text&quot;&gt;행위&lt;/code&gt; 를 정상적으로 수행하는지를 중점으로 작성한다. BDD 는 이따가 자세히 학습해보자.&lt;/p&gt;
&lt;p&gt;또한 아직 프로덕션 코드가 존재하지 않기 때문에, 테스트 코드를 실패할 수 밖에 없다. 핵심은 우선적으로 실패하는 테스트 코드를 작성하는 것, 즉 RED 를 띄우는 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getMemberTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; member &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;devHaon&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; member&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getNickname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isEqualTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;devHaon&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;나는 Member 클래스의 &lt;code class=&quot;language-text&quot;&gt;getter&lt;/code&gt; 가 닉네임을 정상적으로 출력하는지 기능을 테스트하기 위해 위처럼 테스트를 작성했다. 당연하게도 위 테스트 코드는 아직 Member 클래스, getter 등의 프로덕션 코드가 작성되지 않았기 떄문에 실패할 것이다. 즉, 일단 RED 를 띄운 상태이다.&lt;/p&gt;
&lt;h3 id=&quot;green-테스트를-통과하는-최소한의-프로덕션-코드를-작성한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#green-%ED%85%8C%EC%8A%A4%ED%8A%B8%EB%A5%BC-%ED%86%B5%EA%B3%BC%ED%95%98%EB%8A%94-%EC%B5%9C%EC%86%8C%ED%95%9C%EC%9D%98-%ED%94%84%EB%A1%9C%EB%8D%95%EC%85%98-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%9E%91%EC%84%B1%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;green 테스트를 통과하는 최소한의 프로덕션 코드를 작성한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GREEN: 테스트를 통과하는 최소한의 프로덕션 코드를 작성한다.&lt;/h3&gt;
&lt;p&gt;앞서 RED 를 띄웠지만, 아쉽게도 테스트는 실패했다. 다음 단계인 GREEN 에서는 테스트를 통과하도록 최소한의 프로덕션 코드를 작성하는 것이다. 여기서 유의할 점은 &quot;최소한의&quot; 코드를 작성하는 것이다. &lt;strong&gt;리팩토링은 나중으로 미루고, 일단 작성한 테스트 코드를 통과시키는데만 집중하여 코드를 작성하는 것이다.&lt;/strong&gt; 간신히 작동하는 코드여도 괜찮다. 우선적으로 현재 작성된 테스트를 통과시키는데만 집중한다.&lt;/p&gt;
&lt;p&gt;GREEN 을 보기 위해서는 명백한 실제 구현을 입력하는 방법도 있겠지만, 정의상으론 최대한 빨리 GREEN 상태를 보기 위해서는 상수를 반환하는 코드를 작성하고 점진적으로 변수로 바꾸어가는 방법이 더 바람직하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; nickname&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getNickname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;devHaon&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드는 GREEN 을 띄우기 위해 최소한으로 작성된 프로덕션 코드다. 실제로 앞서 작성한 테스트 코드를 실행시키면, 문제 없이 통과할 것이다.&lt;/p&gt;
&lt;p&gt;하지만 실제로 적절한 닉네임을 리턴해주는 역할을 수행하지 못한다. 앞서 작성한 테스트를 통과하는 빈 껍대기에 불과하다. GREEN 을 띄우기 위해선, 한번에 하나의 일만 집중하도록 일단 빠르게 테스트 코드를 통과시키는 껍대기 코드를 작성하는 것이다. &lt;strong&gt;즉, 일단 동작하는 쓰레기 코드를 작성하는 단계다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;refactor-리팩토링-한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#refactor-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;refactor 리팩토링 한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;REFACTOR: 리팩토링 한다.&lt;/h3&gt;
&lt;p&gt;앞서 작성한 GREEN 을 통과하기 위해 만든 더티 코드를 리팩토링한다. 즉, 이 단계에선 좋은 코드로, 정상적인 기능을 수행하는 코드로 리팩토링하는 단계다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; nickname&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; nickname&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nickname &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nickname&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getNickname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; nickname&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;tdd-는-왜-쓰는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tdd-%EB%8A%94-%EC%99%9C-%EC%93%B0%EB%8A%94%EA%B0%80&quot; aria-label=&quot;tdd 는 왜 쓰는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TDD 는 왜 쓰는가?&lt;/h2&gt;
&lt;h3 id=&quot;한번에-하나의-일만-집중할-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%9C%EB%B2%88%EC%97%90-%ED%95%98%EB%82%98%EC%9D%98-%EC%9D%BC%EB%A7%8C-%EC%A7%91%EC%A4%91%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;한번에 하나의 일만 집중할 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;한번에 하나의 일만 집중할 수 있다.&lt;/h3&gt;
&lt;p&gt;TDD 를 적용하지 않은 상황에서는 현재 모듈과 관련한 코드를 작성하다가, 갑자기 다른 모듈과 클래스에 한 눈이 팔릴 수 있다. 즉, TDD 를 적용하면 현재 프로덕션 코드에 대한 테스트를 통과시키는데에 온전히 집중할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;빠른-주기로-피드백-받을-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%A0%EB%A5%B8-%EC%A3%BC%EA%B8%B0%EB%A1%9C-%ED%94%BC%EB%93%9C%EB%B0%B1-%EB%B0%9B%EC%9D%84-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;빠른 주기로 피드백 받을 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;빠른 주기로 피드백 받을 수 있다.&lt;/h3&gt;
&lt;p&gt;TDD 를 적용하여 빠르게 테스트 코드를 작성할 수 있고, 피드백을 받고, 점진적인 리팩토링을 할 수 있다. 처음부터 완벽한 설계와 코드를 작성해야 한다는 부담도 없어진다. 또한 중간중간에 숨겨진 버그를 빠르게 찾아내는 피드백 과정이 동반된다.&lt;/p&gt;
&lt;h3 id=&quot;프로뎍션-코드에-대한-이해도와-신뢰가-높아진다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A1%9C%EB%8E%8D%EC%85%98-%EC%BD%94%EB%93%9C%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%B4%ED%95%B4%EB%8F%84%EC%99%80-%EC%8B%A0%EB%A2%B0%EA%B0%80-%EB%86%92%EC%95%84%EC%A7%84%EB%8B%A4&quot; aria-label=&quot;프로뎍션 코드에 대한 이해도와 신뢰가 높아진다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프로뎍션 코드에 대한 이해도와 신뢰가 높아진다.&lt;/h3&gt;
&lt;p&gt;프로덕션 제품의 기능, 정책, 비즈니스 로직등을 통과시키기 위한 테스트 코드를 중점으로 작성한다. 즉, TDD 에 기반하여 작성된 테스트 코드는 프로덕션 코드에 대한 요구사항을 만족시키기 위해 작성된다. 또한 이 요구사항을 만족시키기 위해 처음부터 완벽한 코드를 작성하지 않고, 점진적으로 작성해나간다. 이 방법을 통해 프로덕션 코드에 대한 테스트 코드의 믿음, 신뢰도가 향상된다. 또한 비슷한 이유로 이해도도 향상될 것이다. 테스트 코드 자체가 일종의 문서가 되는 셈이다.&lt;/p&gt;
&lt;h3 id=&quot;의도치-않은-유용한-부산물을-만들-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%98%EB%8F%84%EC%B9%98-%EC%95%8A%EC%9D%80-%EC%9C%A0%EC%9A%A9%ED%95%9C-%EB%B6%80%EC%82%B0%EB%AC%BC%EC%9D%84-%EB%A7%8C%EB%93%A4-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;의도치 않은 유용한 부산물을 만들 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;의도치 않은 유용한 부산물을 만들 수 있다.&lt;/h3&gt;
&lt;p&gt;프로뎍션을 개발하다보면 요구사항이 변화되고, 다른 방향으로 코드를 유연하게 확장해야하는 상황이 많다. 새로운 코드를 작성한 뒤, 그 코드로 갈아끼웠을 때 이 코드가 다른 모둘에 영향을 끼치지 않을지 불안감에 가득할 수 밖에 없다. 이때 이전에 작성한 테스트를 실행하여, 테스트가 성공 또는 실패하는지 확인하여 틀리지 않았음을 굳게 믿을 수 있다. 반대로 당연히 통과할 것 같던 프로덕션 코드도 예상치 못한 코드로 인해 문제가 발생할 수 있는데, 이 또한 기존에 작성한 테스트를 통해 알아낼 수 있다. 즉, TDD 를 통해 만들어진 테스트 코드는 의도치 않은 유용한 부산물이 된다. 또한 비슷한 맥락으로 안전하게, 믿음직한 확장성, 유지보수를 용이하게 만든다.&lt;/p&gt;
&lt;h2 id=&quot;bdd-behavior-driven-development&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bdd-behavior-driven-development&quot; aria-label=&quot;bdd behavior driven development permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BDD (Behavior Driven Development)&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/66a58e0cc8f35e6fa70895ecd7ea247e/e515d/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.71165644171779%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAACGklEQVR42oWT3U/TUBiH918bL4w3XnmzxBCNUZg3JDN6YaaBBHQifsTJFHTIRgdbGfuijG7t2q7taft42sKAuMFJTnravu9zfu9XhnhFEQQ+hHKLiXwPsep1BsUPuJ12YhLGNrFpcmbuyiRG3pig+4tAqxJqfxHmGfrbAsb7dZS7d+jmFlNYGF5zjuYBE4XCTY5C/Yy/+YBqo0Wl0UDfKvF1aZk3fZ/lQ5N8c0yx52D64Uxo5vK61MBvbOCXHqG2T6nuVnjVsnnSEnzsWOyNPHaGPq9Vi+zeiJrhT/XMAKZfQ0tj8i0rc+qweiLI1Q2EDDVqH8m0tIkmdqJLMQUPd4d07OAaNHM9KWEawtEnmkqJ7P4E208d7FYLbXUVR1WnYX7RXF4cmDcAr1Rzrevw7thKzt7IoF8o0MvnOa2VmeBIgmDkeTyujThzL/M5A5g+X8rkf9ectFB9hWGzwkapwo+9NeqnG2jmIV1DYVExUC0xVTkXWJDFKHbPFTaKuNs5bF1ns7ZEz6glelaOB9zfGbI1cKe+c4G7Q4+n+wY4JwT2ANH5KYtSRne6+GGqKG6fe9s6bfsGhVf76pliUuy7/7VGdG7VHPuUpTo3iKZ+mVnjc/EzTvZCdST7bkzP8XFFgCVCfusez+smf3QX3Q1Qx2JGH86BxoCVtmxuGf5CTe6qQe4ghnm3TMotYCGTa8hxiy+4CD+62Fcm5R+owiyKl6gxrgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/66a58e0cc8f35e6fa70895ecd7ea247e/a6d36/image-1.png&quot;
        srcset=&quot;/static/66a58e0cc8f35e6fa70895ecd7ea247e/222b7/image-1.png 163w,
/static/66a58e0cc8f35e6fa70895ecd7ea247e/ff46a/image-1.png 325w,
/static/66a58e0cc8f35e6fa70895ecd7ea247e/a6d36/image-1.png 650w,
/static/66a58e0cc8f35e6fa70895ecd7ea247e/e548f/image-1.png 975w,
/static/66a58e0cc8f35e6fa70895ecd7ea247e/3c492/image-1.png 1300w,
/static/66a58e0cc8f35e6fa70895ecd7ea247e/e515d/image-1.png 1430w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;BDD 는 말 그대로 행위 주도 개발이다. 앞선 TDD 가 &lt;code class=&quot;language-text&quot;&gt;기능 정상 동작&lt;/code&gt; 을 중점으로 확인한 방법론이라면, BDD 는 &lt;code class=&quot;language-text&quot;&gt;특정한 일련의 행위&lt;/code&gt; 가 정상 동작하는지를 중점으로 테스트하는 방법론이다.  이 떄문에 &lt;strong&gt;BDD 는 테스트를 작성하기 위한 일련의 유저 시나리오를 요구한다.&lt;/strong&gt; 하나의 유저 시나리오내에는 어떠한 행위가 이행되는지의 일련의 스토리가 테스트 코드로 표현될 것이다. 시나리오대로 작성된 테스트 코드가 문제없이 동작하는지 작성해야한다.&lt;/p&gt;
&lt;p&gt;TDD를 하면서 개발을 하다보면 테스트에 대해서도 계속 유지보수해야한다. 하지만 이 규모가 커진다면 관리하기에 번거롭기도 하고, 마감기한이 정해진 프로젝트라면 일정에 대해 압박을 받을 수도 있다. 그렇기 때문에 매번 개발을 진행하면서 기능에 대해 예외사항에 대해 매번 생각하고 모든 엣지 케이스에 대한 테스트를 작성하는 것은 생각보다 비용이 많이 들어가는 일이라고 볼 수 있다.&lt;/p&gt;
&lt;p&gt;하지만 만약 여기에서 이미 작성된 요구사항이나 기획서가 있고, 그에 맞추어 테스트 케이스를 중점으로 작성하게 된다면, 위와 같은 시간에 대한 비용이 줄게된다. 이것이 바로 BDD이다.&lt;/p&gt;
&lt;p&gt;BDD 는 TDD 에서 파생된 테스트 방법론이기 떄문에, 이 둘은 상호 배타적인 관계가 아니라 상호 보완적인 관계이다. 프로젝트에서 BDD의 테스트케이스로 시나리오 검증을 하고, 해당 시나리오에서 사용하는 각 모듈들은 TDD의 테스트케이스로 검증을 하는 방법이 좋다.&lt;/p&gt;
&lt;h3 id=&quot;given-when-then-절&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#given-when-then-%EC%A0%88&quot; aria-label=&quot;given when then 절 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;given-when-then 절&lt;/h3&gt;
&lt;p&gt;잘 몰랐을 때는 given, when, then 절이 TDD 에만 해당하는 기법인 . 줄 알았는데, 이는 다시금 생각해보면 BDD 방법론에 더 가깝다. given, when, then 절은 행위를 검증하는데에 적합한 테스트 작성 구조이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;given&lt;/code&gt; : 테스트를 위해 주어진 상태&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;when&lt;/code&gt; : 테스트 대상에게 가해진 어떠한 상태&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;then&lt;/code&gt; : 앞선 과정의 결과&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;아래는 BDD 기반의 테스트 코드다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; 유저가_로그인을_시도하면_토큰을_반환한다&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// given&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; member &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;devHaon&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// when&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; token &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; loginService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;member&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// then&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;token&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNotEmpty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;어떠한가? 앞서 살펴본 TDD 테스트 코드와 느낌이 다소 다르지 않은가? 보듯이 &lt;strong&gt;BDD 스타일로 작성된 코드는 사용자의 행위를 작성하고, 그 결과에 대해 검증을 진행하는 방식이다.&lt;/strong&gt; 또한 이 행위 검증을 위해 given-when-then 구조를 취하고 있는 모습이다.&lt;/p&gt;
&lt;h2 id=&quot;tdd-vs-bdd&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tdd-vs-bdd&quot; aria-label=&quot;tdd vs bdd permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TDD vs BDD&lt;/h2&gt;
&lt;p&gt;다시 정리하자면, TDD 는 테스트할 모듈의 &quot;기능&quot; 이 정상적으로 동작하는지 확인 검증을 위해 작성되는 방법론이다. 반면 BDD 는 사용자 시나리오 주체를 기준으로 행위를 확인하는 목적으로 작성되는 방법론이다.&lt;/p&gt;
&lt;h3 id=&quot;tdd-와-bdd-를-어떻게-활용해야하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tdd-%EC%99%80-bdd-%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%99%9C%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;tdd 와 bdd 를 어떻게 활용해야하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TDD 와 BDD 를 어떻게 활용해야하는가?&lt;/h3&gt;
&lt;p&gt;그렇다면 두 방법론 중에 1가지만을 선택해서 사용해야하는가? 라고 의문이 들 수 있지만, 둘의 적절한 타협과 조화를 통해 더 나은 테스트 코드를 만들 수 있다. &lt;strong&gt;BDD의 테스트케이스로 시나리오 검증을 하고, 해당 시나리오에서 사용하는 각 모듈들은 TDD의 테스트케이스로 검증을 하는 방법이 많은 커버리지를 확보할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mingule.tistory.com/43&quot;&gt;https://mingule.tistory.com/43&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/test-driven-development/&quot;&gt;https://hudi.blog/test-driven-development/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.wakmusic.xyz/tdd-vs-bdd-c738b507930f&quot;&gt;https://blog.wakmusic.xyz/tdd-vs-bdd-c738b507930f&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[제 4장. 결과를 전달하는 HTTP 상태 코드]]></title><description><![CDATA[💡 본 포스트는 "그림으로 배우는 HTTP & Network Basic…]]></description><link>https://haon.site/network/http-basic/four/</link><guid isPermaLink="false">https://haon.site/network/http-basic/four/</guid><pubDate>Sat, 03 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 본 포스트는 &quot;그림으로 배우는 HTTP &amp;#x26; Network Basic&quot; 라는 책을 읽고 내 생각과 책 내용을 인용하여 작성했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;상태-코드는-서버로부터-리퀘스트-결과를-전달한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%81%ED%83%9C-%EC%BD%94%EB%93%9C%EB%8A%94-%EC%84%9C%EB%B2%84%EB%A1%9C%EB%B6%80%ED%84%B0-%EB%A6%AC%ED%80%98%EC%8A%A4%ED%8A%B8-%EA%B2%B0%EA%B3%BC%EB%A5%BC-%EC%A0%84%EB%8B%AC%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;상태 코드는 서버로부터 리퀘스트 결과를 전달한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;상태 코드는 서버로부터 리퀘스트 결과를 전달한다&lt;/h2&gt;
&lt;p&gt;클라이언트가 서버를 향해 요청을 보낼 때 서버에서 그 결과가 어떻게 되었는지 알려주는 것이 상태코드의 역할이다. 서버가 요청을 정상적으로 처리했는지, 그렇지 않다면 요청 결과가 에러였는지를 알 수 있다.&lt;/p&gt;
&lt;p&gt;상태 코드는 클래스는 아래와 같이 5가지 종류로 정의되어있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;1xx&lt;/code&gt; (Informational) : 요청을 받아들여 처리중인 상태&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;2xx&lt;/code&gt; (Success) : 요청을 정상적으로 처리한 상태&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;3xx&lt;/code&gt; (Redirection) : 요청을 완료하기 위해서 추가 동작이 필요한 상태&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;4xx&lt;/code&gt; (Client Error) : 서버가 요청을 이해 불가능한 상태&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;5xx&lt;/code&gt; (Server Error) : 서버가 요청 처리를 실패한 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;HTTP 상태코드는 RFC2616 에 실려있는 것 만으로도 40개 종류가 넘고, 게다가 WebDAV 와 Addational HTTP Status Codes 등의 확장을 포함하면 60종류 이상이 존재한다. 하지만 실제로 자주 사용되고 있는 것은 그 중에서 14가지 종류 정도이다. 여기선 대표적인 154개의 상태 코드에 대해 학습하도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;2xx-성공-success&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2xx-%EC%84%B1%EA%B3%B5-success&quot; aria-label=&quot;2xx 성공 success permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2xx 성공 (Success)&lt;/h2&gt;
&lt;p&gt;2xx 응답은 클라이언트가 전송한 요청을 서버가 정상적으로 처리했음을 나타내는 것이다. 응답에서 상태코드와 함께 되돌아 오는 정보는 메소드에 따라 다르다. 예를들어 GET 메소드는 요청된 리소스에 대응하는 엔티티가 응답로 보내지고, HEAD 메소드는 요청된 리소스에 대응하는 엔티티 헤더 필드가 Message Body 를 동반하지 않고 응답으로 되돌아온다.&lt;/p&gt;
&lt;h3 id=&quot;204-no-content&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#204-no-content&quot; aria-label=&quot;204 no content permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;204 No Content&lt;/h3&gt;
&lt;p&gt;이 응답은 서버가 요청을 받아서 처리하는데는 성공했지만, 응답에 엔티티 바디를 포함하지 않는다. 또한, 어떠한 엔티티 바디를 되돌려 보내서도 안된다. 클라이언트에서 서버에 정보를 보내는 것만으로 충분하고, 클라이언트에게 새로운 정보를 보낼 필요가 없는 경우에 사용된다.&lt;/p&gt;
&lt;h3 id=&quot;206-partial-content&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#206-partial-content&quot; aria-label=&quot;206 partial content permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;206 Partial Content&lt;/h3&gt;
&lt;p&gt;Range 에 의해서 범위가 지정된 요청에 의해서 서버가 GET 요청을 부분적으로 응답받았음을 나타낼 때 사용한다. 응답에는 &lt;code class=&quot;language-text&quot;&gt;Content-Range&lt;/code&gt; 로 지정된 범위의 엔티티가 포함된다.&lt;/p&gt;
&lt;h3 id=&quot;3xx-리다이렉트redirectiono&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3xx-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%ED%8A%B8redirectiono&quot; aria-label=&quot;3xx 리다이렉트redirectiono permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3xx 리다이렉트(Redirectiono)&lt;/h3&gt;
&lt;p&gt;3xx 응답은 요청이 정상적으로 처리를 종료하기 위해 브라우저 측에 특별한 처리를 수행해야 함을 나타낸다.&lt;/p&gt;
&lt;h3 id=&quot;301-moved-permanently&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#301-moved-permanently&quot; aria-label=&quot;301 moved permanently permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;301 Moved Permanently&lt;/h3&gt;
&lt;p&gt;요청된 리소스에 새로운 URI 가 부여되어 있기 때문에, 이후로는 그 리소스를 참조하는 URI 를 사용해야 한다는 것을 나타내고 있다. 결국 북마크하고 있는 경우에는 Location 헤더 필드에서 가리키고 있는 URI 에 북마크를 다시 하는게 좋다는 것을 나타내고 있다.&lt;/p&gt;
&lt;p&gt;301 이 발생하는 상황 예시로는 디렉토리를 지정했을 때 마지막 부분에 슬래시 &quot;/&quot; 를 붙이는 것을 잊은 경우 등이 해당한다.&lt;/p&gt;
&lt;h3 id=&quot;302-found&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#302-found&quot; aria-label=&quot;302 found permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;302 Found&lt;/h3&gt;
&lt;p&gt;요청된 리소스에는 새로운 URI 가 할당되어 있기 떄문에, 그 URI 를 참조해 주길 바란다는 의미를 나타낸다. 301 Moved Permanently 와 비슷하지만, 302 는 영구적인 이동이 아닌, 어디까지나 일시적인 것이다. 결국, 이동하는 곳의 URI 는 앞으로 이동될 가능성이 있다. 예를들면, 북마크하고 있는 경우네는 301의 경우와 같이 북마크를 변경하지 않고, 계속해서 302를 돌려준 페이지에 대해서 북마크해야 한다.&lt;/p&gt;
&lt;h3 id=&quot;303-see-ohter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#303-see-ohter&quot; aria-label=&quot;303 see ohter permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;303 See Ohter&lt;/h3&gt;
&lt;p&gt;이 응답은 요청에 대한 리소스는 다른 URI 에 있기 떄문에 GET 메소드를 사용해서 얻어야 한다는 것을 나타내고 있다. 이것은 302 Found 와 같은 기능이지만, 리다이렉트 장소를 GET 메소드로 얻어야 한다고 명확하게 되어 있다는 점이 302 와 다르다.&lt;/p&gt;
&lt;p&gt;예를들어, POST 메소드로 엑세스한 CGI 프로그램을 실행한 후에 처리 결과를 별도의 URI 에 GET 메소드로 리다이렉트 시키고 싶은 경우 등에 303 이 사용되고 있다. 302 Found 도 가능하지만, 303 을 사용하는 것이 더 바람직하다.&lt;/p&gt;
&lt;h3 id=&quot;304-not-found&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#304-not-found&quot; aria-label=&quot;304 not found permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;304 Not Found&lt;/h3&gt;
&lt;p&gt;이 응답은 클라이언트가 조건부 요청을 했을 때 리소스에 대한 엑세스는 허락하지만, 조건이 충족되지 않음을 표현한다. 304 를 되돌려 줄 경우에는 응답 바디에 어떤 것도 포함되어 있어서는 안된다. &lt;strong&gt;304 는 3xx 에 분류되어 있지만, 리다이렉트와는 연관이 없다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;307-temporary-redirect&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#307-temporary-redirect&quot; aria-label=&quot;307 temporary redirect permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;307 Temporary Redirect&lt;/h3&gt;
&lt;p&gt;302 Found 와 같은 의미를 지니지만, 302 는 POST 로 부터 GET 으로 치환이 금지되어 있는데도 불구하고 구현상 그와 같이 되어 있지는 않다. 307 에서는 브라우저 사양에 따라 POST 에서 GET 으로 치환하지 않는다. 단지, 브라우저마다 응답을 처리하는 동작이 다를 경우가 있다.&lt;/p&gt;
&lt;h2 id=&quot;4xx-클라이언트-에러client-error&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4xx-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%97%90%EB%9F%ACclient-error&quot; aria-label=&quot;4xx 클라이언트 에러client error permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4xx 클라이언트 에러(Client Error)&lt;/h2&gt;
&lt;h3 id=&quot;400-bad-request&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#400-bad-request&quot; aria-label=&quot;400 bad request permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;400 Bad Request&lt;/h3&gt;
&lt;p&gt;이 응답은 요청 구문이 잘못되었음을 나타내고 있다. 이 애러가 발생한 경우, 요청 내용을 다시 재컴토하고 나서 재송신할 필요가 있다.&lt;/p&gt;
&lt;h3 id=&quot;401-unauthorized&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#401-unauthorized&quot; aria-label=&quot;401 unauthorized permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;401 UnAuthorized&lt;/h3&gt;
&lt;p&gt;송신한 요청에 HTTP 인증 정보가 필요하다는 것을 나타낸다. 즉, 유저 인증(Authentication)에 실패했음을 나타낸다.&lt;/p&gt;
&lt;h3 id=&quot;403-forbidden&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#403-forbidden&quot; aria-label=&quot;403 forbidden permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;403 Forbidden&lt;/h3&gt;
&lt;p&gt;인증(Authentication) 에는 성공했지만, 요청된 리소스의 엑세스가 거부되었음을 나타낸다. 즉, 인가(Authorization) 에 실패했음을 나타낸다. 서버 측은 거부의 이유를 분명히 하여 클라이언트에게 전달할 필요가 있는데, 이유를 명확하게 하는 경우에는 엔티티 바디에 기재해서 유저 측에 표시한다.&lt;/p&gt;
&lt;p&gt;403 이 발생한 원인으로는 파일 시스템의 권한(permission) 이 부여되지 않은 경우와, 엑세스 권한에 문제(허가되지 않은 송신 IP 주소의 엑세스 등) 가 있는것을 예로 들 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;404-not-found&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#404-not-found&quot; aria-label=&quot;404 not found permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;404 Not Found&lt;/h3&gt;
&lt;p&gt;요청한 리소스가 서버에 존재하지 않음을 나타낸다.&lt;/p&gt;
&lt;h2 id=&quot;5xx-서버-에러server-error&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5xx-%EC%84%9C%EB%B2%84-%EC%97%90%EB%9F%ACserver-error&quot; aria-label=&quot;5xx 서버 에러server error permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5xx 서버 에러(Server Error)&lt;/h2&gt;
&lt;h3 id=&quot;500-internal-server-error&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#500-internal-server-error&quot; aria-label=&quot;500 internal server error permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;500 Internal Server Error&lt;/h3&gt;
&lt;p&gt;서버에서 요청을 처리하는 도중에 에러가 발생했음을 나타낸다. 웹 애플리케잇녀에 에러가 발생한 경우나 일시적인 경우도 해당한다.&lt;/p&gt;
&lt;h3 id=&quot;503-service-unavailable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#503-service-unavailable&quot; aria-label=&quot;503 service unavailable permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;503 Service Unavailable&lt;/h3&gt;
&lt;p&gt;일시적으로 서버가 과부하 상태이거나 점검중이기 때문에 현재 요청을 처리할 수 없으을 나타낸다. 이 상태가 해소되기까지 시간이 걸리는 경우에는 &lt;code class=&quot;language-text&quot;&gt;Retry-After&lt;/code&gt; 헤더 필드에 따라 클라리언트에 전달하는 것이 바람직하다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 상태 코드가 현재 상황과 불일치할 수도 있다
응답으로 되돌아오는 상태코드의 대부분은 유저가 다른 내용을 알기 어렵게 되엉씨다. 흔히 있는 상황으로, 웹 애플리케이션에서 애플리케이션 에러가 발생한 겨웅에도 상태 코드로 200 OK 가 되돌아오는 경우가 있다.&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[Git 브랜치 전략 (Git Flow, Github Flow)]]></title><description><![CDATA[현재 우리 하모니 팀은 PR, 브랜치 전략 등에 대해 논의중이다. 이와 관련하여 가장 핵심이 되는 것이 Git Flow 브랜치 전략이기 때문에, 이를 내 생각과 함께 학습하고 정리해보고자 한다. 학습한 내용들을 글쓰기로 인출(Output…]]></description><link>https://haon.site/github/git-flow/</link><guid isPermaLink="false">https://haon.site/github/git-flow/</guid><pubDate>Fri, 02 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;현재 우리 하모니 팀은 PR, 브랜치 전략 등에 대해 논의중이다. 이와 관련하여 가장 핵심이 되는 것이 Git Flow 브랜치 전략이기 때문에, 이를 내 생각과 함께 학습하고 정리해보고자 한다. 학습한 내용들을 글쓰기로 인출(Output) 하며 메타인지 활성화하고, 장기기억화에 큰 도움이 되고자 작성한다.&lt;/p&gt;
&lt;h2 id=&quot;브랜치는-왜-필요한가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%8A%94-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%9C%EA%B0%80&quot; aria-label=&quot;브랜치는 왜 필요한가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;브랜치는 왜 필요한가?&lt;/h2&gt;
&lt;p&gt;Github 를 활용하여 작업을 진행하다보면 항상 독립적인 브랜치를 생성하고, 작업을 수행한뒤 PR 을 날려 팀원들의 검토를 받고 머지를 한다. 이 일련의 과정은 관례적으로 사용되는 브랜치 관리 전략인 Git Flow 전략 내에서 발생하는 흐름이다. 왜 우리는 브랜치를 분기하고, 작업을 진행할까? 디폴트 핵심 브랜치인 main 브랜치에서 여러명이 동시에 작업을 진행하고 커밋을 푸시하면 안될까?&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/eddd58dfab447975b1e9c65c6b7cede6/c1bea/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.6441717791411%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAuklEQVR42s3T0QqCMBQGYN//IYKeoOsueoLwIirBENlcriyKlqnl3P6soRFE1OyiczPO2L6zw2EOfhzO621tlswD9qMmA8k2GCZuB7CIgNO0BaManAtiTmhtAV7WQB62IC92oFnSAZQCOLr1ZXVPZ4KC5Vsb8IHqcwykE4TZCl7KbIdiQinzssAfoz/o4SBSqKqyB5u2GFti4QdPRTqBnHMQ8n66X4FxHP85eGuZUtodbEJKibIsP/rLV5MlAeuCa5BwAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/eddd58dfab447975b1e9c65c6b7cede6/a6d36/image.png&quot;
        srcset=&quot;/static/eddd58dfab447975b1e9c65c6b7cede6/222b7/image.png 163w,
/static/eddd58dfab447975b1e9c65c6b7cede6/ff46a/image.png 325w,
/static/eddd58dfab447975b1e9c65c6b7cede6/a6d36/image.png 650w,
/static/eddd58dfab447975b1e9c65c6b7cede6/e548f/image.png 975w,
/static/eddd58dfab447975b1e9c65c6b7cede6/3c492/image.png 1300w,
/static/eddd58dfab447975b1e9c65c6b7cede6/c1bea/image.png 1388w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;main 브랜치는 이미 서비스가 출시되어 배포된 코드만을 모아두기 위한 브랜치이다. 만약 이 하나의 브랜치에 여러 사람들이 커밋을 푸시하면 어떻게될까? 이 하나의 브랜치에 여러 사람이 한꺼번에 작업을 진행하고 관리한다면, main 브랜치의 소스코드는 정말 불안정한 상태로 관리 될 것이다.&lt;/p&gt;
&lt;p&gt;다른 사람이 작업 또한 누군가 내 파일을 건들였을 경우 충돌이 발생할 가능성도 충분하다. 충돌을 만회하고자 뒤늦게 커밋을 롤백하자니, 내가 작업한 모든 내용을 삭제하고 처음부터 다시 작업을 진행해야 할 수도 있다. 브랜치에 작업한 내용을 PR 을 요청할 떄와 달리 내가 작업한 커밋이 충돌이 발생할지 체크할 방법도 존재하지 않기에, 커밋이 엉키고 꼬일 가능성이 충분하다.&lt;/p&gt;
&lt;p&gt;더 큰 규모의 조직에서 협업을 진행한다면 큰 문제가 발생할 것이다. 당연하게도 브랜치를 분기하지 않는다면, 각 사람이 main 브랜치로부터 독립적인 브랜치를 파생하여 동시간대에 작업을 진행하지 못하기에 작업 효율이 떨어진다. 오직 main 브랜치 하나에서 수 많은 개발자들에 협업한다면, 내가 작업중인 파일을 누군가 건드릴 수 있게된다. 이는 앞서 서술한 충돌 문제를 야기할 수 있다. 충돌을 만회할 수 있는 가장 안전한 방법은 다른 사람이 main 브랜치에 작업 내용을 푸시할 때 까지 대기하는 것인데, 작업 효율성이 매우 떨어지기에 좋은 방법이 아니다.&lt;/p&gt;
&lt;p&gt;또한 여러 기능을 개발하면서 남겨진 커밋 히스토리가 main 브랜치에 뒤죽박죽 섞여서 알아보기 힘든 로그가 쌓이게 된다. 엉켜있고 알아보기 힘든 커밋 히스토리가 쌓여있기에, 기획 변경으로 인해 특정 기능이 필요 없어졌을 때 또는 문제가 발생했을 떄 원하는 시점으로 롤백하기도 어려워진다. 반면 브랜치를 통해 관리했다면 원하는 시점을 가진 브랜치를 활용하여 작업 내용을 적절히 롤백시킬 수 있다.&lt;/p&gt;
&lt;p&gt;브랜치 기능을 사용하면 다른 브랜치에 영향을 받지 않는 독립적인 환경에서 기능을 개발할 수 있다. 또한 main 브랜치 하나에서 프로덕션 코드를 관리할 때와 달리, 프로덕션 코드의 안정성과 품질을 향상 시킬 수 있다. 독립적인 브랜치에 개발한 기능을 main 브랜치에 곧 바로 반영할 수 없기 떄문이다. main 브랜치에 내 코드를 기여하기 위해선, PR 을 먼저 요청해야한다. 팀 내 PR 가이드라인을 준수하여 팀원들의 리뷰와 검토를 받고, CI 를 통해 내 코드가 빌드에 성공했는지, 테스트 커버리지가 일정 수준 이상을 만족하여 프로덕션 코드에 반영해도 적절한지를 미리 검토할 수 있다. 또한 충돌이 발생하여 코드가 꼬일 가능성도 거의 0에 수렴한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;브랜치는-어떻게-관리하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;브랜치는 어떻게 관리하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;브랜치는 어떻게 관리하는가?&lt;/h2&gt;
&lt;p&gt;이렇게나 좋은 이점을 제공하는 브랜치 또한 일정한 규칙 없이 마구잡아로 사용한다면 엉키고 알아보기 힘들 수 밖에 없다. 브랜치 생성 및 관리에 대한 명확한 규칙이 없을 경우 &lt;strong&gt;&quot;이 브랜치는 어떤 기능을 개발하기 위해 생성된거지?, &quot;어떤 브랜치가 실제 프로덕션 및 배포 코드가 담긴 중요한 브랜치이지?&quot;, &quot;PR 은 어디에 요청해서 검토와 승인을 받아야히지?&quot;, &quot;이 커밋과 관련한 작업 내용이 담긴 브랜치가 어떤거였지?&quot;&lt;/strong&gt; 등 수 많은 혼동을 야기할 수 있다.&lt;/p&gt;
&lt;p&gt;이를 해결하고자 등장한 것이 Git 브랜치 전략이다. Git 브랜치 전략은 브랜치를 더 효과적으로 관리하기 위한 규칙이자 컨벤션이다. 팀 내 브랜치 규칙을 직접 만들어도 괜찮지만, 전셰게의 수 많은 팀 내에서 관례적으로 사용하고 있는 워크플로우인만큼 꼭 알아둘 필요가 있다. 나는 Git Flow, Github Flow 전략에 대해 학습하도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;git-flow-전략&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#git-flow-%EC%A0%84%EB%9E%B5&quot; aria-label=&quot;git flow 전략 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Git Flow 전략&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bb8f10d180b7cb56182e07efbccea453/f213e/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 119.01840490797547%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAYCAYAAAD6S912AAAACXBIWXMAAAsTAAALEwEAmpwYAAADuklEQVR42n1Va3eiSBDN//8v+ZCdM9mc3WQ8ZpzER+IDfIAogoAYefh+oXC3qhU3ZJ3tc/rQNN23qm7dKm7wZRwOByzmc7He7XY4Ho+IoghBECBJEvHOZ3hst1vEcZy5f/MZiC/s93t8fHyIted5WC6X4huveW+9XmO1Wok7szAUxq4COo6D+dkztpoksQCIaaZ7lyfv09OaeMJLNsDPC+BsNkOjURcb7CF7FMcJdtspFouA3o+YTqfiHIe+Iq8XqzWe2iparRYKhQJ6vd6/gAzAG+wRh8CgRBUs4w0DXSbre4RhIEA98krwvFhgRu9MiWmal+gEIAMNh0P4vg/TMFEulzEeTzAc1BF4ljg4mUxQKpWgdbuCR/aUDXwdApAzV6lUkM/nMRgMUHmrQO324Awl+BOTPD7AoP37799RLBbhE9hms0F4BjxxfuL6Jl30+31YtgW9oaArtdGkqXZ+weg38fJSRL1ew2g0glytYagPxJ05cfofD1NA5pAz2i+10C3JcHUbs7kNVZEoYRJaTRlHlhKB2YqCCXH6q9kWobOhkCSUAVQVFRHrrTeC8aZgboekRw1yoyxCZr44GdFZr2uSSXlgotNuI5f7gS5xmwFUyGpE2d1EOxiyhnHPIQ9dzKfeRU7MFXPHFcT3VpTpKDpVFn/PALIF9oA9CcwxAtsnjf3EZDwAa5nlZFkWypRpVgSP6f9xyElhxavkqdJVMTANKB2J9BXggyRTkJ8gSzIeHh7QpjB5MH+/zbKqqkKHuecc7u7uUHx9pfeANMcN4Iif9SdRXqxHNsz3UsAUIwOo6zr2hwhKSYJjWqK0hkYdPU0iLhcoUfiGYeC9+i5AeXDD+C0gN4cjue5pDry+DUsz4YxUhP4IfOJFzosS45oPz55drZQU0HVdHKkJrHcbWG0dge5iR0mazRZE/hQF6QeSOBHhJ+duMzvX71VADpkzuVyvYKsDhEMftlVDSypStjuwKNtcw8wzdx2W2JTWaXNJ21tGNtE+wmK9hNsx4WsuoniNsevgOf+MwnNBJCOmPrnf7eG4Y9zXJKGOarUq+M0AdjodbPck6lcqp7EHfxpgSNIxaf7x7Rtub28zMuE5CkLYto1arSaeGUBN00RPO+4O1Fi3FNYS9fe/oPckPD7m8Off95fWn/5b0vrNZPkrqal1Hr43omTZsEkBhdajKC82uieuDzTnZ8CMsFMg3vg8hZQoKY6lnL7HJ6+4jrlLj+lH9tBoig509Sd1bSSfVgmyF0XIm20mXAb8B2OXIJNTBp7GAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/bb8f10d180b7cb56182e07efbccea453/a6d36/image-1.png&quot;
        srcset=&quot;/static/bb8f10d180b7cb56182e07efbccea453/222b7/image-1.png 163w,
/static/bb8f10d180b7cb56182e07efbccea453/ff46a/image-1.png 325w,
/static/bb8f10d180b7cb56182e07efbccea453/a6d36/image-1.png 650w,
/static/bb8f10d180b7cb56182e07efbccea453/e548f/image-1.png 975w,
/static/bb8f10d180b7cb56182e07efbccea453/f213e/image-1.png 1192w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Git Flow 는 총 5가지 종류의 브랜치로 관리하는 전략이다. 가장 중요한 &lt;strong&gt;디밸롭 서버, 프로덕션 서버 실제 코드가 담기고 병합되는 main 브랜치, develop 브랜치가 존재하며, 이 2개의 중요한 브랜치에 병합되기 이전에 각기 다른 목적으로 분기되어 작업을 진행하는 목적을 가진 보조 브랜치들인 feature 브랜치, release 브랜치, hotfix 브랜치&lt;/strong&gt; 가 존재한다.&lt;/p&gt;
&lt;p&gt;main 브랜치와 develop 브랜치는 개발 프로세스 전반에 걸쳐 가장 중요하게 관리되는 브랜치이자, 항상 삭제되지 않고 유지되는 브랜치이다. 이 두 브랜치에 코드를 기여하기 위해선 보조 브랜치를 통해 작업을 진행하고, PR 을 요청해서 승인을 받아야한다. 반면 보조 브랜치(feature, relaese, hotfix) 는 필요할 때 마다 동적으로 생성되는 브랜치로, 제 역할을 다하고 develop (또는 main) 브랜치에 코드를 기여한 뒤에 제 역할을 다했다면 삭제된다. 보조 브랜치 덕분에 팀이 병렬적으로 동시간대에 작업을 진행할 수 있게된다.&lt;/p&gt;
&lt;h3 id=&quot;main-브랜치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#main-%EB%B8%8C%EB%9E%9C%EC%B9%98&quot; aria-label=&quot;main 브랜치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;main 브랜치&lt;/h3&gt;
&lt;p&gt;실제 대중에 배포되어 운영되기 직전(또는 이미 운영되고 있는) 프로뎍션 코드들이 담겨있는 브랜치이다. main 브랜치는 develop 브랜치와 마찬가지로 프로젝트 시작부터 끝까지 사라지지 않고 관리되는 핵심 브랜치다. 실제 프로덕션 코드가 담긴 중요한 브랜치인만큼, 후술할 develop 브랜치의 디밸롭 환경에서 수 많은 테스트와 검증을 거친 뒤에서야 main 브랜치에 반영해야 한다.&lt;/p&gt;
&lt;h3 id=&quot;develop-브랜치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#develop-%EB%B8%8C%EB%9E%9C%EC%B9%98&quot; aria-label=&quot;develop 브랜치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;develop 브랜치&lt;/h3&gt;
&lt;p&gt;main 브랜치에 병합될 다음 버전 배포를 앞두고 있는 코드들을 담고 있는 핵심 브랜치다. 실제 프로덕션 서버에서 돌아가는 코드들이 main 브랜치에서 관리된다면, 프로덕션 환경과 유사하게 구축한 디밸롭(테스트) 서버내의 코드들은 develop 브랜치에서 관리된다. 즉, develop 브랜치에 기여된 코드들은 테스트(디밸롭) 환경에서 많은 테스트 및 검증을 거친 뒤에야 출시해도 문제가 없다고 판단되었을 때 main 브랜치로 병합된다.&lt;/p&gt;
&lt;h3 id=&quot;feature-브랜치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#feature-%EB%B8%8C%EB%9E%9C%EC%B9%98&quot; aria-label=&quot;feature 브랜치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;feature 브랜치&lt;/h3&gt;
&lt;p&gt;하나의 기능을 개발하기 위한 브랜치이다. develop 브랜치로 부터 분기하여 생성하며, 해당 feature 브랜치에서 기능 개발이 완료되면 PR 요청을 날리고, 다시 develop 브랜치로 병합된다.&lt;/p&gt;
&lt;p&gt;머지시 주의할 점은 fast-forward 로 머지하지 않고, merge commit 을 생성하여 머지 해줘야한다. 그래야만 히스토리가 특징 기능 단위로 묶이게 된다.&lt;/p&gt;
&lt;h3 id=&quot;hotfix-브랜치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hotfix-%EB%B8%8C%EB%9E%9C%EC%B9%98&quot; aria-label=&quot;hotfix 브랜치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;hotfix 브랜치&lt;/h3&gt;
&lt;p&gt;이름 그대로 특정 문제가 발생했을 때, 해당 문제를 해결하기 위해 분기한 브랜치다. 정의상으로는 이미 배포된 프로덕션에 문제가 발생했을 때, 해당 문제를 해결하고자 분기하는 브랜치이다. 따라서 develop 브랜치가 아닌 main 브랜치에서 생성하고, 문제 해결이 완료되면 main 과 develop 브랜치 모두에 머지한다.&lt;/p&gt;
&lt;p&gt;다만, 정의상은 main 브랜치로부터 파생되는 것이 맞지만, develop 브랜치로 부터 파생되어도 전혀 상관없다는 생각이다. hotfix 브랜치가 등장한 이유는 문제를 해결하기 위함에 있는데,  실제 출시 단계가 아닌, 개발 단계에서 이미 문제를 발견했다면 develop 브랜치로부터 파생하고 문제를 해결하면 될 것이다.&lt;/p&gt;
&lt;h3 id=&quot;release-브랜치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#release-%EB%B8%8C%EB%9E%9C%EC%B9%98&quot; aria-label=&quot;release 브랜치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;release 브랜치&lt;/h3&gt;
&lt;p&gt;소프트웨어 배포를 준비하기 위한 브랜치이다. develop 브랜치로부터 분기하며, 버전 이름 등의 소소한 데이터를 수정하거나 배포전 사소한 버그를 수정하기 위해 사용된다. 배포 준비가 완료되었다면 main 과 develop 브랜치 모두에 머지한다. 이때, main 브랜치에는 태그를 이용하여 버전을 표시한다. Release 브랜치를 따로 운용함으로써, 배포 업무와 관련없는 팀원들은 병렬적으로 Feature 브랜치에서 이어서 기능을 개발할 수 있게된다. 추가적으로 네이밍은 &lt;code class=&quot;language-text&quot;&gt;release/v1.1&lt;/code&gt; 과 같은 형태로 생성한다.&lt;/p&gt;
&lt;p&gt;다만, release 브랜치는 팀 내 규모에 따라 협의하여 꼭 도입할지 검토해봐도 좋다고한다. 우리 하모니 팀의 경우도 release 브랜치는 도입할 근거가 마땅치 않으며, 불필요한 비용과 복잡성이 높아질 것으로 판단되어 도입하지 않았다.&lt;/p&gt;
&lt;h2 id=&quot;github-flow-전략&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#github-flow-%EC%A0%84%EB%9E%B5&quot; aria-label=&quot;github flow 전략 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Github Flow 전략&lt;/h2&gt;
&lt;p&gt;앞서 서술한 Git Flow 에 비해 Github Flow 는 더 단순해진 구조를 가진다. 다만, Github Flow 전략은 단순함으로 인해 개발이 편하다는 입장을 가진 사람들도 많지만, 내 생각은 다르다. 개인적인 생각으로는 Github Flow 는 매우 단순한 브랜치 구조로 관라하기 떄문에 브랜치 관리가 되려 힘들 것이라는 생각이다. 그러면 Github Flow 에 대해 학습해보자.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/984fd47a16f9bf2922fa69e299596458/5fada/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.78527607361963%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABp0lEQVR42nWSW2+bQBCF+f//pFL65D63UmInVWoqR8SFUC8sxmAMtgOsL1x9OrMJaRQpR1qxO5r5ZvawBl7VdR2EEHAcB2EYwrZfvpvNBuv1GlmWIUkSpGmql/Qk4ihGURQ4nU4653K5wBiAfGAoS/oSt+MJVquVjtV1ja5tUVc1mqaBUgo336/hOk9om1bXcpylgVVVMRHntsHmeMAzFTz8fYKiPQMZtjsoAriY3k8RUEMZRQiiFXaUo+tfZTA9pet01EnVFew0gUhizKSHfVmgVCWOVDT1PVzfjHE3uYVt/cHP+aPOm0ehrueB3iZkaN/3eg06qANiEeD8rFCsd8izJezpDP6ji2jpId8ng1l4L4MhbDKbzwa3dMUjmayKEqEjMJvcw7r7DSksJNTANeeICajKVA9Vk3eDj28TMpQDw6RsMMPdxQJ+IJHTPgwilNSE/2i+z7FNt7j0lN/1eg2DGvigoVMgA3wbjeCTd57wcfXlCt7Cw5GsGH0dYfxjjPPp/FLT/7/2p8DddgvTNLGkt5ilGcxfJsX2GmI9WBCu0M/oo43/ADodqf/VUnABAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/984fd47a16f9bf2922fa69e299596458/a6d36/image-2.png&quot;
        srcset=&quot;/static/984fd47a16f9bf2922fa69e299596458/222b7/image-2.png 163w,
/static/984fd47a16f9bf2922fa69e299596458/ff46a/image-2.png 325w,
/static/984fd47a16f9bf2922fa69e299596458/a6d36/image-2.png 650w,
/static/984fd47a16f9bf2922fa69e299596458/e548f/image-2.png 975w,
/static/984fd47a16f9bf2922fa69e299596458/3c492/image-2.png 1300w,
/static/984fd47a16f9bf2922fa69e299596458/5fada/image-2.png 1706w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Github Flow 는 위와 같이 &lt;code class=&quot;language-text&quot;&gt;main&lt;/code&gt; 브랜치와 &lt;code class=&quot;language-text&quot;&gt;feature&lt;/code&gt; 브랜치 두 종류가 끝이다. 단순히 기능 개발을 하고 master 브랜치에 병합하는 매우 단순한 구조다.&lt;/p&gt;
&lt;h3 id=&quot;main-브랜치-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#main-%EB%B8%8C%EB%9E%9C%EC%B9%98-1&quot; aria-label=&quot;main 브랜치 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;main 브랜치&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;항상 Stable한 상태여야 한다.&lt;/strong&gt; 이때, Stable하다는 것은 Main의 모든 커밋은 언제 배포하든 문제 없어야하고, 언제든 브랜치를 새로 만들어도 문제가 없어야 한다. main 브랜치의 모든 커밋은 빌드가 되고, 테스트를 통과해야한다. 이것이 Github Flow가 강제하는 유일한 사항이다.&lt;/p&gt;
&lt;h3 id=&quot;feature-브랜치-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#feature-%EB%B8%8C%EB%9E%9C%EC%B9%98-1&quot; aria-label=&quot;feature 브랜치 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;feature 브랜치&lt;/h3&gt;
&lt;p&gt;앞서 살펴봤던 git flow 전략의 feature 브랜치와 동일한 역할을 수행하며, 여기서 hotfix 브랜치의 역할 또한 합쳐진 브랜치라고 생각하면 된다. 즉, 기능 개발과 버그 수정 모두 구분없이 동반되어야 하는 브랜치다. 또한 앞선 Git Flow 전략과 달리 Github Flow 에선 main, develop 가 구분되어 있지 않다. 따라서 main 브랜치에 병합되기 이전의 코드들은 언제든 실제 프로덕션 코드에 문제없이 기여될 수 있도록 상시로 완벽하게 준비되어야 한다.&lt;/p&gt;
&lt;h2 id=&quot;gitflow-가-과연-적합할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#gitflow-%EA%B0%80-%EA%B3%BC%EC%97%B0-%EC%A0%81%ED%95%A9%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;gitflow 가 과연 적합할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GitFlow 가 과연 적합할까?&lt;/h2&gt;
&lt;p&gt;어떠한가? 과연 Github Flow 가 단순하다고 해서 좋은 전략일까? 몰론 매우 단순한 구조로 인해 깃허브를 갓 시작한 입문자들에게는 도움이 될 수 있겠다. 하지만 적어도 Git flow 전략처럼 프로덕션과 디밸롭 브랜치가 구분되어 있지도 않다. 또한 feature 브랜치가 버그 수정까지 모두 전담해야 하기에, 역할이 더 커진 구조이다.&lt;/p&gt;
&lt;p&gt;개발 팀이 매우 작은 소규모 애자일 팀이고, 제품을 정말 빠르게 출시해도 문제 없다면 Github Flow 를 채택해도 괜찮을 것이다. 하지만 많은 웹 애플리키이션은 테스트와 검토를 디밸롭 서버에서 충분히 검토해야 하기에 develop 브랜치의 필요성이 더 느껴진다. 또한 기능과 버그를 구분하지 않고 브랜치를 파생하고 관리하다보니, 관리가 다소 힘들어질 수 있다는 생각이다.&lt;/p&gt;
&lt;p&gt;Scott Chacon 의 의견을 인용하면서 글을 마친다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;장기간 프로젝트가 존재하고, 유지보수를 위한 작업을 수행해야 하는 팀은 Git Flow 가 타당한다. 반면 상시로 정말 빠르게 배포해야 하는 팀은 Github Flow 가 적합하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이 외에도 Gitlab Flow 라는 것을 카카오테크 교육과정에서 학습했는데, 아직은 제대로 학습하지 않도록 한다. Github Flow 는 Git Flow 와 Github Flow 의 중간 정도의 복잡성을 가진 전략이라고 한다. 지금 내 판단으로는 Git Flow 전략을 충분히 학습하고 프로젝트에 도입하는데 전혀 불편함을 느끼지 못했기 떄문에, 향후 Gitlab Flow 도입의 필요성이 느껴진다면 그때 글쓰기를 통해 깊게 학습해보도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://techblog.woowahan.com/2553/&quot;&gt;https://techblog.woowahan.com/2553/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@kw2577/Git-branch-%EC%A0%84%EB%9E%B5&quot;&gt;https://velog.io/@kw2577/Git-branch-전략&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.hwahae.co.kr/all/tech/9507&quot;&gt;https://blog.hwahae.co.kr/all/tech/9507&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devocean.sk.com/blog/techBoardDetail.do?ID=165571&amp;#x26;boardType=techBlog&quot;&gt;https://devocean.sk.com/blog/techBoardDetail.do?ID=165571&amp;#x26;boardType=techBlog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/git-branch-strategy/&quot;&gt;https://hudi.blog/git-branch-strategy/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://growth-coder.tistory.com/130&quot;&gt;https://growth-coder.tistory.com/130&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[제 3장. HTTP 정보는 HTTP 메시지에 있다]]></title><description><![CDATA[💡 본 포스트는 "그림으로 배우는 HTTP & Network Basic" 라는 책을 읽고 내 생각과 책 내용을 인용하여 작성했다. HTTP 메시지 HTTP 에서 교환하는 정보는 HTTP 메시지라고 불리는데, 요청 측 HTTP 메세지를 Request…]]></description><link>https://haon.site/network/http-basic/three/</link><guid isPermaLink="false">https://haon.site/network/http-basic/three/</guid><pubDate>Thu, 01 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 본 포스트는 &quot;그림으로 배우는 HTTP &amp;#x26; Network Basic&quot; 라는 책을 읽고 내 생각과 책 내용을 인용하여 작성했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;http-메시지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-%EB%A9%94%EC%8B%9C%EC%A7%80&quot; aria-label=&quot;http 메시지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP 메시지&lt;/h2&gt;
&lt;p&gt;HTTP 에서 교환하는 정보는 HTTP 메시지라고 불리는데, 요청 측 HTTP 메세지를 Request Message, 응답 측 HTTP 메시지를 Response Message 라고 부른다.&lt;/p&gt;
&lt;p&gt;HTTP 메시지는 복수 행의 데이터로 구성된 텍스트 문자열이다. HTTP 메세지는 크게 구분하면 메시지 헤더와 메시지 바디로 구성된다. 또한 최초에 나타내는 개행 문자로 메시지 헤더와 메시지 바디를 구분한다. 이 안에 메시지 바디가 항상 존재한다고는 할 수 없다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;메시지 헤더 : 서버와 클라이언트가 꼭 처리해야 하는 요청과 응답 내용과 속성 등&lt;/li&gt;
&lt;li&gt;개행 문자 : CR + LF&lt;/li&gt;
&lt;li&gt;메시지 바디 : 꼭 전송되는 데이터 그 자체&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;request-message-와-response-message-의-구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#request-message-%EC%99%80-response-message-%EC%9D%98-%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;request message 와 response message 의 구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Request Message 와 Response Message 의 구조&lt;/h2&gt;
&lt;p&gt;요청 메시지와 응답 메시지의 구조를 살펴보도록 하자. 요청 메시지와 응답 메시지의 헤더 내부는 아래와 같은 데이터로 구성되어 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Request Line : 요청에 사용하는 HTTP Method, Request URI, HTTP 버전등이 포함되어 있다.&lt;/li&gt;
&lt;li&gt;Status Line : 응답 결과를 나타내는 상태 코드와 설명, HTTP 버전이 포함된다.&lt;/li&gt;
&lt;li&gt;Header Field : 요청과 응답의 여러 조건과 속성 등을 나타내는 각종 헤더 필드가 포함된다. 일반 헤더 필드, 요청 헤더 필드, 응답 헤더 필드, 엔티티 헤더 필드 총 4가지의 종류가 있다.&lt;/li&gt;
&lt;li&gt;기타 : HTTP 의 RFC 에는 없는 헤더 필드(쿠기 등등) 가 포함되는 경우가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Request Message 예시&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;GET HTTP/1.1
Host: hackr.jp
User-Agent: Mozzlla/5.0 
Accept: tecx/html,application/xhtml+xml,application/xml;q=0.9,*/*
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Response Message 예시&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;HTTP/1.1 200 OK
Date: Fri, 13 Jul 2020 02:45:46 GMT
Server: Apache
Last-Modified: Fri, 31 Aug 2007 02:20:20 GMT
ETag: ~~
Accept_Ranges: ~~
Content-Length: 362
Connection: Close
Content-Type: text/html

&amp;lt;html&gt;
  // ...
&amp;lt;/html&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;인코딩으로-전송-효율을-높이다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EC%BD%94%EB%94%A9%EC%9C%BC%EB%A1%9C-%EC%A0%84%EC%86%A1-%ED%9A%A8%EC%9C%A8%EC%9D%84-%EB%86%92%EC%9D%B4%EB%8B%A4&quot; aria-label=&quot;인코딩으로 전송 효율을 높이다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인코딩으로 전송 효율을 높이다&lt;/h2&gt;
&lt;p&gt;HTTP 로 데이터를 전송할 경우 그대로 전송할 수도 있지만, 전송할 때에 인코딩(변환)을 실시함으로써 전송 효율을 높일 수 있다. 전송할 떄 인코딩을 하면 다량의 엑세스를 효율 좋게 처리할 수 있다. 단지, 컴퓨터에서 인코딩 처리를 해야하기 때문에 CPU 등의 리소스는 보다 많기 소비하게 된다.&lt;/p&gt;
&lt;h3 id=&quot;메시지-바디와-엔티티-바디의-차이&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%94%EC%8B%9C%EC%A7%80-%EB%B0%94%EB%94%94%EC%99%80-%EC%97%94%ED%8B%B0%ED%8B%B0-%EB%B0%94%EB%94%94%EC%9D%98-%EC%B0%A8%EC%9D%B4&quot; aria-label=&quot;메시지 바디와 엔티티 바디의 차이 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메시지 바디와 엔티티 바디의 차이&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;메시지(Message) : HTTP 통신의 기본 단위로 옥텟 시퀀스(8비트) 로 구성되고 통신을 통해서 전송된다.&lt;/li&gt;
&lt;li&gt;엔티티(Entity) : 요청과 응답의 페이로드(payload) 로 전송되는 정보로, 엔티티 헤더 필드와 엔티티 바디로 구성된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;HTTP 메시지 바디의 역할을 요청과 응답에 관한 엔티티 바디를 운반하는 일이다. 기본적으로 메시지 바디와 엔티티 바디는 같지만, 전송 코딩이 적용된 경우에는 엔티티 바디의 내용이 변화하기 떄문에 메시지 바디와 달라진다.&lt;/p&gt;
&lt;h3 id=&quot;압축해서-보내는-콘텐츠-코딩&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%95%EC%B6%95%ED%95%B4%EC%84%9C-%EB%B3%B4%EB%82%B4%EB%8A%94-%EC%BD%98%ED%85%90%EC%B8%A0-%EC%BD%94%EB%94%A9&quot; aria-label=&quot;압축해서 보내는 콘텐츠 코딩 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;압축해서 보내는 콘텐츠 코딩&lt;/h3&gt;
&lt;p&gt;메일에 파일을 첨부해서 보낼 경우 같이 용량을 줄이기 위해서 파일을 zip 으로 압축하고 나서 첨부해서 보내는 일이 있다. HTTP 에는 이와 같은 일이 가능한 &lt;code class=&quot;language-text&quot;&gt;콘텐츠 코딩(Contents Coding)&lt;/code&gt;이라고 불리는 기능이 구현되어 있다. 콘텐츠 코딩은 엔티티에 적용하는 인코딩을 가리키는데, 엔티티 정보를 유지한채로 압축한다. 콘텐츠 코딩된 엔티티는 수신한 클라이언트 측에서 디코딩한다.&lt;/p&gt;
&lt;h3 id=&quot;분해해서-보내는-청크-전송-코딩&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%84%ED%95%B4%ED%95%B4%EC%84%9C-%EB%B3%B4%EB%82%B4%EB%8A%94-%EC%B2%AD%ED%81%AC-%EC%A0%84%EC%86%A1-%EC%BD%94%EB%94%A9&quot; aria-label=&quot;분해해서 보내는 청크 전송 코딩 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;분해해서 보내는 청크 전송 코딩&lt;/h3&gt;
&lt;p&gt;HTTP 통신에서는 요청했었던 리소스 전부에서 엔티티 바디의 전송이 완료되지 않으면 브라우저에 표시되지 않는다. 사이즈가 큰 데이터를 전송하는 경우에 데이터(엔티티 바디)를 작게 쪼개고 나서 조금씩 송신할 수 있다. 또한 이때 쪼개진 각각의 작은 단위 하나를 &lt;code class=&quot;language-text&quot;&gt;청크&lt;/code&gt; 라고 부른다. 이렇게 엔티티 바디를 분할하는 기능을 &lt;code class=&quot;language-text&quot;&gt;청크 전송 코딩(Chunked Transfer Coding)&lt;/code&gt; 이라고 부른다.&lt;/p&gt;
&lt;h3 id=&quot;여러-데이터를-보내는-멀티파트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%AC%EB%9F%AC-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EB%B3%B4%EB%82%B4%EB%8A%94-%EB%A9%80%ED%8B%B0%ED%8C%8C%ED%8A%B8&quot; aria-label=&quot;여러 데이터를 보내는 멀티파트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;여러 데이터를 보내는 멀티파트&lt;/h3&gt;
&lt;p&gt;MIME은 메일로 텍스트, 영상, 이미지같은 다양한 데이터를 보내기 위해 사용되었다. 이것이 이메일에서 잘 작동하여 HTTP에서 여러 다른 종류 데이터를 한번에 보내는 방식에 채택되었고, 이는 멀티파트라고 불린다. HTTP 에서 멀티파트를 사용하면 메시지 바디 내부에 엔티티를 여러개 포함하여 보낼 수 있다. 이때, 이미지, 텍스트 파일 등을 실어 보낼 수 있다.&lt;/p&gt;
&lt;p&gt;멀티파트에는 다음과 같은 것들이 있다.&lt;/p&gt;
&lt;h4&gt;multipart/form-data&lt;/h4&gt;
&lt;p&gt;Web 폼으로부터 파일 업로드에 사용된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Content-Type: multipart/form-data; boundary=AaB03x

--AaB03x
Content-Deisposition: form-data; name=&quot;field1&quot;

Joe Blow
--AaB03x
Content-Disposition: form-data; name=&quot;pics&quot;; filename=&quot;fiel1.txt&quot;
Content-Type: text/plain
... (file1.txt데이터) ...
--AaB03x--&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;multipart/byteranges&lt;/h4&gt;
&lt;p&gt;상태 코드 206(Partial Content) 응답 메시지가 복수 범위의 내용을 포함하는 떄에 사용된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;HTTP/1.1 206 Partial Content
Date: Fri, 13 Jul 2012 02:45:26 GMT
Last-Modified: Fri, 31 Aug 2007 02:02:20 GMT
Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES

--THIS_STRING_SEPARATES
Content-Type: application/pdf
Content-Range: bytes 500-999/8000

...(지정한 범위의 데이터)...
--THIS_STRING_SEPARATES
Content-Type: application/pdf
Content-Range: bytes 7000-7999/8000
--THIS_STRING_SEPARATES--&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;HTTP 메시지로 멀티파트를 사용할 때는 Content-Type 헤더 필드를 사용한다. 그리고 멀티파트에서 각 엔티티를 구분하기 위해서 boundary 문자열을 사용한다. 각 엔티티 앞에는 -- 묹 뒤에 boundary 문자열을 붙인다. 그리고 멀티파트의 마지막은 -- 문자를 boundary 문자열 앞 뒤로 붙여 끝을 알린다.&lt;/p&gt;
&lt;h2 id=&quot;일부분만-받는-레인지-request&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%BC%EB%B6%80%EB%B6%84%EB%A7%8C-%EB%B0%9B%EB%8A%94-%EB%A0%88%EC%9D%B8%EC%A7%80-request&quot; aria-label=&quot;일부분만 받는 레인지 request permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;일부분만 받는 레인지 Request&lt;/h2&gt;
&lt;p&gt;옛날 시절에는 대용량의 이미지와 데이터를 다운로드하기 힘들었다. 다운로드 중에 커넥션이 끊어지면 처음부터 다시 다운로드를 해야했기 떄문이다. 이런 문제를 해결하기 위해 일반적인 &lt;code class=&quot;language-text&quot;&gt;제개(resume)&lt;/code&gt; 이라는 기능이 필요하게 되었다. 제개를 통해 이전에 다운로드를 한 곳에서부터 다운로드를 다시 시작할 수 있다.&lt;/p&gt;
&lt;p&gt;이 기능을 실현하기 위해선 엔티티의 범위를 지정해서 다운로드를 할 필요가 있다. 이와 같이 범위를 지정하여 요청하는 것을 &lt;code class=&quot;language-text&quot;&gt;레인지 요청(Range Request)&lt;/code&gt; 라고 부른다. 레인지 요청을 사용하면 전체 1만 바이트 정도 크기의 리소스에서 5,001 ~ 10,000 바이트 사이의 범위(바이트 레인지) 만을 요청할 수 있게된다.&lt;/p&gt;
&lt;p&gt;레인지 리퀘스트를 할 떄는 Range 헤더 필드를 사용해서 리소스의 바이트 레인지를 지정한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Range: bytes = 5001-10000&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;레인지 리퀘스트에 대한 응답은 상태 코드 &lt;code class=&quot;language-text&quot;&gt;206(Partial Content)&lt;/code&gt; 이라는 메시지가 되돌아온다. 또한, 복수 범위의 레인지 리퀘스트에 대한 응답은 &lt;code class=&quot;language-text&quot;&gt;multipart/byteranges&lt;/code&gt; 로 응답이 되돌아온다. 한편 서버가 레인지 리퀘스트에 지원하지 않는 경우라면 상태 코드 200 이 되돌아온다.&lt;/p&gt;
&lt;h2 id=&quot;최적의-콘텐츠를-돌려주는-컨텐츠-네고시에이션&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B5%9C%EC%A0%81%EC%9D%98-%EC%BD%98%ED%85%90%EC%B8%A0%EB%A5%BC-%EB%8F%8C%EB%A0%A4%EC%A3%BC%EB%8A%94-%EC%BB%A8%ED%85%90%EC%B8%A0-%EB%84%A4%EA%B3%A0%EC%8B%9C%EC%97%90%EC%9D%B4%EC%85%98&quot; aria-label=&quot;최적의 콘텐츠를 돌려주는 컨텐츠 네고시에이션 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;최적의 콘텐츠를 돌려주는 컨텐츠 네고시에이션&lt;/h2&gt;
&lt;p&gt;같은 컨텐트이지만 다르게 표현되어야 하는 페이지가 있을 것이다. 이를테면, 영어 페이지와 한국어 페이지를 따로 제공할 떄가 있을 것이다. 컨텐츠 네고시에이션이란 말 그대로 클라이언트에게 더 적합한 컨텐츠를 제공하기 위해 서버와 클라이언트가 리소스 내용에 대해 협상하는 것이다.&lt;/p&gt;
&lt;p&gt;컨텐츠 네고시에이션은 리소스를 언어, 문자 세트(charset), 인코딩 방식 등을 기준으로 판단한다. 판단 기준은 &lt;strong&gt;Accept, Accept-Charset, Accept-Encoding, Accept-Language, Content-Lanugage&lt;/strong&gt; 등의 요청 헤더 필드다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[제 2장. 간단한 프로토콜 HTTP]]></title><description><![CDATA[💡 본 포스트는 "그림으로 배우는 HTTP & Network Basic" 라는 책을 읽고 내 생각과 책 내용을 인용하여 작성했다. HTTP 는 클라이언트와 서버간에 통신을 한다 HTTP 를 사용하여…]]></description><link>https://haon.site/network/http-basic/two/</link><guid isPermaLink="false">https://haon.site/network/http-basic/two/</guid><pubDate>Wed, 31 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 본 포스트는 &quot;그림으로 배우는 HTTP &amp;#x26; Network Basic&quot; 라는 책을 읽고 내 생각과 책 내용을 인용하여 작성했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;http-는-클라이언트와-서버간에-통신을-한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-%EB%8A%94-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%EC%99%80-%EC%84%9C%EB%B2%84%EA%B0%84%EC%97%90-%ED%86%B5%EC%8B%A0%EC%9D%84-%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;http 는 클라이언트와 서버간에 통신을 한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP 는 클라이언트와 서버간에 통신을 한다&lt;/h2&gt;
&lt;p&gt;HTTP 를 사용하여 2대의 컴퓨터간에 통신을 하는 경우, 어느 한 쪽은 반드시 클라이언트가 되고, 반대는 서버가 된다.&lt;/p&gt;
&lt;h2 id=&quot;request-와-respons-를-교환하여-성립&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#request-%EC%99%80-respons-%EB%A5%BC-%EA%B5%90%ED%99%98%ED%95%98%EC%97%AC-%EC%84%B1%EB%A6%BD&quot; aria-label=&quot;request 와 respons 를 교환하여 성립 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Request 와 Respons 를 교환하여 성립&lt;/h2&gt;
&lt;p&gt;HTTP 는 클라이언트로부터 요청(Request) 이 송신되며, 그 결과가 서버로부터 응답(Response) 로 되돌아온다. 즉, 반드시 클라이언트 측으로부터 통신이 시작된다.
서버 측은 요청을 받지 않고서는 응답을 송신하는 일은 없다.&lt;/p&gt;
&lt;h3 id=&quot;http-request&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-request&quot; aria-label=&quot;http request permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP Request&lt;/h3&gt;
&lt;p&gt;HTTP 요청 내부의 메시지 구조는 어떻게 구성될까? 요청(Request) 메시지는 메소드, URI, 프르토콜 버전, 요청 헤더 필드와 엔티티로 구성된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;POST /form/entry HTTP/1.1
Host: hackr.ip
Connection: keep-alive
Content-Type: application/x-www-form-unlencoded
Content-Length: 16

name=msung99&amp;amp;age=20&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;메소드 : POST&lt;/li&gt;
&lt;li&gt;URI : /form/entry&lt;/li&gt;
&lt;li&gt;프로토콜 버전 : HTTP/1.1&lt;/li&gt;
&lt;li&gt;요청 헤더 필드 : Host, Connection. Content-Type, Content-Length 등장 부분&lt;/li&gt;
&lt;li&gt;엔티티 : name 부분&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;http-response&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-response&quot; aria-label=&quot;http response permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP Response&lt;/h3&gt;
&lt;p&gt;요청을 받은 서버는 요청 내용을 처리한 결과를 응답(Response) 로 클라이언트에게 되돌려준다. 응답 메시지는 보통 프로토콜 버전, 상태 코드 값, 해당 상태 코드를 설명한 구문, 응답 헤더와 응답 바디로 구성된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;HTTP/1.1 200 OK
Date: Tue, 10 2020 06:50:15 GMT
Content-Length: 362
Content-Type: text/html

&amp;lt;html&gt;
...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;HTTP 프로토콜 버전 : HTTP/1.1&lt;/li&gt;
&lt;li&gt;상태코드 : 200&lt;/li&gt;
&lt;li&gt;상태코드 설명 : OK&lt;/li&gt;
&lt;li&gt;응답 헤더 필드 : Date, Content-Length, Content-Type 부분&lt;/li&gt;
&lt;li&gt;응답 바디 : 헤더 필드와 한 줄의 공백을 두고 생긴 본문&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;http-는-상태를-유지하지-않는-프로토콜&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-%EB%8A%94-%EC%83%81%ED%83%9C%EB%A5%BC-%EC%9C%A0%EC%A7%80%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C&quot; aria-label=&quot;http 는 상태를 유지하지 않는 프로토콜 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP 는 상태를 유지하지 않는 프로토콜&lt;/h2&gt;
&lt;p&gt;HTTP 는 상태를 유지하지 않는 &lt;code class=&quot;language-text&quot;&gt;stateless&lt;/code&gt; 프로토콜이다. HTTP 프로토콜은 이전에 보냈던 요청이나 이미 되돌려준 응답에 대해서는 전혀 기억하지 않는다. 이는 많은 데이터를 빠르고 확실하게 처리하는 &lt;code class=&quot;language-text&quot;&gt;범위성(scalability)&lt;/code&gt; 를 확보하기 위해서 복잡한 기억장치 구조없이 간단하게 설계된 것이다.&lt;/p&gt;
&lt;p&gt;그러나 웹이 진화함에 따라, stateless 특성만으로는 처리하기 어려운 일이 증가하게 되었다. 예를들면 로그인했을 때, 다른 페이지로 이동했을 떄도 로그인 상태를 유지할 필요가 있다. 이를 위해서는 누가 어떤 요청을 보냈는지를 파악하기 위해 상태를 유지할 필요가 있다. HTTP/1.1 은 상태를 유지하지 않는 프로토콜이다. 그래서 상태를 유지하고 싶은 요구에 부응하기 위해서 &lt;code class=&quot;language-text&quot;&gt;쿠키(Cookie)&lt;/code&gt; 라는 기술이 도입되었다. 쿠키로 인해 HTTP 를 이용한 통신에서도 상태를 계속 관리할 수 있게 되었다.&lt;/p&gt;
&lt;h2 id=&quot;request-uri-로-리소스를-식별&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#request-uri-%EB%A1%9C-%EB%A6%AC%EC%86%8C%EC%8A%A4%EB%A5%BC-%EC%8B%9D%EB%B3%84&quot; aria-label=&quot;request uri 로 리소스를 식별 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Request URI 로 리소스를 식별&lt;/h2&gt;
&lt;p&gt;HTTP 는 URI 를 사용하여 인터넷 상의 리소스를 저장한다. 이 URI 가 있는 덕분에 인터넷 상의 어떤 장소에 있는 리소스도 호출할 수 있다. 클라이언트는 리소스를 호출할 떄 마다 요청을 송신할 때에 요청 내부에 URI 를 Request URI 라고 불리는 형식으로 포함해야 할 필요가 있다. Request URI 를 지정하는 방법에는 여러 종류가 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;모든 URI 를 Request URI 에 포함한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;GET http://hackr.jp/index.html HTTP/1.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Host 헤더 필드에 네트워크 로케이션을 포함한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;GET /index.htm HTTP/1.1
Host: hackr.ip&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이것 외에도 특정 리소스가 아닌 서버 자신에게 요청을 송신하는 경우에는 Request URI 에 [*] 를 지정할 수 있다. 아래는 HTTP 서버가 지원하고 있는 메소드를 묻는 예이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;OPTIONS * HTTP/1.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;서버에-임무를-부여하는-http-메소드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B2%84%EC%97%90-%EC%9E%84%EB%AC%B4%EB%A5%BC-%EB%B6%80%EC%97%AC%ED%95%98%EB%8A%94-http-%EB%A9%94%EC%86%8C%EB%93%9C&quot; aria-label=&quot;서버에 임무를 부여하는 http 메소드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서버에 임무를 부여하는 HTTP 메소드&lt;/h2&gt;
&lt;p&gt;HTTP/1.1 에서 사용할 수 있는 메소드에 대해 알아보자.&lt;/p&gt;
&lt;h3 id=&quot;get-메소드-리소스-획득&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#get-%EB%A9%94%EC%86%8C%EB%93%9C-%EB%A6%AC%EC%86%8C%EC%8A%A4-%ED%9A%8D%EB%93%9D&quot; aria-label=&quot;get 메소드 리소스 획득 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GET 메소드 (리소스 획득)&lt;/h3&gt;
&lt;p&gt;GET 메소드는 요청 URI 로 식별된 리소스를 가져올 수 있도록 요구한다. 가져올 리소스 내용은 지정된 리소스를 서버가 해석한 결과이다. 결국 리소스가 텍스트이면 그대로 반환하고, GGI 와 같은 프로그램이면 실행해서 출력된 내용을 돌려준다.&lt;/p&gt;
&lt;h3 id=&quot;post-메소드-엔티티-전송&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#post-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%97%94%ED%8B%B0%ED%8B%B0-%EC%A0%84%EC%86%A1&quot; aria-label=&quot;post 메소드 엔티티 전송 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;POST 메소드 (엔티티 전송)&lt;/h3&gt;
&lt;p&gt;POST 메소드는 엔티티 전송을 위해 사용된다. GET 으로 엔티티 전송은 가능하지만, 일반적으로 POST 를 사용한다. POST 는 GET 과 기능이 비슷하지만, Response 에 의한 엔티티를 획득하는 것 만이 목적은 아니다.&lt;/p&gt;
&lt;h3 id=&quot;put-메소드-파일-전송&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#put-%EB%A9%94%EC%86%8C%EB%93%9C-%ED%8C%8C%EC%9D%BC-%EC%A0%84%EC%86%A1&quot; aria-label=&quot;put 메소드 파일 전송 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PUT 메소드 (파일 전송)&lt;/h3&gt;
&lt;p&gt;PUT 메소드는 파일을 전송하기 위해서 사용된다. FTP 프로토콜을 통한 파일 업로드와 같이, Request 중에 포함된 엔티티를 Request URI 로 지정한 곳에 저장하도록 요구한다.&lt;/p&gt;
&lt;p&gt;파일을 전송하기 위해 사용된다. FTP 프로토콜을 통한 업로드와 같이 리퀘스트에 포함된 엔티티를 URI로 지정한 곳에 저장하도록 요구한다. 단, HTTP/1.1 PUT에는 인증 기능이 없어 누구나 파일을 업로드할 수 있게 되는 보안 문제가 발생한다. 따라서 일반적인 웹 사이트에서는 사용되지 않는다.&lt;/p&gt;
&lt;p&gt;REST와 같은 설계 양식에서 사용되는 경우가 있다.&lt;/p&gt;
&lt;h3 id=&quot;head-메소드-메시지-헤더-취득&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#head-%EB%A9%94%EC%86%8C%EB%93%9C-%EB%A9%94%EC%8B%9C%EC%A7%80-%ED%97%A4%EB%8D%94-%EC%B7%A8%EB%93%9D&quot; aria-label=&quot;head 메소드 메시지 헤더 취득 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HEAD 메소드 (메시지 헤더 취득)&lt;/h3&gt;
&lt;p&gt;HEAD 메소드는 GET 과 같은 기능이지만 Message Body 는 돌려주지 않는다. URI 유효성과 리소스 갱신 시간을 확인하는 목적 등으로 사용된다.&lt;/p&gt;
&lt;h3 id=&quot;delete-메소드-파일-삭제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#delete-%EB%A9%94%EC%86%8C%EB%93%9C-%ED%8C%8C%EC%9D%BC-%EC%82%AD%EC%A0%9C&quot; aria-label=&quot;delete 메소드 파일 삭제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DELETE 메소드 (파일 삭제)&lt;/h3&gt;
&lt;p&gt;DELETE 메소드는 파일을 삭제하기 위해 사용된다. PUT 메소드와는 반대로 동작하며, URI 로 지정된 리소스의 삭제를 요구한다. DELETE 자체에는 PUT 과 같이 인증 기능이 없기 떄문에 일반적인 웹 사이트에서는 사용되지 않는다.&lt;/p&gt;
&lt;p&gt;REST와 같은 설계 양식에서 사용되는 경우가 있다.&lt;/p&gt;
&lt;h3 id=&quot;options-메소드-제공하고-있는-메소드의-문의&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#options-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%A0%9C%EA%B3%B5%ED%95%98%EA%B3%A0-%EC%9E%88%EB%8A%94-%EB%A9%94%EC%86%8C%EB%93%9C%EC%9D%98-%EB%AC%B8%EC%9D%98&quot; aria-label=&quot;options 메소드 제공하고 있는 메소드의 문의 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OPTIONS 메소드 (제공하고 있는 메소드의 문의)&lt;/h3&gt;
&lt;p&gt;OPTIONS 매소드는 Request URI 로 지정한 리소스가 제공하고 있는 메소드를 조사하기 위해 사용된다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이 외에도 &lt;code class=&quot;language-text&quot;&gt;TREACE&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;CONNECT&lt;/code&gt; 메소드 등이 있지만, 향후 필요성을 느낄 때 동기 기반 학습을 진행하도록 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;메소드를-사용해서-지시를-내리다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%94%EC%86%8C%EB%93%9C%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-%EC%A7%80%EC%8B%9C%EB%A5%BC-%EB%82%B4%EB%A6%AC%EB%8B%A4&quot; aria-label=&quot;메소드를 사용해서 지시를 내리다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메소드를 사용해서 지시를 내리다&lt;/h2&gt;
&lt;p&gt;Request URI 로 지정한 리소스에 요청을 보내는 경우는 메소드라는 명령이 있다. 메소드는 리로스에 어떠한 행동을 하기 원하는지를 지시하기 위해 존재한다. HTTP/1.0 과 HTTP/1.1 에서 제공하고 있는 메소드 종류는 아래와 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GET : 리소스 취득&lt;/li&gt;
&lt;li&gt;POST : 엔티티 바디 전송&lt;/li&gt;
&lt;li&gt;PUT : 파일 전송&lt;/li&gt;
&lt;li&gt;HEAD : 메시지 헤더 취득&lt;/li&gt;
&lt;li&gt;DELETE : 파일 삭제&lt;/li&gt;
&lt;li&gt;OPTIONS : 서포트하고 있는 메소드 문의&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 외의 다양한 메소드들에 대해선 앞서 말한 이유와 동일하게, 필요성을 느낄 떄 학습하도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;지속-연결로-접속량을-절약&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%80%EC%86%8D-%EC%97%B0%EA%B2%B0%EB%A1%9C-%EC%A0%91%EC%86%8D%EB%9F%89%EC%9D%84-%EC%A0%88%EC%95%BD&quot; aria-label=&quot;지속 연결로 접속량을 절약 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;지속 연결로 접속량을 절약&lt;/h2&gt;
&lt;p&gt;HTTP 초기 버전에는 HTTP 통신을 한번 할 때마다 TCP 에 의해 연결과 종료를 할 필요가 있었다. 초기 당시의 통신에서는 작은 사이즈의 텍스트를 보내는 정도였기 떄문에, 한번 연결을 보낸 후 바로 연결을 종료해도 문제가 없었다. 그러나 HTTP 가 널리 보급되어감에 따라, 다량의 이미지를 포함한 문서가 늘어났다. 요청을 보낼 때 마다 매번 TCP 연결과 종료를 하게되는 과한 비용이 발생되어 통신량이 증가하게 되었다.&lt;/p&gt;
&lt;h3 id=&quot;지속-연결&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%80%EC%86%8D-%EC%97%B0%EA%B2%B0&quot; aria-label=&quot;지속 연결 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;지속 연결&lt;/h3&gt;
&lt;p&gt;HTTP/1.1 과 일부 HTTP/1.0 에서는 TCP 연결 문제를 해결하기 위해 &lt;code class=&quot;language-text&quot;&gt;지속 연결(Persistent Connections)&lt;/code&gt; 이라는 방법을 고안했다. 지속 연결의 특징은 어느 한 쪽이 명시적으로 연결을 종료하지 않는 이상 TCP 연결을 계속 유지한다. 지속 연결을 하는 이점은 TCP 커넥션 연결과 종료를 반복하는 오버헤드를 줄여주기 떄문에 서버에 대한 부하가 줄어든다는 것이다. 또한, 오버헤드를 줄인만큼 HTTP 요청과 응답이 빠르게 완료되기 떄문에 웹 페이지를 빨리 표시할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;파이프라인화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8%ED%99%94&quot; aria-label=&quot;파이프라인화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파이프라인화&lt;/h3&gt;
&lt;p&gt;지속 연결은 여러 요청을 보낼 수 있도록 &lt;code class=&quot;language-text&quot;&gt;파이프라인화(HTTP Pipelining)&lt;/code&gt; 를 가능하게 한다. 파이프라인화에 의해서, 이전에는 요청 송신 후에 응답을 수신할 떄까지 기다린 뒤에 요청을 발행하던 것을, 응답을 기다리지 않고 바로 다음 요청을 보낼 수 있다. 이로인해, 여러 요청을 병행해서 전송하는 것이 가능해졌다. 따라서 일일이 응답을 기다릴 필요가 없어졌다.&lt;/p&gt;
&lt;h2 id=&quot;쿠키를-사용한-상태관리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BF%A0%ED%82%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC&quot; aria-label=&quot;쿠키를 사용한 상태관리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쿠키를 사용한 상태관리&lt;/h2&gt;
&lt;p&gt;HTTP 는 Stateless 한 프로토콜이다. 따라서 과거에 교환했던 요청과 응답의 상태를 관리하지 않는다. 결국, 과거 상태를 근거로해서 요청을 처리한다는 것은 불가능하다.
예를들면, 인증이 필요한 웹 페이지에서 상태 관리를 하지 않는다면 인증을 마친 상태로 잊어버리기 때문에 새로운 페이지로 이동할 떄마다 재차 로그인 정보를 보내든지 요청마다 매개 변수가 추가 정보를 붙여서 로그인 상태를 관리해야한다.&lt;/p&gt;
&lt;p&gt;stateless 라는 특징이 제공해주는 이점이 많지만, 이와 같은 상태 관리가 필요한 상황을 해결하기 위해 쿠키가 드장했다. 쿠키는 요청과 응답에 쿠키 정보를 추가해서 클라이언트의 상태를 파악하기 위해 시스템이다. 쿠키는 서버에서 응답으로 보내진 &lt;code class=&quot;language-text&quot;&gt;Set-Cookie&lt;/code&gt; 라는 헤더 필드에 의해 쿠키를 클라이언트에 보존하게 된다. 다음번에 클라이언트가 같은 서버로 요청을 보낼 때, 자동으로 쿠키 값을 넣어서 송신한다. 서버는 클라이언트가 보내온 쿠키를 확인해서 어느 클라이언트가 접속했는지 체크하고 서버 상의 기록을 확인해서 이전 상태를 알 수 있다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[제 1장. 웹과 HTTP 는 이렇게 태어났고 성장했다.]]></title><description><![CDATA[💡 본 포스트는 "그림으로 배우는 HTTP & Network Basic" 라는 책을 읽고 내 생각과 책 내용을 인용하여 작성했다. 웹은 HTTP 로 나타낸다. 우리는 브라우저 주소 입력란에 지정된 URL…]]></description><link>https://haon.site/network/http-basic/one/</link><guid isPermaLink="false">https://haon.site/network/http-basic/one/</guid><pubDate>Mon, 29 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 본 포스트는 &quot;그림으로 배우는 HTTP &amp;#x26; Network Basic&quot; 라는 책을 읽고 내 생각과 책 내용을 인용하여 작성했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;웹은-http-로-나타낸다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%B9%EC%9D%80-http-%EB%A1%9C-%EB%82%98%ED%83%80%EB%82%B8%EB%8B%A4&quot; aria-label=&quot;웹은 http 로 나타낸다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;웹은 HTTP 로 나타낸다.&lt;/h2&gt;
&lt;p&gt;우리는 브라우저 주소 입력란에 지정된 URL 에 의지해서 웹 서버로부터 리소스라고 불리는 파일등의 정보를 얻고있다. 이때, 서버에 의뢰를 하는 웹 브라우저 등을 클라이언트라고 부른다.
이렇게 클라이언트에서 서버까지 일련의 흐름을 결정하고 있는 것은 웹에서 &lt;code class=&quot;language-text&quot;&gt;HTTP(Hypertext Transfer Protocol)&lt;/code&gt; 이라고 불리는 프로토콜이다.
프로토콜은이라는 의미는 &quot;약속&quot;이다. 즉, 웹은 HTTP 라는 약속을 사용한 통신으로 이루어져있다.&lt;/p&gt;
&lt;h2 id=&quot;http-는-이렇게-태어났고-성장했다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-%EB%8A%94-%EC%9D%B4%EB%A0%87%EA%B2%8C-%ED%83%9C%EC%96%B4%EB%82%AC%EA%B3%A0-%EC%84%B1%EC%9E%A5%ED%96%88%EB%8B%A4&quot; aria-label=&quot;http 는 이렇게 태어났고 성장했다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP 는 이렇게 태어났고 성장했다.&lt;/h2&gt;
&lt;h3 id=&quot;진보-안하는-http&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%84%EB%B3%B4-%EC%95%88%ED%95%98%EB%8A%94-http&quot; aria-label=&quot;진보 안하는 http permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;진보 안하는 HTTP&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;HTTP/0.9&lt;/code&gt; : HTTP 가 등장항 때는 1990년인데, 이 당시 HTTP 가 정식 사양서는 아니었다. 이 당시 등장한 HTTP 는 1.0 이전이라는 의미에서 HTTP/0.9 로 불리고 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;HTTP/1.0&lt;/code&gt; : HTTP 가 정식 사양으로 공개된 것은 1996년 6월이었다. 이떄 HTTP/1.0 으로 RFC1945 가 발행되었다. 초기의 사양이지만 현재에도 아직 많은 서버 상에서 현역으로 가동되고 있는 프로토콜 사양이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;HTTP/1.1&lt;/code&gt; : 1997년 1월에 공개된 HTTP/1.1 버전이 현재 가장 많이 사용되는 버전이다. 그 당시의 사양은 RFC2068 이지만 개정판으로 발행된 RFC2616 이 최신 버전이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;웹 문서 전송 프로토콜로써 등장한 HTTP 는 거의 버전이 업그레이드 되지 않았다. 현재 차세대를 담당할 HTTP/2.0 이 책정되어 있지만 널리 사용되기 까지는 아직 시간이 걸릴 것이다.
HTTP 는 등장한 당시에 주로 텍스트를 전송하기 위한 프로토콜이었지만, 프로토콜 자체가 상당히 심플해서 여러가지 응용 방법을 고려해 기능이 계속해서 추가되었다. 지금은 웹이라는 틀을 넘어서 다양하게 사용되는 프로토콜이 되었다.&lt;/p&gt;
&lt;h2 id=&quot;네트워크의-기본은-tcpip&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%EC%9D%98-%EA%B8%B0%EB%B3%B8%EC%9D%80-tcpip&quot; aria-label=&quot;네트워크의 기본은 tcpip permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;네트워크의 기본은 TCP/IP&lt;/h2&gt;
&lt;p&gt;인터넷을 포함하여 일반적으로 사용하고 있는 네트워크는 TCP/IP 라는 프로토콜에서 움직이고 있다. HTTP 는 그 중 하나이다.&lt;/p&gt;
&lt;h3 id=&quot;tcpip-는-프로토콜의-집합&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tcpip-%EB%8A%94-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%9D%98-%EC%A7%91%ED%95%A9&quot; aria-label=&quot;tcpip 는 프로토콜의 집합 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TCP/IP 는 프로토콜의 집합&lt;/h3&gt;
&lt;p&gt;컴퓨터와 네트워크 기기가 상호간에 통신하기 위해서는 서로 같은 방법으로 통신하지 않으면 안된다. 예를들면, 어떻게 상대를 찾고, 어떻게 상대에게 이야기를 시작하고, 어떠한 언어로 이야기를 하며, 어떻게 이야기를 종료할까와 같은 규칙을 결정할 필요가 있다. 이렇게 서로 다른 하드웨어와 운영체제 등을 가지고 통신하기 위해선 모든 요소에 규칙이 필요하다. 이러한 규칙을 프로토콜이라고 한다.&lt;/p&gt;
&lt;p&gt;프로토콜에는 여러가지가 있다. 케이블 규격이랑 IP 주소 지정방법, 떨어진 상대를 찾기 위한 방법과 그 곳에 도달하는 순서, 그리고 웹을 표시하기 위한 순서 등이다.
이렇게 인터넷과 관련된 프로토콜들을 모은 것을 TCP/IP 라고 부른다. TCP 와 IP 프로토콜을 가리켜 TCP/IP 라고 부르기도 하지만, IP 프로토콜을 사용한 통신에서 사용되고 있는 프로토콜을 총칭해서 TCP/IP 라는 이름이 사용되고 있다.&lt;/p&gt;
&lt;h3 id=&quot;계층으로-관리하는-tcpip&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%84%EC%B8%B5%EC%9C%BC%EB%A1%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94-tcpip&quot; aria-label=&quot;계층으로 관리하는 tcpip permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;계층으로 관리하는 TCP/IP&lt;/h3&gt;
&lt;p&gt;TCP/IP 는 애플리케이션 계층, 트랜스포트 계층, 네트워크 계층, 링크 계층 이렇게 4계층으로 나뉘어 있다.&lt;/p&gt;
&lt;p&gt;TCP/IP 가 계층화된 것은 메리트가 있다. 예를들면, 인터넷이 하나의 프로토콜로 되어 있다면 어디선가 사양이 변경되었을 때 전체를 바꾸지 않으면 안되지만, 계층화되어 있으면 사양이 변경된 해당 계층만 바꾸면 된다. 각 계층은 계층이 연결되어 있는 부분만 결정되어 있어서, 각 계층의 내부는 자유롭게 설정할 수 있다.&lt;/p&gt;
&lt;p&gt;또한 계층화를 하면 설계가 편하다. 애플리케이션 계층에서 애플리케이션은 자기 자신이 담당하는 부분을 고려하면 되고, 상대가 어디에 있는지, 어떠한 루트로 메시지를 전달하는지, 전달한 메시지가 확실하게 전달되고 있는지 등을 고려하지 않아도 된다.&lt;/p&gt;
&lt;h4&gt;애플리케이션 계층&lt;/h4&gt;
&lt;p&gt;유저에게 제공되는 애플리케이션에서 사용하는 통신의 움직임을 결정하고 있다. TCP/IP 에는 여러가지의 공통 애플리케이션이 준비되어 있다. 예를들면 FTP 랑 DNS 등도 애플리케이션의 한 가지이다. HTTP 도 이 계층에 포함된다.&lt;/p&gt;
&lt;h4&gt;트랜스포트 계층&lt;/h4&gt;
&lt;p&gt;트랜스포트 계층은 애플리케이션 계층에 네트워크로 접속되어 있는 2대의 컴퓨터 사이의 데이터 흐름을 제공한다. 트랜스포트 계층에서는 서로 다른 성질을 가진 TCP 와 UDP 2가지 프로토콜이 있다.&lt;/p&gt;
&lt;h4&gt;네트워크 계층 (or 인터넷 계층)&lt;/h4&gt;
&lt;p&gt;네트워크 계층은 네트워크 상에서 패킷의 이동을 다룬다. 패킷이란 전송하는 데이터의 최소 단위이다. 이 계층에서는 어떠한 경로(이른바 절차) 를 거쳐 상대의 컴퓨터까지 패킷을 보낼지를 결정하기도 한다. 인터넷의 경우라면 상대 컴퓨터에 도달하는 동안에 여러대의 컴퓨터랑 네트워크 기기를 거쳐서 상대방에게 배송된다. 그러한 여러가지 선택지 중에서 하나의 길을 결정하는 것이 네트워크 계층의 역할이다.&lt;/p&gt;
&lt;h4&gt;링크 계층 (or 데이터 링크 계층)&lt;/h4&gt;
&lt;p&gt;네트워크에 접속하는 하드웨어적인 면을 다룬다. 운영체제가 하드웨어를 제어하기 때문에 디바이스 드라이버랑 네트워크 인터페이스 카드(NIC) 를 포함한다. 그리고 케이블 등과 같이 물리적으로 보이는 부분(커넥트 등을 초함한 여러가지 전송 매체) 도 포함한다. 하드웨어직 측면은 모두 링크 계층의 역할이다.&lt;/p&gt;
&lt;h3 id=&quot;tcpip-통신의-흐름&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tcpip-%ED%86%B5%EC%8B%A0%EC%9D%98-%ED%9D%90%EB%A6%84&quot; aria-label=&quot;tcpip 통신의 흐름 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TCP/IP 통신의 흐름&lt;/h3&gt;
&lt;p&gt;TCP/IP 로 통신을 할때 순서대로 거쳐 상대와 통신한다. 송신하는 측은 애플리케이션 계층에서부터 내려가고, 수신하는 측은 애플리케이션 계층으로 올라간다.&lt;/p&gt;
&lt;p&gt;HTTP 를 예로들면, 먼저 송신측 클라이언트의 애플리케이션 계층(HTTP) 에서 어느 웹 페이지를 보고 싶다라는 HTTP 요청을 지시한다. 그 다음에 있는 트랜스포트 계층(TCP) 에선 애플리케이션 계층에서 받은 데이터(HTTP 메시지) 를 통신하기 쉽게 조각내어 안내 번호와 포트 번호를 붙여 네트워크 계층에 전달한다. 네트워크 계층(IP) 에서는 수신지 MAC 주소를 추가해서 링크 계층에 전달한다. 이로써 네트워크를 통해 송신할 준비가 되었다.&lt;/p&gt;
&lt;p&gt;수신측 서버는 링크 계층에서 데이터를 받아들여 순서대로 위의 계층에 전달하여 애플리케이션 계층까지 도달한다. 애플리케이션 계층에 도달하게 되면 드디어 클라이언트가 발신했던 HTTP 요청 내용을 수신할 수 있다.&lt;/p&gt;
&lt;p&gt;각 계층으 거칠때는 반드시 헤더로 불려지는 해당 계층마다 해당 계층에 필요한 저보를 추가한다. 반대로 수신측에서는 각 계층을 거칠 대마다 반드시 해당 계층마다 사용한 헤더를 삭제한다. 이렇게 정보를 감싸는 것을 캡슐화라고 부른다.&lt;/p&gt;
&lt;h2 id=&quot;http-와-관계가-깊은-프로토콜은-iptcpdns&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-%EC%99%80-%EA%B4%80%EA%B3%84%EA%B0%80-%EA%B9%8A%EC%9D%80-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%9D%80-iptcpdns&quot; aria-label=&quot;http 와 관계가 깊은 프로토콜은 iptcpdns permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP 와 관계가 깊은 프로토콜은 IP/TCP/DNS&lt;/h2&gt;
&lt;p&gt;TCP/IP 중에서 HTTP 와 관계가 깊은 IP, TCP, DNS 3개의 프로토콜에 대해 알아보자.&lt;/p&gt;
&lt;h3 id=&quot;배송을-담당하는-ip&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%EC%86%A1%EC%9D%84-%EB%8B%B4%EB%8B%B9%ED%95%98%EB%8A%94-ip&quot; aria-label=&quot;배송을 담당하는 ip permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배송을 담당하는 IP&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;IP(Internet Protocol)&lt;/code&gt; 은 계층으로 말하자면 네트워크 계층에 해당된다. 실제 이름 그래도 인터넷을 활용하는 대부분의 시스템의 IP 를 활용하고 있다. IP 와 IP 주소를 혼동하는 사람이 있는데, IP 는 프로토콜의 명칭이다.&lt;/p&gt;
&lt;p&gt;IP 의 역할은 개개의 패킷을 상대방에 전달하는 것이다. 상대방에게 전달하기까지 여러가지 요소가 필요하다. 그 중에서도 IP 주소와 MAC 주소가 중요하다. IP 주소는 각 노드에 부여된 주소를 가리키고, MAC 주소는 각 네트워크 카드에 할당된 고유의 주소다. IP 주소는 MAC 주소와 결부된다. IP 주소는 변경 가능하지만, 기본적으로 MAC 주소는 변경할 수 없다.&lt;/p&gt;
&lt;h4&gt;통신은 ARP 를 이용하여 MAC 주소에서 한다&lt;/h4&gt;
&lt;p&gt;IP 통신은 MAC 주소에 의존해서 통신한다. 인터넷에서 통신 상대가 같은 랜선 내에 있을 경우는 적어서 여러대의 컴퓨터와 네트워크 기기를 중계해서 상대방에게 도착한다. 그렇게 중계하는 동안에는 다음으로 중계할 곳의 MAC 주소를 사용하여 목적지를 찾아가는 것이다. 이떄, ARP 이라는 프로토콜이 사용된다.&lt;/p&gt;
&lt;p&gt;ARP 는 주소를 해결하기 위한 프로토콜 중 하나이다. 수신지의 IP 주소를 바탕으로 MAC 주소를 조사할 수 있다.&lt;/p&gt;
&lt;h4&gt;그 누구도 인터넷 전체를 파악하고 있지는 않다&lt;/h4&gt;
&lt;p&gt;목적지까지 중계를 하는 도중에 컴퓨터와 라우터 등의 네트워크 기기는 목적지에 도달하기까지 대략적인 목적지만을 알고있다. 이 시스템을 라우팅이라고 부르는데, 택배 배송과 흡사하다. 화물을 보내는 사람은 택배 집배소 등에 화물을 갖고 가면 택배를 보낼 수 있는 것만 알고 있으며, 집배소는 화물을 보내는 것을 보고 어느 지역의 집배소에 보내면 되는지만 알고있다. 그리고 목적지에 있는 집배소는 어느 집에 배달하면 되는지만 알고있다. 결국, 어떤 컴퓨터나 네트워크 기기도 인터넷 전체를 상세하게 파악하고 있지는 못하다는 것이다.&lt;/p&gt;
&lt;h3 id=&quot;신뢰성을-담당하는-tcp&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A0%EB%A2%B0%EC%84%B1%EC%9D%84-%EB%8B%B4%EB%8B%B9%ED%95%98%EB%8A%94-tcp&quot; aria-label=&quot;신뢰성을 담당하는 tcp permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;신뢰성을 담당하는 TCP&lt;/h3&gt;
&lt;p&gt;TCP 는 계층으로 말하자면 트랜스포트 층에 해당하는데, 신뢰성 있는 바이트 스트림 서비스를 제공한다. 바이트 스트림 서비스란 용량이 큰 데이터를 보내기 쉽게 TCP 세그먼트라고 불리는 단위 패킷으로 작게 분해하여 관리하는 것을 말하고, 신뢰성 있는 서비스는 상대방에게 보내는 서비스를 말한다. 결국 TCP 는 대용량의 데이터를 보내기 위해 쉽게 작게 본해하여 상대방에게 보내고, 정확하게 도착했는지 확인하는 역할을 담당하고 있다.&lt;/p&gt;
&lt;h4&gt;상대에게 데이터를 확실하게 보내는 것이 일이다&lt;/h4&gt;
&lt;p&gt;상대에게 확실하게 데이터를 보내기 위해서 TCP 는 3-way 핸드쉐이킹 방법을 사용한다. 이 방법은 패킷을 보내고나서 바로 끝내는 것이 아니라, 보내졌는지 여부를 상대에게 확인하러 간다. 이것은 SYN 와 ACK 이라는 TCP 플래그를 사용한다. 송신측에서는 최초 SYN 플래그로 상대에게 접속함과 동시에 패킷을 보내고, 수신측에서는 SYN/ACK 플래그로 상대에게 접속함과 동시에 패킷을 수신한 사실을 전한다. 마지막으로 송신측이 ACK 플래그를 보내 패킷 교환이 완료되었으을 전한다.&lt;/p&gt;
&lt;p&gt;이 과정에서 어디선가 통신이 끊어지면 TCP 는 그와 동시에 같은 수순으로 패킷을 재전송한다.&lt;/p&gt;
&lt;p&gt;TCP 는 3-way 핸드쉐이킹 외에도 통신의 신뢰성을 보증하기 위해 다양한 시스템을 갖추고 있다.&lt;/p&gt;
&lt;h2 id=&quot;이름-해결을-담당하는-dns&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%A6%84-%ED%95%B4%EA%B2%B0%EC%9D%84-%EB%8B%B4%EB%8B%B9%ED%95%98%EB%8A%94-dns&quot; aria-label=&quot;이름 해결을 담당하는 dns permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이름 해결을 담당하는 DNS&lt;/h2&gt;
&lt;p&gt;DNS 는 HTTP 와 같이 응용 계층 시스템에서 도메인 이름과 IP 주소 이름 확인을 제공한다. 컴퓨터는 IP 주소와는 별도로 호스트 이름과 도메인 이름을 붙일 수 있다.&lt;/p&gt;
&lt;p&gt;DNS 는 도메인명에서 IP 주소를 조사하거나, 반대로 IP 주소로부터 도메인명을 조사하는 서비스를 제공하고 있다.&lt;/p&gt;
&lt;h2 id=&quot;각각과-http-와의-관계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%81%EA%B0%81%EA%B3%BC-http-%EC%99%80%EC%9D%98-%EA%B4%80%EA%B3%84&quot; aria-label=&quot;각각과 http 와의 관계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;각각과 HTTP 와의 관계&lt;/h2&gt;
&lt;p&gt;IP, TCP, DNS 가 HTTP 를 이용해 통신을 할 때, 어떤 역할을 하는지 알아보자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TCP 담당 : 통신하기 쉽도록 HTTP 메시지를 패킷으로 분해 및 상대방에게 전송&lt;/li&gt;
&lt;li&gt;IP 담당 : 상대가 어디에 있는지 찾아 중계해 가면서 배송&lt;/li&gt;
&lt;li&gt;TCP 담당 : 상대방으로부터 패킷을 수신&lt;/li&gt;
&lt;li&gt;HTTP 담당 : 웹 서버에 대한 요청 내용을 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;uri-와-url&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#uri-%EC%99%80-url&quot; aria-label=&quot;uri 와 url permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;URI 와 URL&lt;/h2&gt;
&lt;p&gt;웹 브라우저 등으로 웹 페이지를 표시하기 위해 입력하는 주소가 바로 URL 이다. 예를들어 &lt;a href=&quot;https://www.google.co.kr&quot;&gt;https://www.google.co.kr&lt;/a&gt; 이 URL 이다.&lt;/p&gt;
&lt;h3 id=&quot;uri-는-라소스-식별자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#uri-%EB%8A%94-%EB%9D%BC%EC%86%8C%EC%8A%A4-%EC%8B%9D%EB%B3%84%EC%9E%90&quot; aria-label=&quot;uri 는 라소스 식별자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;URI 는 라소스 식별자&lt;/h3&gt;
&lt;p&gt;URI 는 Uniform Resource Identifiers 의 약자이지만, RFC2396 에서는 각각의 단어가 다음과 같이 정의되어 있다.&lt;/p&gt;
&lt;h4&gt;Uniform&lt;/h4&gt;
&lt;p&gt;통일된 서식을 결정하는 것으로, 여러가지 종류의 리소스 지정 방법을 같은 맥락에서 구별없이 취급할 수 있게 한다. 또한, 새로운 스키마(http: 와 ftp 등) 도입을 용이하게 한다.&lt;/p&gt;
&lt;h4&gt;Resource&lt;/h4&gt;
&lt;p&gt;리소스는 &quot;식별 가능한 모든 것&quot; 이라고 정의되어 있다. 도큐먼트 파일뿐만 아니라 이미지와 서비스(예를들면, 오늘의 일기 예보) 등 다른것과 구별할 수 있는 것은 모두 리소스이다. 또한 리소스는 단일한 부분만 아니라 복수의 집합도 리소스로 파악할 수 있다.&lt;/p&gt;
&lt;h4&gt;Identifier&lt;/h4&gt;
&lt;p&gt;식별 가능한 것을 참조하는 오브젝트이며 식별자로 불린다. 결국, URI 는 스키마를 나타내는 리소스를 식별하기 위한 식별자다. 스키마는 리소스를 얻기위한 수단에 이름을 붙이는 방법이다.&lt;/p&gt;
&lt;p&gt;HTTP 는 &quot;http&quot; 를 사용한다. 그 외에도 &quot;ftp&quot; 와 &quot;mailto&quot; 등이 있다.&lt;/p&gt;
&lt;p&gt;URI 는 리소스를 식별하기 위해 문자열 전반을 나타내는데 비해, URL 은 리소스의 장소(네트워크 상의 윛) 를 나타낸다. URL 은 URI 의 서브셋이다.&lt;/p&gt;
&lt;h3 id=&quot;url-포맷&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#url-%ED%8F%AC%EB%A7%B7&quot; aria-label=&quot;url 포맷 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;URL 포맷&lt;/h3&gt;
&lt;p&gt;URI 는 필요한 정보 전체를 지정하는 완전 수식 절대 URI 혹은 완전 수식 절대 URL 과 브라우저 중의 기준 URI 에서 상대적 위치를 지정하는 상대 URL 이 있다. 여기선 절대 URI 포맷을 살펴보도록 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;http://user:pass@www.example.jp:80/dir/index.htm?uid=1#ch1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;http : 스키마  /  user:pass : 자격정보(크리덴셜) / &lt;a href=&quot;http://www.example.jp&quot;&gt;www.example.jp&lt;/a&gt; : 서버주소 / 80 : 서버포트 / dir/index.htm : 계층적 파일 패스 /  uid=1 : 쿼리 스트링 / ch1 : fragment 식별자&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;자격정보(크리덴셜)&lt;/code&gt; : 서버로부터 리소스를 취득하려면 자격정보(크리덴셜) 가 필요하다. 유저명과 패스웨드를 지정할 수 있다. 이는 옵션이다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;서버 주소&lt;/code&gt; : 완전 수식 형식인 URI 에서는 서버 주소를 지정할 필요가 있다. 주소는 DNS 주소, IPv4 주소, IPv6 주소를 대고라호로 묶어서 지정한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;서버 포트&lt;/code&gt; : 서버의 접속 대상이 되는 네트워크 포트 번호를 지정한다. 이것은 옵션이고, 생략하면 디폴트 포트가 사용된다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;계층적 파일 패스&lt;/code&gt; : 특정 리소스를 식별하기 위해서 서버 상의 파일 패스를 지정한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;쿼리 스트링&lt;/code&gt; : 파일 패스로 지정된 리소스에 임의의 파라미터를 넘겨주기 위해 쿼리 스트링을 사용한다. 이 또한 옵션이다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;fragment 식별자&lt;/code&gt; : 주로 취득한 리소스에서 서브 리소스(도큐먼트 중간에 위치) 를 가리키기 위해서 사용된다. 이 또한 옵션이다.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[스프링에서 Argument Resolver 기반 커스텀 어노테이션으로 인증 책임을 분리해보자! (vs Interceptor)]]></title><description><![CDATA[현재 포스트는 harmony 팀 기술 블로그에 게시된 글 입니다. Argument Resolver 를 사용하지 않는 상황 현재 우리 팀의 프로젝트의 인증/인가 도메인을 맡아 개발하면서 ArgumentResolver…]]></description><link>https://haon.site/spring/argument-resolver/</link><guid isPermaLink="false">https://haon.site/spring/argument-resolver/</guid><pubDate>Sun, 28 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/argument-resolver/&quot;&gt;harmony 팀 기술 블로그&lt;/a&gt;에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;argument-resolver-를-사용하지-않는-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#argument-resolver-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EC%83%81%ED%99%A9&quot; aria-label=&quot;argument resolver 를 사용하지 않는 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Argument Resolver 를 사용하지 않는 상황&lt;/h2&gt;
&lt;p&gt;현재 우리 팀의 프로젝트의 인증/인가 도메인을 맡아 개발하면서 ArgumentResolver 에 기반한 커스텀 어노테이션을 개발했다. 이 어노테이션에 대한 구현의 이유(근거), 왜 사용되는지에 대한 생각의 정교화를 위해 글을 작성한다.&lt;/p&gt;
&lt;p&gt;백문이불어일타. 직접 코드를 작성해보면서 ArgumentResolver 에 대해 이해해보자.&lt;/p&gt;
&lt;p&gt;Argument Resolver 의 활용 유무는 프레젠테이션 계층에서 가장 차이를 보인다. 일반적인 상황이라면, 즉 Argument Resolver 에 기반한 커스텀 어노테이션이 존재하지 않는 경우라면 어떻게 클라이언트의 요청을 처리할까? 당연하게도 컨트롤러에서 &lt;code class=&quot;language-text&quot;&gt;@RequestBody&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@RequestParam&lt;/code&gt; 등 이미 스프링내에 정의된 편리한 어노테이션으로 가변적인 변수를 바인딩시킬 수 있을 것이다. 예를들어 아래처럼 사용자의 정보를 &lt;code class=&quot;language-text&quot;&gt;LoginMember&lt;/code&gt; 라는 객체로 바인딩한다고 해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/me&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;about&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestBody&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; accessToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JwtTokenProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseLong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jwtTokenProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPayload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accessToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;LoginMember&lt;/span&gt; loginMember &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoginMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ... (service 로직 수행)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 문제점이 하나있다. &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 사이에 있는 로직이 여러 컨트롤러에서 중복되는 로직이 라고 생각해보자. 가령 위와 같이 Jwt 의 Payload 에서 유저의 정보를 추출하는 로직은 대부분의 컨트롤러에서 구현해야하는 로직일 것이며, 모든 컨트롤러에서 직접 매번 구현하자니 중복 코드가 발생한다.&lt;/p&gt;
&lt;h3 id=&quot;중복-로직-제거-데이터-바인딩&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A4%91%EB%B3%B5-%EB%A1%9C%EC%A7%81-%EC%A0%9C%EA%B1%B0-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B0%94%EC%9D%B8%EB%94%A9&quot; aria-label=&quot;중복 로직 제거 데이터 바인딩 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;중복 로직 제거, 데이터 바인딩&lt;/h3&gt;
&lt;p&gt;우리는 프레젠테이션 계층, 즉 클라이언트의 요청을 처리하는 모든 컨트롤러의 중복 코드를 제거하고 싶다. 정확히는 HTTP Header, 세션, 쿠키 등 직접적이지 않은 방식으로 데이터를 바인딩하고, 중복 로직을 제거하고 싶다. 이때 사용하는 것이 바로 Argument Resolver 이다. &lt;strong&gt;Argument Resolver 를 사용하면 컨트롤러 메소드의 파라미터 중 특정 조건에 맞는 파라미터가 있다면, 요청에 들어온 값을 원하는 객체를 만들어 바인딩해줄 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;다시말해, 특정 요청으로 들어온 값을 원하는 객체로 생산해내는 작업을 Argument Resolver 에게 위임할 수 있다. 위의 경우 클라이언트 요청으로 전달받은 Dto 를 LoginMember 라는 객체로 바인딩시키는 것이다. 또한 이 작업의 책임을 제 3자인 Argument Resolver 구현체에게 인가함으로써 많은 컨트롤러의 중복 로직을 제거할 수 있게된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;argument-resolver-구헌체&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#argument-resolver-%EA%B5%AC%ED%97%8C%EC%B2%B4&quot; aria-label=&quot;argument resolver 구헌체 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Argument Resolver 구헌체&lt;/h2&gt;
&lt;p&gt;ArgumentResolver 의 혜택을 누리기 위해선 이에 대한 구현체가 핋요하다. 간단하게 앞선 특정 유저의 정보를 조회하는 API 를 가정하고. ArgumentResolver 를 적용해보자. 즉, 우리의 최종적인 목적은 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 사이의 로직을 제 3자인 Argument Resolver 기반 구현체에게 책임을 인기하여, &lt;code class=&quot;language-text&quot;&gt;LoginMember&lt;/code&gt; 객체를 자동 생성하는 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/me&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;about&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestBody&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; accessToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JwtTokenProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseLong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jwtTokenProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPayload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accessToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;LoginMember&lt;/span&gt; loginMember &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoginMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ... (service 로직 수행)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또한 UserRequest 라는 DTO 는 아래와 같이 정의되었음을 가정해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRequest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;handlermethodargumentresolver-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#handlermethodargumentresolver-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;handlermethodargumentresolver 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HandlerMethodArgumentResolver 구현&lt;/h3&gt;
&lt;p&gt;Argument Resolver 를 만들기 위해선 스프링 내에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;HandlerMethodArgumentResolver&lt;/code&gt; 인터페이스를 구현해야한다. 이 인터페이스를 구현한 클래스가 바로 Argument Resolver 가 되는 것이다. 인터페이스는 아래 2가지 메소드를 구현하도록 명시하고 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;supportsParameter() : 요청받은 메소드의 파라미터에 원하는 어노테이션이 붙어있는지 확인하고, 원하는 어노테이션을 포함하고 있다면 true 를 리턴한다.&lt;/li&gt;
&lt;li&gt;resolveArgument() : supportsParameter() 에서 true 를 리턴받은 경우, 즉 특정 어노테이션이 붙어있는 어느 메소드가 존재하는 경우 파라미터가 원하는 형태로 정보를 바인딩하여 리턴하는 메소드다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;나는 이에 대한 구현체를 아래와 같이 구현했다. &lt;code class=&quot;language-text&quot;&gt;JwtTokenProvider&lt;/code&gt; 는 jwt 토큰을 추출하는 객체이며, &lt;code class=&quot;language-text&quot;&gt;BearerTokenExtractor&lt;/code&gt; 는 토큰에 대한 유효성을 검증하는, 즉 &lt;code class=&quot;language-text&quot;&gt;Bearer&lt;/code&gt; 가 명시되어있는지 검증하는 객체다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthArgumentResolver&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerMethodArgumentResolver&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JwtTokenProvider&lt;/span&gt; jwtTokenProvider&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BearerTokenExtractor&lt;/span&gt; bearerTokenExtractor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthArgumentResolver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JwtTokenProvider&lt;/span&gt; jwtTokenProvider&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BearerTokenExtractor&lt;/span&gt; bearerTokenExtractor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jwtTokenProvider &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; jwtTokenProvider&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bearerTokenExtractor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bearerTokenExtractor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;supportsParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MethodParameter&lt;/span&gt; methodParameter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; methodParameter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasParameterAnnotation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AuthPrincipal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resolveArgument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MethodParameter&lt;/span&gt; methodParameter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                  &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndViewContainer&lt;/span&gt; modelAndViewContainer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                   &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NativeWebRequest&lt;/span&gt; nativeWebRequest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                  &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebDataBinderFactory&lt;/span&gt; webDataBinderFactory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nativeWebRequest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getNativeRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BadRequestException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; accessToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bearerTokenExtractor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;extractValidAccessToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseLong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jwtTokenProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPayload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accessToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoginMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;supportsParameter&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;supportsParameter()&lt;/code&gt; 를 보면 파라미터로 &lt;code class=&quot;language-text&quot;&gt;MethodParameter&lt;/code&gt; 를 전달받는다. 우리가 가정한 현 상황에선 컨트롤러의 &lt;code class=&quot;language-text&quot;&gt;about()&lt;/code&gt; 메소드의 파라미터인 &lt;code class=&quot;language-text&quot;&gt;UserRequest&lt;/code&gt; 가 MethodParameter 에 바인딩 될 것이다. 즉, 요청받은 메소드 about 의 파라미터인 UserRequest 에 어노테이션이 붙어있는지 확인한다.&lt;/p&gt;
&lt;p&gt;아직은 UserRequest 에 어노테이션을 명시한것이 없기 떄문에 &lt;code class=&quot;language-text&quot;&gt;suppertsParameter()&lt;/code&gt; 는 false 를 리턴하게 될 것이다. 우리는 true 를 리턴하도록 해야 Argument Resolver 가 원활히 동작하도록 만들 수 있다. 따라서 향후 커스텀 어노테이션 개발의 필요성을 인지하고 넘어가자. 또한 나의 경우 &lt;code class=&quot;language-text&quot;&gt;AuthPrincipal&lt;/code&gt; 이라는 커스텀 어노테이션을 만들었다.&lt;/p&gt;
&lt;h4&gt;resolveArgument&lt;/h4&gt;
&lt;p&gt;supportsParameter 로 부터 true 를 리턴받았다면, 즉 커스텀 어노테이션을 개발 후 컨트롤러 메소드에 파라미터로 명시해줬다면 파라미터가 원하는 형태로 정보를 바인딩하여 리턴할 수 있게된다. 나는 커스텀 어노테이션을 개발 후 about 메소드의 파라미터가 &lt;code class=&quot;language-text&quot;&gt;LoginMember&lt;/code&gt; 로 바인딩되도록 구현했다.&lt;/p&gt;
&lt;h3 id=&quot;커스텀-어노테이션-개발하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EA%B0%9C%EB%B0%9C%ED%95%98%EA%B8%B0&quot; aria-label=&quot;커스텀 어노테이션 개발하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커스텀 어노테이션 개발하기&lt;/h3&gt;
&lt;p&gt;앞선 요구사항에 따라 커스텀 어노테이션을 구현해준다. 이 어노테이션 타입의 객체를 컨트롤러의 요청 파라미터로 명시하면, 이를 명시한 특정 컨트롤러 메소드에서만 객체가 자동으로 Argument Resolver 를 통해 바인딩된다. 이 메소드에선 컨트롤러에서 중복해서 발생하는 JWT payload 추출 로직, 유효성 검증 로직등이 담기게 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ElementType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PARAMETER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Retention&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RetentionPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RUNTIME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthPrincipal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;webmvcconfigurer-에-argument-resolver-를-등록하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webmvcconfigurer-%EC%97%90-argument-resolver-%EB%A5%BC-%EB%93%B1%EB%A1%9D%ED%95%98%EA%B8%B0&quot; aria-label=&quot;webmvcconfigurer 에 argument resolver 를 등록하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;WebMvcConfigurer 에 Argument Resolver 를 등록하기&lt;/h3&gt;
&lt;p&gt;또한 &lt;code class=&quot;language-text&quot;&gt;WebMvcConfigurer&lt;/code&gt; 를 구현한 클래스를 생성 후, 앞서 만든 Argument Resolver 를 등록해주자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthResolverConfig&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebMvcConfigurer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthArgumentResolver&lt;/span&gt; authArgumentResolver&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthResolverConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthArgumentResolver&lt;/span&gt; authPrincipalArgumentResolver&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;authArgumentResolver &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; authPrincipalArgumentResolver&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addArgumentResolvers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HandlerMethodArgumentResolver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; argumentResolvers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        argumentResolvers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;authArgumentResolver&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;컨트롤러에-argument-resolver-적용하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC%EC%97%90-argument-resolver-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; aria-label=&quot;컨트롤러에 argument resolver 적용하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨트롤러에 Argument Resolver 적용하기&lt;/h3&gt;
&lt;p&gt;마지막으로 컨트롤러에 커스텀 어노테이션 &lt;code class=&quot;language-text&quot;&gt;@AuthPrincipal&lt;/code&gt; 이 명시된 객체를 메소드 파라미터로 명시해주자. 이로써 여러 컨트롤러에서 중복되어 발생하던 인증/인가 중복 로직이 깔끔하게 제거되었다. 이 어노테이션이 파라미터로 명시된 컨트롤러 메소드에서만 자동으로 객체가 Argument Resolver 를 통해 바인딩된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/about&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MemberResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;about&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@AuthPrincipal&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoginMember&lt;/span&gt; loginMember&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;MemberResponse&lt;/span&gt; memberResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; memberService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;loginMember&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;memberResponse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 함으로써 검증의 책임을 컨트롤러에게 책임지지않고 제 3자에게 위임할 수 있게 되었다. 어노테이션만 붙여주면 유효한 토큰을 사용하는 것이 검증된 사용자가 필요한 정보를 가지고 필요한 객체로 바인딩되니 매우 깔끔하고 편리한 코드로 개선되었다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;스프링-인터셉터-spring-interceptor-와의-차이점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9D%B8%ED%84%B0%EC%85%89%ED%84%B0-spring-interceptor-%EC%99%80%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; aria-label=&quot;스프링 인터셉터 spring interceptor 와의 차이점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링 인터셉터 (Spring Interceptor) 와의 차이점&lt;/h2&gt;
&lt;p&gt;미트윈 프로젝트에서 Argument Resolver 를 학습하고 구현하면서 가장 비교되고 혼동되는 대상이 스프링의 인터셉터였다. 일반적으로 인증에 대한 로직을 Arguement Resolver 와 Interceptor 를 함께 활용하여 구현하는 경우가 많은듯했다. 또는 세부 요구사항에 따라 Interceptor 만을 구현하는 경우도 있는듯하다. 그럼에도 나는 Interceptor 를 추가적으로 활용하지 않고 Argument Resolver 만을 활용하여 인가의 책임을 떠넘기도록 구현했다.&lt;/p&gt;
&lt;h3 id=&quot;interceptor&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#interceptor&quot; aria-label=&quot;interceptor permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Interceptor&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0eb051c35b5f35e0748b73d5f4f00c39/9b1e2/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.963190184049076%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACCklEQVR42n2TW09TQRSF+ZXG+At88ME3HzHwYEQfTIyJMRESImkMXqDcBSOXCghGhRBBK6aW0mLh1LbnPnNmPvcp1GCpTrIeJpP5Zu299vTQscIwbMkYQ2ISrLUkSYKWvZbzC5JzI2qvnvOw9HLNqeGcOC2grwIOfx3hxiGJ6xDlc+jCe0xlF13aQRe3UeU9Yh13B6YQa42QxV36+toqZniY+PgE7RQJF4dw5gdpLAxiP2RhbxGTXyVWUXdgWlrZMVRrAk6B2XHo6yMullACZPYeZmmIZO0pejVDsvEM+/l1d4e+79Fo1MmuW6bXFW4UoGam4c4A7vcCzYNd3MfX8MZuYzdG8ecf4c8+wIjTWKsuQC8FNhnLBUys1PGikPr4GNXeXur5fRqFHU4eXqX54hbmYxZ3JUNz6Qnh1hw6Ud0duq7L9KZUtiEOw4BwcgKvvx91WEbXDmHuPuZtBn9zBjf3XKCjRNtvUN2ArZQllFozoe6e9TAteWBAEi2jgp/YgvT0x0tUfoTwawbzbQRzMCUlR/9LWfIVJe1QbvaiDyok/r6kehc+XSdcvkz11RV07hJ264akHLYtdQItWgtMlA5slP9CtLJM8/gYzz0irr5DVRZQpUmC4pQ4nyCuLKL+NYft4W4r/QGeUvjSTz+QH6QMJn24Q/bc/QvADvqpzsr5sz8b/lPZv678BsHBhtYJHf+cAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;image 3&quot;
        title=&quot;&quot;
        src=&quot;/static/0eb051c35b5f35e0748b73d5f4f00c39/a6d36/image-3.png&quot;
        srcset=&quot;/static/0eb051c35b5f35e0748b73d5f4f00c39/222b7/image-3.png 163w,
/static/0eb051c35b5f35e0748b73d5f4f00c39/ff46a/image-3.png 325w,
/static/0eb051c35b5f35e0748b73d5f4f00c39/a6d36/image-3.png 650w,
/static/0eb051c35b5f35e0748b73d5f4f00c39/e548f/image-3.png 975w,
/static/0eb051c35b5f35e0748b73d5f4f00c39/3c492/image-3.png 1300w,
/static/0eb051c35b5f35e0748b73d5f4f00c39/9b1e2/image-3.png 1880w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;스프링 인터셉터는 &lt;code class=&quot;language-text&quot;&gt;서블릿 필터(servlet filter)&lt;/code&gt; 와 같이 웹과 관련한 공통 관심 사항을 효과적으로 해결할 수 있는 기술이다. 이 점에서 AOP 와 같이 횡단 관심사를 깔끔하게 처리할 수 있다는 특징을 지닌다.&lt;/p&gt;
&lt;p&gt;그럼에도 서블릿 필터와 스프링 인터셉터는 다소 다른 개념이다. &lt;code class=&quot;language-text&quot;&gt;서블릿 필터&lt;/code&gt; 는 서블릿이 제공하는 기술이라면, 스프링 인터셉터는 &lt;code class=&quot;language-text&quot;&gt;스프링 MVC&lt;/code&gt; 가 제공하는 기술이다. &lt;strong&gt;스프링 인터셉터는&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;DispatcherServlet&lt;/code&gt; &lt;strong&gt;과 컨트롤러 이 둘 사이에서 컨트롤러 호출 직전에 호출된다는 특징을 지닌다.&lt;/strong&gt; 즉, 클라리언트가 스프링부트 서버의 한 API 를 호출한다면 &lt;strong&gt;HTTP 요청 -&gt; WAS -&gt; Filter -&gt; DispatcherServlet -&gt; 스프링 인터셉터 -&gt; 컨트롤러&lt;/strong&gt; 순으로 요청이 넘어가게 된다.&lt;/p&gt;
&lt;h3 id=&quot;prehandle-posthandle&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#prehandle-posthandle&quot; aria-label=&quot;prehandle posthandle permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;preHandle, postHandle&lt;/h3&gt;
&lt;p&gt;더 정확히는 바로 위에서 설명한 인터셉터는 컨트롤러 호출 전에 대해서만 다룬 것이다. 사실 엄밀히 말해, 인터셉터는 컨트롤러 호출 전과 호춯 후에 모두 호출될 수 있다. 이는 &lt;code class=&quot;language-text&quot;&gt;Handler Interceptor&lt;/code&gt; 인터페이스의 &lt;code class=&quot;language-text&quot;&gt;preHandle()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;postHandle()&lt;/code&gt; 을 구현함으로써 가능해진다. 가령 아래와 같이 구현하면 컨트롤러 호출전에 수행할 인증(authentication) 로직을 미리 수행할 수 있다. 미리 수행할 로직은 &lt;code class=&quot;language-text&quot;&gt;preHandle()&lt;/code&gt; 에 구현하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoginInterceptor&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerInterceptor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;preHandle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; requestURI &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestURI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;[interceptor] requestURI : &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; requestURI&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ... (Authentication 로직 구현)&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// false -&gt; 이후에 진행을 하지 않는다.&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;postHandle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; handler&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt; modelAndView&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;[interceptor] postHandle&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;afterCompletion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; handler&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; ex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;[interceptor] afterCompletion&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;차이점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; aria-label=&quot;차이점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;차이점&lt;/h3&gt;
&lt;p&gt;이러한 인터셉터는 Argument Resolver 와 무슨 차이가 있을까? 이 둘은 &lt;code class=&quot;language-text&quot;&gt;호출되는 시점&lt;/code&gt; 에서 차이를 보인다. Argument Resolver 가 특정 컨트롤러에 요청이 들어왔을 때 동작하는 것이라면, 인터셉터는 컨트롤러가 요청되기 이전 단계에 호출된다.&lt;/p&gt;
&lt;p&gt;또한 &lt;code class=&quot;language-text&quot;&gt;특정 객체의 반환 가능 여부&lt;/code&gt; 에서도 차이를 보인다. ArgumentResolver 는 인터셉터 이후에 도작을 하며, 어떠한 요청이 컨트롤러에 들어왔을 때, 요청에 들어온 값으로부터 원하는 객체를 반환하는 역할을 수행한다. 반면 인터셉터는 실제 컨트롤러가 실행되기전에 요청을 가로채며, &lt;strong&gt;특정 객체를 반환할 수 없다.&lt;/strong&gt; 또한 오직 boolean 혹은 void 반환 타입만 존재한다.&lt;/p&gt;
&lt;h3 id=&quot;인터셉터를-사용하지-않은-이유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%ED%84%B0%EC%85%89%ED%84%B0%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%80-%EC%9D%B4%EC%9C%A0&quot; aria-label=&quot;인터셉터를 사용하지 않은 이유 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인터셉터를 사용하지 않은 이유&lt;/h3&gt;
&lt;p&gt;결론적으로 Argument Resolver 구현만으로도 충분히 인증/인가에 대한 효과적인 로직을 충분히 수행할 수 있다는 판단으로 인터셉터를 굳이 도입하지 않았다. (명확한 이유, 근거없는 기술 도입은 되려 의미없는 행위라고 생각하니깐 🙂)&lt;/p&gt;
&lt;p&gt;현재 인증이 필요한 로직 대부분에서 유저 id 값을 필요로 하기 때문에 굳이 Argument Resolver 와 Interceptor 두 곳에 나눌 필요가 없다고 판단했다. 다만 추가적으로 interceptor가 필요한 시점에 분리하는 것도 좋을 것 같다는 결론을 내렸다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-05-24-spring-interceptor/&quot;&gt;https://tecoble.techcourse.co.kr/post/2021-05-24-spring-interceptor/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://steady-coding.tistory.com/601&quot;&gt;https://steady-coding.tistory.com/601&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/spring-argument-resolver/#WebMvcConfigurer%EC%97%90%EC%84%9C-Argument-Resolver-%EB%93%B1%EB%A1%9D&quot;&gt;https://hudi.blog/spring-argument-resolver/#WebMvcConfigurer%EC%97%90%EC%84%9C-Argument-Resolver-%EB%93%B1%EB%A1%9D&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/250&quot;&gt;https://mangkyu.tistory.com/250&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[자바와 커맨드 패턴(Command Pattern)]]></title><description><![CDATA[커맨드 패턴 커맨트 패턴이란 객체의 "행위(action…]]></description><link>https://haon.site/haon/java/command-pattern/</link><guid isPermaLink="false">https://haon.site/haon/java/command-pattern/</guid><pubDate>Sun, 28 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;커맨드-패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EB%A7%A8%EB%93%9C-%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;커맨드 패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커맨드 패턴&lt;/h2&gt;
&lt;p&gt;커맨트 패턴이란 객체의 &lt;strong&gt;&quot;행위(action)&quot;&lt;/strong&gt; 를 클래스로 만들어 캡슐화하고, 공통적으로 겹치는 여러 행위들을 인터페이스로 공통화하여 언제든지 행위(클래스) 구현체를 유연하게 갈아끼우고 대응할 수 있도록하는 패턴입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;여기서 &quot;행위(action)&quot; 란 메소드로 정의됩니다.&lt;/strong&gt; 보통 다들 개발을 하다보면 어떤 한 묶음의 행위 또는 로직에 대해 &quot;메소드&quot; 를 단위로하여 정의하곤 했을겁니다. 즉, 커맨드 패턴은 공통되는 행위(action) 을 인터페이스로 묶고, 각 세부적인 행위별로 클래스에 세부 행위를 정의해두는 방식입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;커맨드-패턴-구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EB%A7%A8%EB%93%9C-%ED%8C%A8%ED%84%B4-%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;커맨드 패턴 구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커맨드 패턴 구조&lt;/h2&gt;
&lt;p&gt;커맨트 패턴 구조에서 등장하는 등장인물은 아래와 같이 4가지가 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Client (클라이언트) : 인보커에 커맨드를 세팅하고 커맨드 실행을 위임&lt;/li&gt;
&lt;li&gt;Invoker (인보커) : 커맨드를 실행&lt;/li&gt;
&lt;li&gt;Command (커맨드) : 수행해야 할 작업들을 캡슐화 하여 가지고 있으며, 그 작업들을 실행&lt;/li&gt;
&lt;li&gt;Receiver (리시버) : 수행해야 할 작업&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/25acc32b-556b-4a9b-993f-33307dac9591/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;클라이언트에서 실행을 위임받아 여러 작업을 처리하는 객체가 인보커(Invoker) 인데, 인보커가 직접 리시버들을 구성으로 가지고 호출해서 사용하게 되면 인보커와 리시버 사이에 강한 결합이 생기게됩니다. 이렇게 되면 인보커는 구성으로 가지고 있는 리시버의 작업만 행할 수 있으므로 유연성이 떨어지죠.&lt;/p&gt;
&lt;p&gt;여기서 커맨드가 중간에 끼어들어 인보커와 리시버의 결합을 끊어버립니다. 커맨트 패턴을 커맨드를 사용해서 의존성을 역전시키고 유연한 구조를 만듭니다. 이전에 설명한 &quot;전략패턴&quot; 의 경우는 커맨드가 없는 구조로, 인보커와 리시버가 서로 강하게 결합되어 있는 형태입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;커맨드-패턴-도식화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EB%A7%A8%EB%93%9C-%ED%8C%A8%ED%84%B4-%EB%8F%84%EC%8B%9D%ED%99%94&quot; aria-label=&quot;커맨드 패턴 도식화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커맨드 패턴 도식화&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/62bdb6e8-3607-4329-9cf8-c6407027ea15/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;간단한 게임을 하나 가정하고, 커맨드 패턴을 적용해봅시다. 사용자의 초기 위치(좌표)는 0 이라고 가정하고, &lt;code class=&quot;language-text&quot;&gt;MoveFront&lt;/code&gt; 라는 앞으로 이동하기 명령어를 전달받은 경우 +2 만큼 이동합니다. 단, 장애물이 존재하는 경우는 앞으로 이동하지 못하고 현좌표 그대로 유지합니다. &lt;code class=&quot;language-text&quot;&gt;MoveBack&lt;/code&gt; 이라는 뒤로 이동하기 명령어를 전달받는 경우는 -1 만큼 이동하며, 장애물과 상관없이 무조건 이동합니다.&lt;/p&gt;
&lt;h3 id=&quot;command-인터페이스--movecommand&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#command-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4--movecommand&quot; aria-label=&quot;command 인터페이스  movecommand permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Command 인터페이스 : MoveCommand&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Command&lt;/code&gt; 는 인터페이스로 정의되며, 여러 명령어들에 대한 공통적인 &lt;strong&gt;&quot;행위(action)&quot;&lt;/strong&gt; 을 메소드로 선언합니다. 또, 선언된 메소드에 대해 각 구현 클래스에서 자유롭게 구체적인 행위를 정의하면 됩니다. 이 게임의 경우 &quot;이동하기&quot; 가 행위가 되는것이므로 move() 라는 메소드를 선언해줬습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveCommand&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;reviver리시버--movefront-moveback&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reviver%EB%A6%AC%EC%8B%9C%EB%B2%84--movefront-moveback&quot; aria-label=&quot;reviver리시버  movefront moveback permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reviver(리시버) : MoveFront, MoveBack&lt;/h3&gt;
&lt;p&gt;앞서 말했듯이 구현 클래스, 즉 Command 의 &quot;행위(action)&quot; 에 대한 구체적인 행위를 구체적으로 각 클래스에서 정의해주면 됩니다. MoveFront 와 MoveBack 에서 각각 적절히 &quot;이동하는 행위&quot; 에 대해 정의해주면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 앞에 장애물이 있으면 움직이지 못한다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveFront&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveCommand&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; currentPosition&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; moveFrontDistance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; isObstacle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveFront&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; currentPosition&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; isObstacle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentPosition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; currentPosition&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isObstacle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; isObstacle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isObstacle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; currentPosition&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; currentPosition &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; moveFrontDistance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;



&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveBack&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveCommand&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; currentPosition&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; moveBackDistance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveBack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; currentPosition&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentPosition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; currentPosition&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; currentPosition &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; moveBackDistance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;invoker--gamecontroller&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#invoker--gamecontroller&quot; aria-label=&quot;invoker  gamecontroller permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Invoker : GameController&lt;/h3&gt;
&lt;p&gt;다음으로 명령어를 전달받고 적절히 게임을 수행할 &lt;code class=&quot;language-text&quot;&gt;인보커(invoker)&lt;/code&gt; 를 정의해줬습니다. 이때 유효성 검증(Validation) 은 이번 핵심 개념에서 벗어나는 내용이므로 생략합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GameController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;playGame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MoveCommand&lt;/span&gt; command&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// validation(command);&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; command&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;client&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#client&quot; aria-label=&quot;client permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Client&lt;/h3&gt;
&lt;p&gt;마지막으로 인보커를 활용하여, 생성한 여러 명령어들을 전달함으로써 게임이 원활히 수행됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Client&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; currentPosition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// 첫번째 게임(이동) 시도&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;MoveCommand&lt;/span&gt; command1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveFront&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentPosition&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        currentPosition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GameController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;playGame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;command1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// 두번째 액션(게임) 시도&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;MoveCommand&lt;/span&gt; command2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveBack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentPosition&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        currentPosition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GameController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;playGame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;command2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// 세번째 액션(게임) 시도&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;MoveCommand&lt;/span&gt; command3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveFront&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentPosition&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        currentPosition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GameController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;playGame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;command3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;최종좌표:&quot;&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; currentPosition&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 최종좌표:1&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;명령어의-확장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%85%EB%A0%B9%EC%96%B4%EC%9D%98-%ED%99%95%EC%9E%A5&quot; aria-label=&quot;명령어의 확장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;명령어의 확장&lt;/h3&gt;
&lt;p&gt;만약에 명령어 타입을 더 추가하여, 맨 마지막 좌표로 이동하고 싶은 경우는 어떻게할까요? 종점이 좌표값이 100이라고 가정한다면, 아래처럼 Command 인터페이스를 상속받는 명령어 구체 클래스 타입을 새롭게 정의해주면 될 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveToEndPoints&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveCommand&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;커맨드-패턴과-전략-패턴의-차이점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EB%A7%A8%EB%93%9C-%ED%8C%A8%ED%84%B4%EA%B3%BC-%EC%A0%84%EB%9E%B5-%ED%8C%A8%ED%84%B4%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; aria-label=&quot;커맨드 패턴과 전략 패턴의 차이점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커맨드 패턴과 전략 패턴의 차이점&lt;/h2&gt;
&lt;p&gt;지난 &lt;a href=&quot;https://velog.io/@msung99/%EC%A0%84%EB%9E%B5-%ED%8C%A8%ED%84%B4Strategy-Pattern-%EC%9C%BC%EB%A1%9C-%EC%BD%94%EB%93%9C%EC%9D%98-%ED%99%95%EC%9E%A5%EC%84%B1%EC%9D%84-%EA%B3%A0%EB%A0%A4%ED%95%B4%EB%B3%B4%EC%9E%90&quot;&gt;전략 패턴(Strategy Pattern)&lt;/a&gt; 에 이어서 이번 커맨드 패턴을 학습하면서 차이점이 잘 구분되지 않아서 많이 햇갈려했습니다. 다행히도 &lt;a href=&quot;https://stackoverflow.com/questions/4834979/difference-between-strategy-pattern-and-command-pattern&quot;&gt;스택오버플로우&lt;/a&gt; 를 보고서 이에 대한 궁금증이 다소 해결되었습니다.&lt;/p&gt;
&lt;p&gt;결론부터 말하자면 &lt;strong&gt;전략 패턴은 &quot;주어진 입력 및 조건이 동일한 환경&quot; 일때의 알고리즘에 대한 확장성을 고려했다면, &quot;커맨트 패턴은 &quot;액션(Action)&quot;, 즉 행위에 대해 확장성을 고려한 것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&quot;최단경로 구하기&quot; 라는 상황이 주어졌다고 가정해봅시다. 이때 좌표값 x,y 가 주어졌을때, x 에서 y 라는 최단경로를 구해야하는 상황이 주어졌으며, 이를 어떤 알고리즘, 즉 해결법으로 해결하는가에 대한것이 &quot;전략패턴&quot; 입니다. 이 경우는 &quot;경로 구하기&quot; 가 인터페이스라면, 그에 대한 구현체가 다익스트라 알고리즘, 브루트포스 알고리즘등의 다양한 전략이 해당될 것입니다.&lt;/p&gt;
&lt;p&gt;반면 &quot;커맨드 패턴&quot; 에서 바라봤을때는, 좌표 x, y 에 대한것은 중요치 않습니다. 오로지 &quot;최단 경로를 구한다&quot; 라는 행위 그 자체에만 관점이 맞춰져 있기 때문에, x, y 값이 주어지던말던, 또는 그 외에 기타 파라미터들이 주어지는 것과 별개로 &quot;최단 경로를 구할 수 있다&quot; 라는 행위가 잘 동작하면 됩니다.&lt;/p&gt;
&lt;p&gt;이런 이유로 전략패턴은 해당 메소드의 파라미터에 강하게 결합되어있고, 영향을 받습니다. 반면 커맨드패턴은 파라미터와 무관하게 오로지 행위에만 초점이 맞춰져 있으므로, 메소드의 파라미터와 무관하게 동작해야 합니다. 만약 커맨트 패턴으로 설계했는데 파라미터로 인해 제약되는 사항이 있다면 잘못 설계되었을 가능성이 매우 큽니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-10-04-strategy-command-pattern/&quot;&gt;https://tecoble.techcourse.co.kr/post/2021-10-04-strategy-command-pattern/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tinkerbellbass.tistory.com/74&quot;&gt;https://tinkerbellbass.tistory.com/74&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lodado.tistory.com/42&quot;&gt;https://lodado.tistory.com/42&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://victorydntmd.tistory.com/295&quot;&gt;https://victorydntmd.tistory.com/295&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/4834979/difference-between-strategy-pattern-and-command-pattern&quot;&gt;https://stackoverflow.com/questions/4834979/difference-between-strategy-pattern-and-command-pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.hongo.app/chess-command/&quot;&gt;https://blog.hongo.app/chess-command/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kotlinworld.com/370?category=1018782&quot;&gt;https://kotlinworld.com/370?category=1018782&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[생성자 대신 정적 팩터리 메서드 사용을 고려하라]]></title><description><![CDATA[전통적인 public 생성자 전통적인 수단으로  를 활용하여 인스턴스를 생성하는 방법을 한번씩을 사용해봤을 겁니다. 예를들어 아래와 같이 생성했겠죠. 하지만 public…]]></description><link>https://haon.site/haon/java/static-factory-method/</link><guid isPermaLink="false">https://haon.site/haon/java/static-factory-method/</guid><pubDate>Sun, 28 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;전통적인-public-생성자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%ED%86%B5%EC%A0%81%EC%9D%B8-public-%EC%83%9D%EC%84%B1%EC%9E%90&quot; aria-label=&quot;전통적인 public 생성자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전통적인 public 생성자&lt;/h2&gt;
&lt;p&gt;전통적인 수단으로 &lt;code class=&quot;language-text&quot;&gt;public 생성자&lt;/code&gt; 를 활용하여 인스턴스를 생성하는 방법을 한번씩을 사용해봤을 겁니다. 예를들어 아래와 같이 생성했겠죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt; cat1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;고양이&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 public 생성자 대신 (혹은 생성자와 함꼐) 정적 팩토리 메소드를 제공하여 인스턴스를 쉽고 가독성있게 생성할 수 있는 방법이 존재합니다. 정적 팩토리 메소드를 활용했을 때 어떤 장점이 있는것인지에 대해 알아봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1-이름을-가진-생성자-용도를-명확히-파악할-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EC%9D%B4%EB%A6%84%EC%9D%84-%EA%B0%80%EC%A7%84-%EC%83%9D%EC%84%B1%EC%9E%90-%EC%9A%A9%EB%8F%84%EB%A5%BC-%EB%AA%85%ED%99%95%ED%9E%88-%ED%8C%8C%EC%95%85%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;1 이름을 가진 생성자 용도를 명확히 파악할 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 이름을 가진 생성자 (용도를 명확히 파악할 수 있다)&lt;/h2&gt;
&lt;h3 id=&quot;전통-public-생성자로-선언시&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%ED%86%B5-public-%EC%83%9D%EC%84%B1%EC%9E%90%EB%A1%9C-%EC%84%A0%EC%96%B8%EC%8B%9C&quot; aria-label=&quot;전통 public 생성자로 선언시 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전통 public 생성자로 선언시&lt;/h3&gt;
&lt;p&gt;전통적인 public 생성자를 통해 생성자를 생성한다면, 해당 생성자는 이름을 가질 수 없습니다. 예를들어 아래와 같이 Cat 이라는 클래스를 생성했다고 해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sound&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; speed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sound&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sound&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;냐용&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sound &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sound&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;speed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
       &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sound&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;냐오오오용&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sound &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sound&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;speed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
       &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; speed&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;spped &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
           &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sound &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;냐용&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
           &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;speed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; speed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;spped &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
           &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sound &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;냐오오오용&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
           &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;speed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; speed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위는 파라미터로 전달받는 타입(String, int) 에 따라 생성하는 인스턴스의 구현체가 달라지며, 이름도 존재하지 않습니다. 사용자, 즉 클라이언트는 클래스의 인스턴스 사용법을 정확히 파악하기 위해선 모든 생성자의 구현부를 일일이 살펴볼 수 밖에 없을겁니다.&lt;/p&gt;
&lt;p&gt;객체를 생성할 경우는 아래와 같이 여러 타입의 파라미터로 분기처리 될 겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt; cat1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;나용&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt; cat2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;냐오오오용&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt; cat3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt; cat4 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 이렇게 작성하면 과연 가독성이 좋을까요? 사용자는 인스턴스를 생성하기 위해 어떤 파라미터를 넘겨야 인스턴스를 생성할 수 있는지를 모르며, 결국 가독성 저하의 원인이 됩니다. 여기서 생성자가 더 추가된다면 사용자는 더욱이 인스턴스 생성에 혼란을 느낄 수밖에 없습니다.&lt;/p&gt;
&lt;h3 id=&quot;사용용도가-명확히-파악되는-생성자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%82%AC%EC%9A%A9%EC%9A%A9%EB%8F%84%EA%B0%80-%EB%AA%85%ED%99%95%ED%9E%88-%ED%8C%8C%EC%95%85%EB%90%98%EB%8A%94-%EC%83%9D%EC%84%B1%EC%9E%90&quot; aria-label=&quot;사용용도가 명확히 파악되는 생성자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;사용용도가 명확히 파악되는 생성자&lt;/h3&gt;
&lt;p&gt;반면 정적 팩터리 메소드는 이름만 잘 지으면 반환될 객체의 특성을 쉽게 묘사할 수 있습니다. 아래를 보면, static 메소드를 사용했을때의 인스턴스 생성 방식으로써 더욱 가독성있는 객체 생성방식이 되었습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt; cat1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createByMaxSound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;냐용&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt; cat2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createByMinSpeed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;결국 정적 펙토리 메소드를 통한 인스턴스 생성방식은, 한 클래스에 시그니처가 같은 생성자가 여러개 필요할 것 같으면 생성자를 정적 펙토리 메소드로 바꾸고 각각의 차이를 잘 들어내는 이름을 지어주는 것이 좋습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2-싱글톤singleton-또는-통제-클래스로-동작한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EC%8B%B1%EA%B8%80%ED%86%A4singleton-%EB%98%90%EB%8A%94-%ED%86%B5%EC%A0%9C-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%A1%9C-%EB%8F%99%EC%9E%91%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;2 싱글톤singleton 또는 통제 클래스로 동작한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 싱글톤(SingleTon) 또는 통제 클래스로 동작한다.&lt;/h2&gt;
&lt;p&gt;다음으로, &lt;strong&gt;호출될 때마다 인스턴스를 새로 생성하지 않아도 되며, 불필요한 중복 객체생성을 방지할 수 있습니다.&lt;/strong&gt; 덕분에 인스턴스를 미리 생성해두거나, 생성한 인스턴스를 캐싱하여 재활용하는 방식으로 불필요한 객체 생성을 피할 수 있습니다.&lt;/p&gt;
&lt;p&gt;즉, 반복되는 요청에 같은 객체를 반환하는 식으로 동작하는 클래스를 &lt;code class=&quot;language-text&quot;&gt;통제(instance-controlled)&lt;/code&gt; 클래스라고 하며, 이는 곧 해당 클래스가 &lt;code class=&quot;language-text&quot;&gt;싱글톤(SingleTon)&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;불가(noninstaniable)&lt;/code&gt; 를 보장할 수 있게 되는것입니다. 또한 불변 값 클래스에서 동일한 값을 가지고있는 인스턴스를 단 하나 뿐임을 보장할 수 있게 됩니다. (a == b 일떄만 a.equals(b))&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IntegerCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;low &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IntegerCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;high&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IntegerCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IntegerCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;low&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 자주 생성되는 인스턴스는 클래스 내부에 미리 생성해 놓은 다음 반환한다면 코드 성능을 개선할 수 있을겁니다. 예를들어 위처럼 &lt;code class=&quot;language-text&quot;&gt;Integer&lt;/code&gt; 클래스의 &lt;code class=&quot;language-text&quot;&gt;valueOf&lt;/code&gt; 구현 내용을 보면, 전달된 정수 &lt;code class=&quot;language-text&quot;&gt;i&lt;/code&gt; 가 캐싱된 숫자 범위내에 있으면 , 객체를 새롭게 생성하지 않고 &quot;미리 생성된&quot; 객체를 반환합니다. 그렇지 않을 경우에만 &lt;code class=&quot;language-text&quot;&gt;new&lt;/code&gt; 키워드를 사용하여 객체를 생성하는 것을 확인할 수 있습니다. 이 또한 인스턴스 통제(Instance-Controlled) 클래스라고 부릅니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3-반환-타입으로-하위-타입-객체를-반환가능&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%EB%B0%98%ED%99%98-%ED%83%80%EC%9E%85%EC%9C%BC%EB%A1%9C-%ED%95%98%EC%9C%84-%ED%83%80%EC%9E%85-%EA%B0%9D%EC%B2%B4%EB%A5%BC-%EB%B0%98%ED%99%98%EA%B0%80%EB%8A%A5&quot; aria-label=&quot;3 반환 타입으로 하위 타입 객체를 반환가능 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 반환 타입으로 하위 타입 객체를 반환가능&lt;/h2&gt;
&lt;p&gt;클래스의 단순 생성자는 해당 클래스의 인스턴스만 만들 수 있습니다. 반면 정적 펙토리 메소드를 사용하면, 하위 클래스의 인스턴스까지 반환할 수 있게됩니다. 이를 활용하면 구현 클래스를 공개하지 않고도 그 객체를 반환할 수 있습니다.&lt;/p&gt;
&lt;p&gt;새로운 인터페이스와 수많은 구현 클래스가 있을 때, 구현 클래스의 생성자로 인스턴스를 만드는게 아니라 인터페이스의 정적 펙토리 메소드로 인스턴스를 만들어서 개발자가 수많은 구현 클래스들을 이해하지 않고도 인터페이스를 사용할 수 있도록 할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;	&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exercise&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exercise&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createSoccer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Soccer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exercise&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createBaseball&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Baseball&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;playGame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Soccer&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exercise&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;playGame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;축구를 시작합니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Baseball&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exercise&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;playGame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;야구를 시작합니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 테스트를 진행&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Exercise&lt;/span&gt; exercise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exercise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createBaseball&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        exercise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;playGame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;야구를 시작합니다;&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;예를들어 위처럼 사용자는 Exercise 인터페이스의 하위 타입인 클래스 Soccer, Baseball, Boxing 의 구현체를 직접 알 필요가 없습니다. 오로지 Exercise 라는 상위 타입인 인터페이스를 알고있기만 하면, 상황에 알맞게 정적 펙토리 메소드를 호출하여 하위 타입을 주입받으면 끝입니다.&lt;/p&gt;
&lt;p&gt;자바 8부터는 &lt;strong&gt;인터페이스가 정적 메소드를 가질 수 있게 되었으므로, 인터페이스 내부에 정적 펙토리 메소드를 갖는 형태로 코드를 작성하면 됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;4-입력-파라미터에-따른-다른-클래스-객체-반환가능&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-%EC%9E%85%EB%A0%A5-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EC%97%90-%EB%94%B0%EB%A5%B8-%EB%8B%A4%EB%A5%B8-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B0%9D%EC%B2%B4-%EB%B0%98%ED%99%98%EA%B0%80%EB%8A%A5&quot; aria-label=&quot;4 입력 파라미터에 따른 다른 클래스 객체 반환가능 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 입력 파라미터에 따른 다른 클래스 객체 반환가능&lt;/h2&gt;
&lt;p&gt;앞서 말한 장점과 비슷한 내용입니다. 정적 펙토리 메소드를 활용하면, 같은 메소드드라도 상황 및 입력 파라미터 개수에 따라 다른 클래스 인스턴스를 반환할 수 있게됩니다. 예를들어 적은 메모미를 사용해야하는 경우와, 그 반대의 경우에 따라 다른 클래스를 반환함으로써 자원을 효율적으로 사용할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;5-정적-팩토리를-작성하는-시점에는-반환할-객체의-클래스가-존재하지-않아도-된다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC%EB%A5%BC-%EC%9E%91%EC%84%B1%ED%95%98%EB%8A%94-%EC%8B%9C%EC%A0%90%EC%97%90%EB%8A%94-%EB%B0%98%ED%99%98%ED%95%A0-%EA%B0%9D%EC%B2%B4%EC%9D%98-%ED%81%B4%EB%9E%98%EC%8A%A4%EA%B0%80-%EC%A1%B4%EC%9E%AC%ED%95%98%EC%A7%80-%EC%95%8A%EC%95%84%EB%8F%84-%EB%90%9C%EB%8B%A4&quot; aria-label=&quot;5 정적 팩토리를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. 정적 팩토리를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.&lt;/h2&gt;
&lt;p&gt;클래스가 존재해야 생성자가 존재할 수 있습니다. 하지만 정적 펙토리 메소드는 메소드와 반환할 타입만 정해두고, 실제 반환될 클래스는 나중에 구현하는게 가능합니다. 프로젝트의 규모 및 여러 개발팀이 협력하는 상황에서, 원활한 협업을 위해 인터페이스까지 먼저 합의하여 만들고, 실제 구현체는 추후 만드는 식으로 업무가 진행됩니다. 이때 정적 펙토리 메소드를 사용할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;정적-펙토리-메소드의-단점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EC%A0%81-%ED%8E%99%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%86%8C%EB%93%9C%EC%9D%98-%EB%8B%A8%EC%A0%90&quot; aria-label=&quot;정적 펙토리 메소드의 단점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정적 펙토리 메소드의 단점&lt;/h2&gt;
&lt;p&gt;무조건 장점만 있는것은 아닙니다. 분명 단점도 존재하긴 합니다.&lt;/p&gt;
&lt;h3 id=&quot;1-문서화가-힘들다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EB%AC%B8%EC%84%9C%ED%99%94%EA%B0%80-%ED%9E%98%EB%93%A4%EB%8B%A4&quot; aria-label=&quot;1 문서화가 힘들다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 문서화가 힘들다.&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;자바독(JavaDoc)&lt;/code&gt; 등을 활용하여 Java 클래스를 문서화할때, 자바독에서 클래스의 생성자는 잘 표시해두지만, 정적 펙토리메 메소드는 일반 메소드이기 때문에 개발자가 직접 문서를 찾아야합니다.&lt;/p&gt;
&lt;h3 id=&quot;2-private-생성자인-경우-상속이-불가능하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-private-%EC%83%9D%EC%84%B1%EC%9E%90%EC%9D%B8-%EA%B2%BD%EC%9A%B0-%EC%83%81%EC%86%8D%EC%9D%B4-%EB%B6%88%EA%B0%80%EB%8A%A5%ED%95%98%EB%8B%A4&quot; aria-label=&quot;2 private 생성자인 경우 상속이 불가능하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. private 생성자인 경우 상속이 불가능하다.&lt;/h3&gt;
&lt;p&gt;상속을 하려면 public 이나 protected 생성자가 필요하니, 정적 펙토리 메소드만 제공하면 하위 클래스를 만들 수 없습니다. 정적 펙토리 메소드만을 사용하게 하려면 기존 생성자는 private 으로 해야하고, 상속을 할 수 없게됩니다. 하지만 이 단점은 상속보다 컴포지션 사용을 유도하고 불변 타입으로 만들려면 이 제약을 지켜야 한다는 점에서 장점으로 받아들일 수도 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;정적-펙토리-메소드-네이밍&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EC%A0%81-%ED%8E%99%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%86%8C%EB%93%9C-%EB%84%A4%EC%9D%B4%EB%B0%8D&quot; aria-label=&quot;정적 펙토리 메소드 네이밍 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정적 펙토리 메소드 네이밍&lt;/h2&gt;
&lt;p&gt;앞서 언급한 &lt;code class=&quot;language-text&quot;&gt;단점1. 문서화가 힘들다&lt;/code&gt; 를 보완하기 위해, 널리 알려진 정적 펙토리 메소드 규악을 준수하면서 메소드를 명명하는 것이 좋습니다.&lt;/p&gt;
&lt;h3 id=&quot;1-from&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-from&quot; aria-label=&quot;1 from permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. from&lt;/h3&gt;
&lt;p&gt;매개변수를 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메소드&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt; d &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;instant&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-of&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-of&quot; aria-label=&quot;2 of permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. of&lt;/h3&gt;
&lt;p&gt;여러 매개변수를 받아 적합한 타입의 인스턴스로 반환하는 집계 메소드&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Rank&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; faceCards &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EnumSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JACK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;QUEEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KING&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;3-valueof&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-valueof&quot; aria-label=&quot;3 valueof permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. valueOf&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;from&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;of&lt;/code&gt; 의 더 자세한 버전&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt; prime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigInteger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MAX_VALUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;4-instance-또는-getinstance&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-instance-%EB%98%90%EB%8A%94-getinstance&quot; aria-label=&quot;4 instance 또는 getinstance permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. instance 또는 getInstance&lt;/h3&gt;
&lt;p&gt;(매개변수를 받는다면) 매개변수로 명시한 인스턴스를 반환하지만, 같은 인스턴스임을 보장하지는 않습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;StackWalker&lt;/span&gt; luke &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StackWalker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;5-create-또는-newinstance&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-create-%EB%98%90%EB%8A%94-newinstance&quot; aria-label=&quot;5 create 또는 newinstance permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. create 또는 newInstance&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;instance&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;getInstance&lt;/code&gt; 와 같지만, 매번 새로운 인스턴스를 생성해 반환함을 보장합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; newArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;classObject&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arrayLen&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;6-gettype&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-gettype&quot; aria-label=&quot;6 gettype permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. getType&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;getInstance&lt;/code&gt; 와 같지만, 생성할 클래스가 아닌 다른 클래스에 펙토리 메소드를 정의할 때 씁니다. &quot;Type&quot; 은 펙터리 메소드가 반환할 객체의 타입입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;FileStore&lt;/span&gt; fs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FIles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFileStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;7-newtype&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#7-newtype&quot; aria-label=&quot;7 newtype permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7. newType&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;newInstance&lt;/code&gt; 와 같으나, 생성할 클래스가 아닌 다른 클래스에 펙터리 메소드를 정의할 때 씁니다. &quot;Type&quot; 은 펙터리 메소드가 반환할 객체의 타입입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;BufferedReader&lt;/span&gt; br &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newBufferedReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;8-path&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#8-path&quot; aria-label=&quot;8 path permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;8. path&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;getType&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;newType&lt;/code&gt;의 간결한 버전&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; litany &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Collections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;legacyLitancy&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nankisu.tistory.com/87&quot;&gt;https://nankisu.tistory.com/87&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/effective-java-static-factory-method/&quot;&gt;https://hudi.blog/effective-java-static-factory-method/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[팩토리 메소드 패턴 (Factory Method Pattern)]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/java/factory-method-pattern/</link><guid isPermaLink="false">https://haon.site/haon/java/factory-method-pattern/</guid><pubDate>Sat, 27 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;팩토리-메소드-패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%86%8C%EB%93%9C-%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;팩토리 메소드 패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;팩토리 메소드 패턴&lt;/h2&gt;
&lt;p&gt;팩토리 메소드 패턴은 &lt;strong&gt;객체의 생성 코드를 별도의 클래스 및 메소드로 분리&lt;/strong&gt;함으로써 객체 생성의 변화에 대비하는데 유용합니다. &lt;strong&gt;상황에 따라 다양한 타입의 적절한 객체를 생성하는 코드가 중복되는 것이 많을 때&lt;/strong&gt; 사용하기 매우 유용합니다. 객체 생성 코드를 따로 분리한다면, 요구사항 및 여러 분기처리에 따라 다양한 타입의 객체를 생성해야하는 경우에 효과적으로 대응할 수 있게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;구성-방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B5%AC%EC%84%B1-%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;구성 방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;구성 방법&lt;/h3&gt;
&lt;p&gt;인스턴스 생성을 서브 클래스에게 위임하고, 부모 추상 클래스는 인터페이스에만 의존하고 실제로 어떤 구현 클래스를 호출할지는 서브 클래스에서 구현합니다. 이렇게하면 새로운 구현 클래스가 추가되어도 기존 팩토리 코드의 수정없이 새로운 팩토리를 추가하면 됩니다. 이때 팩토리란, 구체 타입의 객체를 생성해주는 역할을 담당하는 추상 클래스를 말합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;팩토리-메소드-적용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%A0%81%EC%9A%A9&quot; aria-label=&quot;팩토리 메소드 적용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;팩토리 메소드 적용&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a3cf24cf-ced6-451e-894a-66e32d999502/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;지금부터 적용해볼 상황은 위와 같습니다. 클라이언트는 선호에 따라 다양한 종류의 책을 구매할 원합니다. 이를위해 클라이언트는 추상 메소드 &lt;code class=&quot;language-text&quot;&gt;BookFactory&lt;/code&gt; 에만 의존하고 있으며, 상황에 따라 적절한 Book 타입의 객체를 생성해줄 팩토리를 활용하여 객체를 생성받게 됩니다.&lt;/p&gt;
&lt;p&gt;각 팩토리 구현체는 객체를 생성하는 메소드 &lt;code class=&quot;language-text&quot;&gt;createBook()&lt;/code&gt; 만을 구현하면 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;팩토리-추상-클래스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%A9%ED%86%A0%EB%A6%AC-%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4&quot; aria-label=&quot;팩토리 추상 클래스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;팩토리 추상 클래스&lt;/h3&gt;
&lt;p&gt;클라이언트가 의존하게 될 부모 클래스는 &lt;code class=&quot;language-text&quot;&gt;abstract&lt;/code&gt; 를 활용하여 추상 클래스로 정의해줬습니다. 특히 &lt;code class=&quot;language-text&quot;&gt;createBook()&lt;/code&gt; 를 보면 추상 메소드임을 알 수 있는데, 이로써 BookFactory 를 상속하는 각 팩토리 구현 클래스들은 각자의 책임에 알맞게 적절한 타입의 객체를 생성하는 코드를 구성하면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookFactory&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;newInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        book&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;buyBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;팩토리-구체-클래스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%A9%ED%86%A0%EB%A6%AC-%EA%B5%AC%EC%B2%B4-%ED%81%B4%EB%9E%98%EC%8A%A4&quot; aria-label=&quot;팩토리 구체 클래스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;팩토리 구체 클래스&lt;/h3&gt;
&lt;p&gt;각 팩토리 클래스는 본인의 역할에 알맞는 타입의 객체를 생성하는 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EssayFactory&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Essay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DiaryFactory&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Diary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;객체-인터페이스-및-구현체&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%9D%EC%B2%B4-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EB%B0%8F-%EA%B5%AC%ED%98%84%EC%B2%B4&quot; aria-label=&quot;객체 인터페이스 및 구현체 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;객체 인터페이스 및 구현체&lt;/h3&gt;
&lt;p&gt;여러 타입의 객체를 생성하는 것을 대표할 추상 클래스인 BookFactory 는, 본인이 생성할 객체가 구체 타입에 의존해선 안됩니다. 때문에 객체는 추상화된 인터페이스로 정의되어있으며, BookFactory 는 이 인터페이스만을 의존하게되는 형태입니다. 그러고 Book 을 구현하고 있는 구체타입에 객체에 대해 각각의 팩토리 구현 클래스 (EssayFactory, DiraryFactory) 들이 생성하는 형태입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;buyBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Essay&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;buyBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;소설책을 구매합니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;언제-사용하면-좋을까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%A9%B4-%EC%A2%8B%EC%9D%84%EA%B9%8C&quot; aria-label=&quot;언제 사용하면 좋을까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;언제 사용하면 좋을까?&lt;/h2&gt;
&lt;p&gt;이런 팩토리 메소드 패턴의 특징을 고려했을 때, 다음과 같은 상황에 활용하면 좋을겁니다. 우선 작성한 코드가 함께 작동해야하는 객체들의 정확한 유형들과 의존관계를 미리 모르는 경우가 사용하면 좋을겁니다. 필요에 따라서 얼마든지 객체 생성을 기존 코드의 수정없이 마음껏 확장 가능하기 때문이겠죠. 이로싸 앞서 언급했듯이, 객체의 생성 코드를 별도의 클래스/메서드로 분리함으로써 객체 생성의 변화에 대비하는 데 유용할겁니다.&lt;/p&gt;
&lt;p&gt;비슷한 맥락으로, 상황에 따라 적절한 객체를 생성하는 코드가 자주 중복될 것 같을때 활용하면 됩니다. 이는 앞선 내용들을 이해했다면 무슨 의미인지 이해하실겁니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://bcp0109.tistory.com/367&quot;&gt;https://bcp0109.tistory.com/367&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://refactoring.guru/ko/design-patterns/factory-method&quot;&gt;https://refactoring.guru/ko/design-patterns/factory-method&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gmlwjd9405.github.io/2018/08/07/factory-method-pattern.html&quot;&gt;https://gmlwjd9405.github.io/2018/08/07/factory-method-pattern.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[자바와 템플릿 메소드 패턴(Template Method Pattern)]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/java/template-method-pattern/</link><guid isPermaLink="false">https://haon.site/haon/java/template-method-pattern/</guid><pubDate>Sat, 27 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습동기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%8F%99%EA%B8%B0&quot; aria-label=&quot;학습동기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습동기&lt;/h2&gt;
&lt;p&gt;지난번부터 계속해서 우연히 템플릿 메소드 패턴에 대해 알게 되었습니다. 이번에 이 디자인패턴에 대해 자세히 학습해보고, 코드를 깔끔하게 정리하는 방식으로 기존 코드를 개선해보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;템플릿-메소드-패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A9%94%EC%86%8C%EB%93%9C-%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;템플릿 메소드 패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;템플릿 메소드 패턴&lt;/h2&gt;
&lt;p&gt;템플릿 메소드 패턴은 여러 작업들이 완전히 똑같은 로직 및 과정을 거치지만, 극히 일부 코드 및 동작에 대해선 각각 다르게 구현해야할 떄 사용되는 패턴입니다. 즉, &lt;strong&gt;전체적으로는 동일하면서 일부분만 다른 구문으로 메소드를 구성하여 중복 코드를 최소화&lt;/strong&gt;할 수 있습니다. 템플릿 메소드 패턴은 아래와 같이 2가지로 나뉩니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;실행 과정을 구현한 상위 클래스 (추상 클래스)&lt;/li&gt;
&lt;li&gt;실행 과정의 일부 단계를 구현한 하위 클래스(구체 클래스)&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;상위 클래스는 작업의 전체 흐름을 구현합니다. 즉, 상위 클래스가 흐름 제어의 주최가됩니다. 그리고 각 구현체별로 다르게 구현해야할 일부 동작은 추상 메소드로 따로 정의한 다음, 그 추상 메소드를 호출하는 방식으로 구현하게 도비니다. 하위 클래스는 다르게 동작해야하는 부분, 즉 추상 메소드만 재정의하면 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;템플릿-메소드를-미적용한-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A9%94%EC%86%8C%EB%93%9C%EB%A5%BC-%EB%AF%B8%EC%A0%81%EC%9A%A9%ED%95%9C-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;템플릿 메소드를 미적용한 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;템플릿 메소드를 미적용한 경우&lt;/h3&gt;
&lt;p&gt;간단한 게임을 하나 구현해봅시다. 시작좌표와 끝좌표를 입력받고, 그에 대한 이동 관련 메소드 &lt;code class=&quot;language-text&quot;&gt;move()&lt;/code&gt; 를 정의해줬습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PlayGame&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; end&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러고 PlayGame 에 대한 &lt;code class=&quot;language-text&quot;&gt;move()&lt;/code&gt; 를 구체화할 수 있도록, 2개의 구체 클래스를 정의하고 그 안에 행위를 정의해줬습니다. 2개의 클래스는 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 는 두 구체클래스에서 아예 흐름이 겹치지 않는 별개의 코드입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DefaultGame&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PlayGame&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... (생략)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; end&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// (1) - 다른 코드내용&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;=====================================&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;시작좌표:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;끝좌표:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; end&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;시작+끝좌표:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;start&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;end&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;이동거리:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;end &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;=====================================&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// (2) - 다른 코드내용&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 사이의 코드를 보면 알겠지만, 동일한 플로우 및 로직을 가진 중복되는 코드가 발생했습니다. 시작좌표와 끝좌표를 활용하여 다양한 좌표값을 계산하고 출력하는 흐름 및 코드가 모두 동일합니다. 차이점이라 함은, 이동거리를 계산할때 &lt;code class=&quot;language-text&quot;&gt;abs()&lt;/code&gt; 를 활용하는가 아닌가에만 차이가 존재합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AbsGame&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PlayGame&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... (생략)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; end&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// (1) - 다른 코드내용&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;=====================================&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;시작좌표:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;끝좌표:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; end&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;시작+끝좌표:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;start&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;end&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;이동거리:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;end &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;=====================================&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// (2) - 다른 코드내용&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;템플릿-메소드-패턴-적용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A9%94%EC%86%8C%EB%93%9C-%ED%8C%A8%ED%84%B4-%EC%A0%81%EC%9A%A9&quot; aria-label=&quot;템플릿 메소드 패턴 적용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;템플릿 메소드 패턴 적용&lt;/h3&gt;
&lt;p&gt;중복되는 코드를 제거하기 위해, 앞서 설명한 것 처럼 템플릿 메소드를 적용할 수 있습니다. 앞서 설명했듯이 좌표를 계산하는 여러 흐름중 &quot;이동거리&quot; 계산방법에서만 약간의 차이를 보입니다. 이를 감안하고 패턴을 적용하여 중복을 제거해봅시다.&lt;/p&gt;
&lt;p&gt;우선 상위 클래스인 &lt;code class=&quot;language-text&quot;&gt;PlayGame&lt;/code&gt; 을 아래와 같이 다시 구현했습니다. 각 구체 클래스에 정의했던 기존 &lt;code class=&quot;language-text&quot;&gt;move()&lt;/code&gt; 들은 플로우가 동일하므로, 재활용성을 높이고 중복을 제거하기 위해 상위 클래스로 이동 및 공통으로 정의해줬습니다. 또한 각각의 세부 동작을 구체 클래스에서 정의해줄 수 있도록, 세부 행위를 &lt;code class=&quot;language-text&quot;&gt;calculate()&lt;/code&gt; 라는 추상 메소드로 추상화해줬습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PlayGame&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; end&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;=====================================&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;시작좌표:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;끝좌표:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; end&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;이동거리&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calculate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;start&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;=====================================&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calculate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; end&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;세부 동작(행위) 에 대해선 구체 클래스에서 그 행위들을 개별적으로 정의해줬습니다. 앞서 살펴본 각 일부분만 다른 구체적인 동작들이 이렇게 구체 클래스에서 정의되는 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DefaultGame&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PlayGame&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... (생략)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calculate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; end&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;end &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;start&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AbsGame&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PlayGame&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... (생략)&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calculate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; end&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;end &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;start&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이로써 전체적으로는 동일한 플로우에서 일부 세부 동작만을 각각 구체 클래스에서 재정의함으로써, 중복 코드가 최소화되는 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/template-method-pattern/&quot;&gt;https://hudi.blog/template-method-pattern/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gmlwjd9405.github.io/2018/07/13/template-method-pattern.html&quot;&gt;https://gmlwjd9405.github.io/2018/07/13/template-method-pattern.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[전략 패턴(Strategy Pattern)]]></title><description><![CDATA[…]]></description><link>https://haon.site/java/strategy-pattern/</link><guid isPermaLink="false">https://haon.site/java/strategy-pattern/</guid><pubDate>Fri, 26 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습동기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%8F%99%EA%B8%B0&quot; aria-label=&quot;학습동기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습동기&lt;/h2&gt;
&lt;p&gt;이번 카카오테크 교육과정 실습 미션은 &lt;code class=&quot;language-text&quot;&gt;상품 할인 관리 시스템 구현하기&lt;/code&gt; 이다. 디자인패턴을 학습하고 코드를 구현하는 것이 요구사항인데, 이 과정에서 전략패턴에 대해 학습하게 되었다. 전략 패턴에 대한 생각의 정교화를 위해 글을 작성해본다.&lt;/p&gt;
&lt;h2 id=&quot;전략패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%EB%9E%B5%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;전략패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전략패턴&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7bb3035294278302b482f1a8c8b6e999/a8979/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.061349693251532%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA3UlEQVR42o2RzUrDUBBG+/5P4sKFBTciVEWNVEFNjDVpG425VJuqAWsSvem9c4xx0ShV+sH8wDCH4ZsOjaTJxmhmiYMa9ep6QlkWVLrgOdlHhTvMbnfRZUZ757c67WFVaQLvgMFlj4G7Rzp9JH/LGF5sctXfQAXb5PP0e0Psf8ClpFVFhIWxzAtL9vpB/m7RC8HK6utWAr9kreVJnTLyukyiQ14mZ4y9LaaxQ3rfr/tuMzfGrA+Mxy6Bf0QcuXV4hP4x6s4nCs8Jrx0ekpvaomo94A8LpGWD/PWKJfATFmXNi6VRAlcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7bb3035294278302b482f1a8c8b6e999/a6d36/image.png&quot;
        srcset=&quot;/static/7bb3035294278302b482f1a8c8b6e999/222b7/image.png 163w,
/static/7bb3035294278302b482f1a8c8b6e999/ff46a/image.png 325w,
/static/7bb3035294278302b482f1a8c8b6e999/a6d36/image.png 650w,
/static/7bb3035294278302b482f1a8c8b6e999/e548f/image.png 975w,
/static/7bb3035294278302b482f1a8c8b6e999/3c492/image.png 1300w,
/static/7bb3035294278302b482f1a8c8b6e999/a8979/image.png 1828w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;전략 패턴이란, 유사한 행위(알고리즘)를 수행하는 여러 전략들에 대해 공통의 인터페이스를 정의해두고, 각각의 구체적인 전략에 대한 클래스로 캡슐화하고, 언제든 동적으로 전략을 교체할 수 있도록하는 디자인패턴이다.&lt;/strong&gt; 이때 전략이라는 말이 다소 와닿지 않을 수 있는데, 전략을 &quot;알고리즘&quot; 이라고 생각해도 좋다.&lt;/p&gt;
&lt;p&gt;우리에게 주어진 특정한 문제를 해결하기 위한 여러 전략(알고리즘) 을 미리 클래스로 설계하여 캡슐화해둔다. 즉, 전략을 클래스로 캡슐화해둔다. 이후 요구사항 및 상황에 따라 유연하게 전략을 교체하는 방식으로 동작한다. 마치 A 에서 B 로 도달하는 알고리즘을 구현할 때 다익스트라 알고리즘으로 구현하기, 크루스칼 알고리즘으로 구현하기, DFS 로 구현하기 등으로 생각하면 쉽다.&lt;/p&gt;
&lt;h3 id=&quot;전략패턴을-사용하지-않았을-때&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%EB%9E%B5%ED%8C%A8%ED%84%B4%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EC%95%98%EC%9D%84-%EB%95%8C&quot; aria-label=&quot;전략패턴을 사용하지 않았을 때 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전략패턴을 사용하지 않았을 때&lt;/h3&gt;
&lt;p&gt;백문이불어일타. 코드를 직접 작성하면서 디자인패턴을 이해해보자. 이를위해, 우선 전략패턴을 사용하지 않은 일반적인 코드에 대해 살펴보자.&lt;/p&gt;
&lt;p&gt;아래와 같은 &lt;code class=&quot;language-text&quot;&gt;NumberConverter&lt;/code&gt; 오브젝트가 있다고 해보자. 이 객체는 주어진 숫자를 증폭해주는 변환기 역할을 한다. 1은 10으로, 2는 100으로, 3은 1000으로 변환해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberConverter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateRandomNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;10 으로 변환한다!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;100 으로 변환한다!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1000 으로 변환한다!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같은 컨버터 클래스는 아래와 같이 사용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;NumberConverter&lt;/span&gt; one &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberConverter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;NumberConverter&lt;/span&gt; two &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberConverter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;NumberConverter&lt;/span&gt; three &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberConverter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

one&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateRandomNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
two&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateRandomNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
three&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateRandomNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 이러한 구조는 어떤 문제점이 있을까? 예를들어 기존의 3가지 변환 규칙 이외에 새로운 전략으로 number 값이 4인 경우에 대해 코드를 작성하려면, 아래와 같이 4에 대한 코드를 추가함으로써, &lt;code class=&quot;language-text&quot;&gt;generateRandomNumber()&lt;/code&gt; 메소드 내에 변환이 발생한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateRandomNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;10 으로 변환한다!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;100 으로 변환한다!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1000 으로 변환한다!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;10000 으로 변환한다!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이러한 구조는 &lt;code class=&quot;language-text&quot;&gt;OCP(개방 폐쇄 원칙)&lt;/code&gt; 을 위반하는 것이다. 결국 기존 코드에 영향을 주게되었고, 이는 상황에 따라 자칫 서비스 전체에 영향을 미칠 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;전략패턴을-적용한-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%EB%9E%B5%ED%8C%A8%ED%84%B4%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%9C-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;전략패턴을 적용한 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전략패턴을 적용한 경우&lt;/h2&gt;
&lt;p&gt;전략패턴은 &lt;strong&gt;유사 행위(전략) 을 수행하는 단위별로 클래스를 나누어 캡슐화하고, 공통적인 인터페이스로 그룹화한다.&lt;/strong&gt; 이 경우 &quot;숫자를 변환&quot; 하는 행위가 공통 행위가 될 것이며, 어떻게 숫자를 변환시킬지에 대한 상세 행위가 &quot;전략&quot; 이 된다.&lt;/p&gt;
&lt;p&gt;따라서 숫자 변환 결과를 반환하는 &lt;code class=&quot;language-text&quot;&gt;generateNumber&lt;/code&gt; 메소드를 갖는 &lt;code class=&quot;language-text&quot;&gt;NumberGenerateStrategy&lt;/code&gt; 인터페이스를 아래와 같이 추상화한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerateStrategy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;다음으로 각 숫자별 세부 전략을 클래스로 캡슐화한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SmallNumberStrategy&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerateStrategy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MediumNumberStrategy&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerateStrategy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BidNumberStrategy&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerateStrategy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;        
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;기존의 &lt;code class=&quot;language-text&quot;&gt;NumberConveter&lt;/code&gt; 는 아래와 같이 생성자 주입으로 인터페이스 타입의 전략을 주입받는다. 이로써 NumberConverter 는 상황에 따라 알맞은 전략 구체 클래스를 외부로 부터 주입받아서, 상황에 따라 알맞은 전략을 사용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberConverter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerateStrategy&lt;/span&gt; strategy&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NumberGenerateStrategy&lt;/span&gt; strategy&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; strategy&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateRandomNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; 으로 변환한다!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;각 전략을 캡슐화함으로써, 각 전략에 미치는 영향력은 최소화되었다. 또한 NumberConverter 는 적절히 전략 객체를 주입받아 숫자를 변환하면 끝이다.&lt;/p&gt;
&lt;h2 id=&quot;전략패턴의-사용-효과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%EB%9E%B5%ED%8C%A8%ED%84%B4%EC%9D%98-%EC%82%AC%EC%9A%A9-%ED%9A%A8%EA%B3%BC&quot; aria-label=&quot;전략패턴의 사용 효과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전략패턴의 사용 효과&lt;/h2&gt;
&lt;p&gt;코드 문맥이 특정한 구현체가 아닌 인터페이스에 의존한다. 즉, 문맥이 구체 컴포넌트에 의존하지 않고, 역으로 컴포넌트가 읜터페이스에 의존하는 &lt;code class=&quot;language-text&quot;&gt;DIP(의존성 역전이 원칙)&lt;/code&gt; 를 따르게 된다.&lt;/p&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;간혹 코드를 짜다보면 &lt;strong&gt;매우 비슷한 형태 또는 플로우를 지닌 여러 기능들이 존재할것이다.&lt;/strong&gt; 즉, 매우 비슷한 로직 또는 형태를 지닌 여러 기능 및 케이스가 존재할때 사용하면 매우 유용한 전략이 될것이다. 또는 기능이 완전히 동일한데 요구사항 및 세부정책에 따라 자그마한 분기처리만 처리해도 좋을 경우에도 활용하면 좋은 패턴이 될것이다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://diqmwl-programming.tistory.com/101&quot;&gt;https://diqmwl-programming.tistory.com/101&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/strategy-pattern/&quot;&gt;https://hudi.blog/strategy-pattern/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://victorydntmd.tistory.com/292&quot;&gt;https://victorydntmd.tistory.com/292&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[싱글톤(SingleTon) 패턴 구현방법 6가지, Bill Pugh Solution]]></title><description><![CDATA[…]]></description><link>https://haon.site/java/singleton/</link><guid isPermaLink="false">https://haon.site/java/singleton/</guid><pubDate>Wed, 24 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습동기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%8F%99%EA%B8%B0&quot; aria-label=&quot;학습동기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습동기&lt;/h2&gt;
&lt;p&gt;이번 카카오테크 교육과정 실습 미션은 &lt;code class=&quot;language-text&quot;&gt;상품 할인 관리 시스템 구현하기&lt;/code&gt; 이다. 디자인패턴을 학습하고 코드를 구현하는 것이 요구사항인데, 이 과정에서 싱글톤을 사용하게 되었다. 지난 포스트에서 전략 패턴에 대해서도 다루었는데, 이어서 싱글톤에 대해서도 생각을 정교화하고자 글을 작성한다.&lt;/p&gt;
&lt;h2 id=&quot;싱글톤singleton&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%B1%EA%B8%80%ED%86%A4singleton&quot; aria-label=&quot;싱글톤singleton permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;싱글톤(SingleTon)&lt;/h2&gt;
&lt;p&gt;싱글톤(SingleTon) 이란 인스턴스를 오직 하나만 생성할 수 있는 클래스를 뜻한다. &lt;code class=&quot;language-text&quot;&gt;(1) 유일성&lt;/code&gt; - 프로그램 내에서 하나의 객체만 존재하며, &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 프로그램 내의 여러 부분에서 해당 객체를 공유해서 사용해야 할 때 사용하는 디자인패턴이다.&lt;/p&gt;
&lt;p&gt;다만 유의할 점은, 싱글톤으로 구현한 클래스는 테스트하기 어렵다는 특징이 있다. 인터페이스를 하나 정의 후, 해당 인터페이스를 구현해서 만든 오브젝트가 싱글톤이 아니라면 Mock 객체 구현 방식으로도 대체할 수 없기 떄문이다.&lt;/p&gt;
&lt;h2 id=&quot;싱글톤-오브젝트-생성-방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%B1%EA%B8%80%ED%86%A4-%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1-%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;싱글톤 오브젝트 생성 방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;싱글톤 오브젝트 생성 방법&lt;/h2&gt;
&lt;p&gt;싱글톤 객체를 생성하는 방식에는 아래와 같은 2가지 방식이 존재한다.&lt;/p&gt;
&lt;h3 id=&quot;public-static-fial-키워드와-private-생성자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#public-static-fial-%ED%82%A4%EC%9B%8C%EB%93%9C%EC%99%80-private-%EC%83%9D%EC%84%B1%EC%9E%90&quot; aria-label=&quot;public static fial 키워드와 private 생성자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;public static fial 키워드와 private 생성자&lt;/h3&gt;
&lt;p&gt;private 생성자를 두고, public static final 키워드로 객체를 생성하는 방식이다. private 생성자는 public static final 필드인 인스턴스를 초기화할 떄 딱 1번만 호출된다는 특징이 있다. 생성자가 public 이나 protected 가 아니므로, 오브젝트가 초기화화 될 떄 만들어진 인스턴스가 전체 시스템에서 단 1개뿐임이 보장된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 시스템 내에 유일성을 갖는 단 1개의 객체&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBookInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;정적-팩토리-메소드로-public-static-멤버로-제공하는-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%86%8C%EB%93%9C%EB%A1%9C-public-static-%EB%A9%A4%EB%B2%84%EB%A1%9C-%EC%A0%9C%EA%B3%B5%ED%95%98%EB%8A%94-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;정적 팩토리 메소드로 public static 멤버로 제공하는 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정적 팩토리 메소드로 public static 멤버로 제공하는 방식&lt;/h3&gt;
&lt;p&gt;정적 팩토리 메소드를 통해 public static 멤버를 제공하는 방식으로도 싱글톤을 보장할 수 있다. 아래의 경우 &lt;code class=&quot;language-text&quot;&gt;getInstance()&lt;/code&gt; 로 항상 같은 동일한 객체를 참조한 결과를 반환하므로 싱긑톤이 보장된다. 여기서도 마찬가지로 private 생성자가 호출되기 떄문에, 외부에서 신규 객체가 생성되는 상황을 방지할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBookInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;private-생성자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#private-%EC%83%9D%EC%84%B1%EC%9E%90&quot; aria-label=&quot;private 생성자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;private 생성자&lt;/h3&gt;
&lt;p&gt;여기서 둘의 공통점은 바로 &lt;code class=&quot;language-text&quot;&gt;private 생성자&lt;/code&gt; 이다. 생성자를 private 으로 생성함으로써 외부에서 새로운 객체의 생성을 방지할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;geInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;book1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;// book1, book2 는 같은 인스턴스이므로 같은 주소값 반환&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;book2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;장점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90&quot; aria-label=&quot;장점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;같은 자원을 사용하려는 여러 클라이언트가 의존 객체들을 안심하고 공유할 수 있다.&lt;/li&gt;
&lt;li&gt;정적 팩토리, 빌더 모두에 동일하게 적용 가능하다.&lt;/li&gt;
&lt;li&gt;클래스의 테스트, 유연성, 재사용성등을 개선해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;싱글톤의-구현-방식-6가지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%B1%EA%B8%80%ED%86%A4%EC%9D%98-%EA%B5%AC%ED%98%84-%EB%B0%A9%EC%8B%9D-6%EA%B0%80%EC%A7%80&quot; aria-label=&quot;싱글톤의 구현 방식 6가지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;싱글톤의 구현 방식 6가지&lt;/h2&gt;
&lt;p&gt;위와 같은 기본적인 규칙을 기반으로, 지금까지 싱글톤의 구현 방식이 역사적으로 진화해왔다. 6가지 형태로 진화해왔는데, 각 구현 방식에 대해 간단히 살펴보도록 한다.&lt;/p&gt;
&lt;h3 id=&quot;early-initalization&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#early-initalization&quot; aria-label=&quot;early initalization permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Early Initalization&lt;/h3&gt;
&lt;p&gt;가장 먼저 등장한 싱글톤 구현 방식으로, &lt;code class=&quot;language-text&quot;&gt;즉시 초기화(Eager Initalization)&lt;/code&gt; 방식이다. 이 방식은 필드에 자기자신 타입의 인스턴스를 필드로 하나 보유하고, 바로(즉시) 초기화하는 방식이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;public class MoveRepository {
    private static final MoveRepository instance = new MoveRepository();

    private MoveRepository() {
    }

    public static MoveRepository getInstance(){
        return instance;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;장점&lt;/h4&gt;
&lt;p&gt;인스턴스 생성이 굉장히 빠르다.&lt;/p&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;p&gt;생성한 인스턴스를 사용하는 일이 없더라도, 프로그램을 실행하면 static 을 로딩하면서 바로 메모리 공간을 차지해버린다. 즉, 클라이언트에서 이렇게 생성된 인스턴스를 사용하지 않더라도 인스턴스가 항상 무조건 생성되버리며, 무엇보다 예외 처리를 할 수 있는 방법이 없다.&lt;/p&gt;
&lt;h3 id=&quot;static-block-initalization&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#static-block-initalization&quot; aria-label=&quot;static block initalization permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Static Block Initalization&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RunTimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;싱글톤 객체를 생성하는데 실패했습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 단점중에 &quot;에외처리&quot; 문제를 보완하고자, &lt;code class=&quot;language-text&quot;&gt;static&lt;/code&gt; 블록을 추가하고, 그 안에서 인스턴스 생성을 시도하면서 동시에 예외 처리를 넣는 방식이 등장했다. 이로써 인스턴스 생성시 즉시 예외처리가 가능해진다는 점을 보완했다. 하지만 여전히 처음처음 시작할 떄 바로 초기화 된다는 단점을 가지고 있다.&lt;/p&gt;
&lt;h3 id=&quot;lazy-initalization&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lazy-initalization&quot; aria-label=&quot;lazy initalization permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Lazy initalization&lt;/h3&gt;
&lt;p&gt;따라서 컴파일 로딩 시점에 &quot;즉시&quot; 인스턴스를 생성하려는 행위를 방지하고자, 지연 초기화 방식이 등장했다. 이는 처음에 필드를 생성할 떄는 초기화하지 않고, &lt;code class=&quot;language-text&quot;&gt;getInstance()&lt;/code&gt; 메소드를 호출하게 되면 그제서야 검사를 시도한다. 아래처럼 인스턴스가 null 이라면, 해당 인스턴스를 초기화를 시도한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;instance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;장점&lt;/h4&gt;
&lt;p&gt;이로써 즉시 인스턴스가 생성되는 것이 아니라, 클라잉너트가 인스턴스를 사용하려고 하는 시점에 천천히 초기화 및 인스턴스가 생성되므로, 메모리를 절약할 수 있다.&lt;/p&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;p&gt;하지만 &lt;code class=&quot;language-text&quot;&gt;thread safe&lt;/code&gt; 하지 않다. 만약 여러개의 쓰레드가 동시에 요청하게 되면 싱글톤의 &lt;code class=&quot;language-text&quot;&gt;유일성(unique)&lt;/code&gt; 를 보장한다는 특성을 어길 수 있게된다. 즉, 멀티쓰레드 환경에서 동시에 인스턴스 생성을 요청하는 상황에 발생하면 다중 인스턴스가 생길 수 있게된다.&lt;/p&gt;
&lt;h3 id=&quot;thread-safe-initalization&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#thread-safe-initalization&quot; aria-label=&quot;thread safe initalization permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Thread-safe initalization&lt;/h3&gt;
&lt;p&gt;앞선 지연 초기화 방법은 thread safe 하지 않으므로, 이를 보완할 수 있게 thread safe 한 초기화 방법이 등장했다. 자바에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;synchornized&lt;/code&gt; 키워드를 활용하는 방식으로, 쓰레드가 이 인스턴스를 생성하는 &lt;code class=&quot;language-text&quot;&gt;임계영역(Critical Section)&lt;/code&gt; 에 진입할 때 순차적으로 진입할 수 있도록 하는 방식이다. &lt;strong&gt;즉, 동시에 여러 쓰레드가 인스턴스 생성 메소드에 접근하지 못하도록 하나의 인스턴스만 생성됨을 보장한다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;instance &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;장점&lt;/h4&gt;
&lt;p&gt;Thread Safe 하므로 유일성이 보장된다.&lt;/p&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;p&gt;하지만 &lt;code class=&quot;language-text&quot;&gt;synchornized&lt;/code&gt; 키워드를 사용함으로 인해 성능이 저하될 수 있다. 하나릐 쓰레드만 초기화 임계영역에 접근 가능하기 떄문이다.&lt;/p&gt;
&lt;h3 id=&quot;double-checked-locking&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#double-checked-locking&quot; aria-label=&quot;double checked locking permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Double-Checked Locking&lt;/h3&gt;
&lt;p&gt;그래서 등장한 것이 2번 체크를 하는 방식입니다. 첫번째로 인스턴스가 null 인지를 체크합니다. 조건문에서 인스턴스가 이미 생성되었는지 아닌지를 체크하고, 생성된게 아직 없는 경우에만 &lt;code class=&quot;language-text&quot;&gt;synchornized&lt;/code&gt; 키워드에 기반하여 &lt;code class=&quot;language-text&quot;&gt;쓰레드 안전(Thread Safe)&lt;/code&gt; 하게 인스턴스를 생성하는 방식입니다. 즉, if 문으로 앞단에서 먼저 필터링하여 &lt;code class=&quot;language-text&quot;&gt;synchornized&lt;/code&gt; 가 최소한으로 사용되게 하는 방식인 것으로, 이전 방식보다 성능이 더 좋아지게됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;instance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;instance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      	&lt;span class=&quot;token keyword&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;instance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            	instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;장점&lt;/h4&gt;
&lt;p&gt;synchornized 로 인한 성능 저하 비용이 최소화되었으며, 쓰레드 안전하게 &quot;유일성&quot; 의 특징을 지닌 인스턴스 생성이 가능하다.&lt;/p&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;p&gt;가독성이 떨어진다. 메소드 내부의 코드가 많아졌으며 인덴트도 늘어난다. (위 경우 인덴트가 3이다.)&lt;/p&gt;
&lt;h3 id=&quot;bill-pugh-solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bill-pugh-solution&quot; aria-label=&quot;bill pugh solution permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Bill Pugh Solution&lt;/h3&gt;
&lt;p&gt;위의 가독성 저하 문제를 보완하고자, &lt;code class=&quot;language-text&quot;&gt;정적 내부 클래스(static inner class)&lt;/code&gt; 클래스를 만들어서 &lt;code class=&quot;language-text&quot;&gt;핼퍼 클래스(Helper Class)&lt;/code&gt; 로써 동작하ㅔ 만들었다. 이 헬퍼 클래스 안에 인스턴스를 &lt;code class=&quot;language-text&quot;&gt;static final&lt;/code&gt; 로 보유하고 있다. &lt;code class=&quot;language-text&quot;&gt;static&lt;/code&gt; 의 특징으로 인해 메모리에 미리 할당했으며, 내부의 인스턴스 변수도 &lt;code class=&quot;language-text&quot;&gt;final&lt;/code&gt; 을 붙여서 &lt;code class=&quot;language-text&quot;&gt;불변성&lt;/code&gt; 의 특징을 보장한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SingleTonHelper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;INSTANCE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SingleTonHelper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;INSTANCE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이로써 인스턴스가 &lt;code class=&quot;language-text&quot;&gt;getInstance&lt;/code&gt; 를 활용하여 호출될 때, 동시에 여러 쓰레드가 호출되더라도 메모리에 미리 올라간 동일한 인스턴스를 헬퍼 클래스를 통해 전달받기 떄문에, 동시성 문제도 깔끔히 해결되고 코드의 가독성도 개선되었다.&lt;/p&gt;
&lt;p&gt;따라서 여러 쓰레드로 인한 동시성 문제도 해결할 수 있게 됩니다. 또한 메소드 길이도 짧아지고 깔끔해졌다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=C6CczyrkYXU&quot;&gt;https://www.youtube.com/watch?v=C6CczyrkYXU&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2020-11-07-singleton/&quot;&gt;https://tecoble.techcourse.co.kr/post/2020-11-07-singleton/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kephilab.tistory.com/50&quot;&gt;https://kephilab.tistory.com/50&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bangu4.tistory.com/286&quot;&gt;https://bangu4.tistory.com/286&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[DTO(Data Transfer Object) 의 올바른 사용 방식에 대해]]></title><description><![CDATA[학습배경 이전부터 자바 및 스프링부트로 프로그래밍을 하면서 밥 먹듯이 DTO 를 많이 들어왔고, 사용해왔다. 하지만 DTO 를 올바른 정의대로 바람직하게 사용하고 있다는 확신이 들지 않았다. 오늘 카카오테크 교육과정에서 맥(mac…]]></description><link>https://haon.site/java/dto/</link><guid isPermaLink="false">https://haon.site/java/dto/</guid><pubDate>Mon, 22 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;이전부터 자바 및 스프링부트로 프로그래밍을 하면서 밥 먹듯이 DTO 를 많이 들어왔고, 사용해왔다. 하지만 DTO 를 올바른 정의대로 바람직하게 사용하고 있다는 확신이 들지 않았다. 오늘 카카오테크 교육과정에서 맥(mac) 코치님의 서버 기초 강의를 들으면서 DTO 를 다시금 배웠는데, 이번 기회에 확실히 정리해보고자 글을 작성한다.&lt;/p&gt;
&lt;h2 id=&quot;dto-data-transfer-object&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dto-data-transfer-object&quot; aria-label=&quot;dto data transfer object permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DTO (Data Transfer Object)&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DTO&lt;/code&gt; 란 &lt;strong&gt;계층 간 데이터를 전송하기 위해 도메인 모델 대신에 사용하는 객체&lt;/strong&gt;를 뜻한다. DTO 는 순수하게 데이터르 ㄹ저장하고, 데이터에 대한 getter 와 setter 만을 가져야 한다. 그 어떠한 비즈니스 로직을 가져서도 안되며, 오르지 저장, 검색등의 로직만을 보유할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;publci &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserInfoDto&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserInfoDto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 작성한 클래스는 DTO 라고 할 수 있겠는가? 적어도 앞서 정의한 내용에 따르면 맞는 말이다. 어떠한 비즈니스 로직을 갖고있지 않으며, 단순히 값을 저장하고 제공하는 로직만을 포함하고 있다. 오로지 데이터를 전송하는 단순 캡슐화 객체 그 자체로써 동작할 것이다.&lt;/p&gt;
&lt;h2 id=&quot;dto-를-왜-사용하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dto-%EB%A5%BC-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;dto 를 왜 사용하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DTO 를 왜 사용하는가?&lt;/h2&gt;
&lt;h3 id=&quot;도메인-모델을-캡슐화하여-보호할-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EB%A9%94%EC%9D%B8-%EB%AA%A8%EB%8D%B8%EC%9D%84-%EC%BA%A1%EC%8A%90%ED%99%94%ED%95%98%EC%97%AC-%EB%B3%B4%ED%98%B8%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;도메인 모델을 캡슐화하여 보호할 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도메인 모델을 캡슐화하여 보호할 수 있다.&lt;/h3&gt;
&lt;p&gt;DTO 를 사용하는 가장 큰 목적은 &lt;strong&gt;도메인 모델을 보호하는 것 이다.&lt;/strong&gt;. 만약 DTO 가 아닌 도메인 모델을 계층간의 데이터 송수신에 직접 활용하면 어떤 문제가 발생할까? 당연하게도, Business 계층을 비롯한 Persistence 계층, Presenataion 계층 모두에게 도메인 모델을 노출시키기 떄문에 다른 계층과의 강한 결합 및 의존성이 발생하게 된다. 즉, 추후 요구사항 변화로 인해 다른 계층에 전송해야하는 데이터 스팩이 변경되어야 하면, 매번 도메인 코드를 변경해야하는 상황이 발생할 수 있다. &lt;strong&gt;즉, DTO 를 활용하면 도메인 모델을 계층간의 전송에 직접 활용하지 않으므로 보호할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;도메인-서례를-외부에-직접-유출시키지-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%84%9C%EB%A1%80%EB%A5%BC-%EC%99%B8%EB%B6%80%EC%97%90-%EC%A7%81%EC%A0%91-%EC%9C%A0%EC%B6%9C%EC%8B%9C%ED%82%A4%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;도메인 서례를 외부에 직접 유출시키지 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도메인 서례를 외부에 직접 유출시키지 않는다.&lt;/h3&gt;
&lt;p&gt;앞서 서술했듯이, DTO 는 도메인 모델을 보호하는 것에 목적이 있다. 비슷한 이유로, &lt;strong&gt;도메인 모델 대신 DTO 를 활용함으로써 보안 문제를 한층 해결할 수 있다.&lt;/strong&gt; 만약 DTO 가 아닌 도메인 모델을 계층 간 전달에 사용하면, 프레젠테이션 계층에서 도메인 모델의 상태를 변경시킬 수 있고, 중요한 내부 필드들이 불필요하게 모두 노출된다. 이 떄문에 DTO 를 대신 활용하여 필요한 일부 데이터 만을 외부에 노출시킴으로써 도메인을 보호할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;3-검증validation-코드를-도메인-코드와-분리-가능하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%EA%B2%80%EC%A6%9Dvalidation-%EC%BD%94%EB%93%9C%EB%A5%BC-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%BD%94%EB%93%9C%EC%99%80-%EB%B6%84%EB%A6%AC-%EA%B0%80%EB%8A%A5%ED%95%98%EB%8B%A4&quot; aria-label=&quot;3 검증validation 코드를 도메인 코드와 분리 가능하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 검증(Validation) 코드를 도메인 코드와 분리 가능하다.&lt;/h3&gt;
&lt;p&gt;Domain Layer 의 역할을 다시 복습해보자. 이 계층은 비즈니스 로직을 처리하는 핵심 계층으로, 시스템의 핵심 로직이 담겨있다. 주요 역할로 데이터의 유효성 검증, 엔티티 간의 관계처리, 비즈니스 로직 수행등을 수행한다고 했다. &gt; 💡 Domain Layer 에 대한 역할은 &lt;a href=&quot;https://haon.blog/spring/layered-architecture/&quot;&gt;스프링의 독립적인 계층화 아키텍처 (Layered Architecture)&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;p&gt;따라서 도메인 모델은 매우 중요한 정책, 비즈니스 정책이 흐르는 모델로, 외부에서 받아온 필드에 대한 자잘한 유효성 검증 로직은 DTO 에서 처리하는 것이 바람직하다. 여기서 유의할 점은 도메인 모델에서 데이터 필드 검사를 수행 안한다는 것이 아니다. 도메인 모델에선 시스템의 비즈니스 정책(ex. 유저의 비밀번호는 최대 10자를 허용하는 정책) 에 대한 유효성 검증을 수행한다. 반면 DTO 는 이 외의 자잘한 데이터 검증을 처리한다.&lt;/p&gt;
&lt;h2 id=&quot;dto-를-어떤-계층에서까지-사용할-수-있을까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dto-%EB%A5%BC-%EC%96%B4%EB%96%A4-%EA%B3%84%EC%B8%B5%EC%97%90%EC%84%9C%EA%B9%8C%EC%A7%80-%EC%82%AC%EC%9A%A9%ED%95%A0-%EC%88%98-%EC%9E%88%EC%9D%84%EA%B9%8C&quot; aria-label=&quot;dto 를 어떤 계층에서까지 사용할 수 있을까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DTO 를 어떤 계층에서까지 사용할 수 있을까?&lt;/h2&gt;
&lt;p&gt;적어도 대다수의 의견은 DTO 가 영속성 계층까지 도달하는 것은 지양한다. 그렇다면, 우리가 집중해서 살펴봐야할 부분은 비즈니스 계층과 프레젠테이션 계층이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A Service Layer defines an application&apos;s boundary [Cockburn PloP] and its set of available operations from the perspective of interfacing client layers. It encapsulates the application&apos;s business logic, controlling transactions and coor-dinating responses in the implementation of its operations. - 마틴 파울러 -&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;마틴 파울러는 Service 계층은 도메인을 보호하는 계층이라고 말한다. 적어도 그의 말에 따르면, 프레젠테이션 계층까지 도메인을 유출해선 안되며, &lt;strong&gt;도메인은 서비스 계층에서 DTO 로 변환되어 프레젠테이션 계층으로 넘겨지는 것이 올바르다.&lt;/strong&gt; 반대로 프레젠테이션 계층에서 비즈니스 계층으로 데이터가 전달될 떄도 DTO 형태로전달되는 것이 바람직하다.&lt;/p&gt;
&lt;h2 id=&quot;dto-entity-변환-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dto-entity-%EB%B3%80%ED%99%98-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;dto entity 변환 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DTO, Entity 변환 방식&lt;/h2&gt;
&lt;p&gt;그렇다면 DTO 와 도메인 객체(엔티티) 간의 변환은 어떤 방식으로 이루어질까?&lt;/p&gt;
&lt;h2 id=&quot;dto-mapper&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dto-mapper&quot; aria-label=&quot;dto mapper permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DTO Mapper&lt;/h2&gt;
&lt;p&gt;첫번째로 DTO와 도메인 모델의 변환은 별도의 Mapper 객체를 사용하는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c1b0540793ec68f586192cdbe9d0ac49/d4b10/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 19.631901840490798%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA80lEQVR42pWLyUrDUABF87XSha4E9/2IIoq4cqEg1ao4LgodBBtxKGLT0hKSJjF5L3mpeYkT6jFFf8ADZ3EPXOMLmPvJL4XW9Icetnj5K9/8ByN5aCMu6yS9A3zHJkw0MvC5O93D6zZQVo8iVOggRN43kb0jVL9Jkihm+QexZSKuDpHmMUUqMKKTGv7mEmp7hU7LpN59YnR7Tbi1jLtewW7UOGu7tDsW3k4VZ61CtFvlotznNwmj/RqT1QWcjUVS5xFDCw/tj0snxFISxSnPKibzhuipVXabV6FKZdnG5NMBeTAhnWVkxRv5/O8OyFyL90LzA2+9IF1t9hBIAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/c1b0540793ec68f586192cdbe9d0ac49/a6d36/image.png&quot;
        srcset=&quot;/static/c1b0540793ec68f586192cdbe9d0ac49/222b7/image.png 163w,
/static/c1b0540793ec68f586192cdbe9d0ac49/ff46a/image.png 325w,
/static/c1b0540793ec68f586192cdbe9d0ac49/a6d36/image.png 650w,
/static/c1b0540793ec68f586192cdbe9d0ac49/e548f/image.png 975w,
/static/c1b0540793ec68f586192cdbe9d0ac49/3c492/image.png 1300w,
/static/c1b0540793ec68f586192cdbe9d0ac49/d4b10/image.png 1394w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;일반적인 상황에선, 대부분 위처럼 컨트롤러는 DTO 형태의 데이터로 요청을 서비스 계층에 넘기고, 서비스는 DTO 를 엔티티(도메인 모델) 로 변환하는 구조를 지닌다. 만약 View 계층으로 부터 전달받은 데이터가 DTO 타입이 아니라면, DTO 로 변환후 서비스 계층에 전달해야한다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dbb0f36b90ae787590b581e2eb3d6765/062c8/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17.177914110429448%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA1ElEQVR42iWL2y/DUADG+8+TPTHjkXkTYU0EE5c1DMsmjTmtWpZ1MaI9a7Ux2d7Wc/pz8PBd8l2s+XyB1ppCKVSxJMty3BfJbLGkBLQqDBtXGhivCoPfrdKUWplc//21+ldL9q+J7Qr5+RYD4fEYfjMRPd6P1pDNGq+ex+34Cqdf520yRbYPiexVUmcXfxgTjD+JL3eIGiukHRtr6t+Rnm0yu6kTiIDu8IvouUfSrBIfrzN6cGmJBhdimzD8IHEOTLdBfr/P00DijzKS9h7ypErmnvIDIBLT+WNuDJwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/dbb0f36b90ae787590b581e2eb3d6765/a6d36/image-1.png&quot;
        srcset=&quot;/static/dbb0f36b90ae787590b581e2eb3d6765/222b7/image-1.png 163w,
/static/dbb0f36b90ae787590b581e2eb3d6765/ff46a/image-1.png 325w,
/static/dbb0f36b90ae787590b581e2eb3d6765/a6d36/image-1.png 650w,
/static/dbb0f36b90ae787590b581e2eb3d6765/e548f/image-1.png 975w,
/static/dbb0f36b90ae787590b581e2eb3d6765/3c492/image-1.png 1300w,
/static/dbb0f36b90ae787590b581e2eb3d6765/062c8/image-1.png 1386w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;반면 서비스 요청/응답시 서비스 계층의 메소드가 사용하는 서비스 DTO 를 별도로 만들고, DTO 와 서비스 DTO 를 매핑하는 Mapper(매퍼) 를 중간에 끼워넣는 방식도 있다고 한다. Mapper 를 사용하면 컨트롤러와 서비스 계층이 완전히 분리됨으로써 결함도가 낮아지는 효과를 볼 수 있다고 한다. 아직은 실제로 Mapper 를 활용하면서 까지 코드를 작성해본 경험이 없기 떄문에 공감대가 없어서, 추가 학습이 필요하다.&lt;/p&gt;
&lt;p&gt;스프링부트 JPA 에선 &lt;code class=&quot;language-text&quot;&gt;ModelMapper&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;MapStruct&lt;/code&gt; 라는 매퍼 기능을 이미 제공하고 있다는 점을 처음 알았다. 이들도 시간이 된다면 학습해보도록 한다.&lt;/p&gt;
&lt;h3 id=&quot;toentity-todto&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#toentity-todto&quot; aria-label=&quot;toentity todto permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;toEntity, toDto&lt;/h3&gt;
&lt;p&gt;사실 지금까지 내가 사용했던 방식이다. 아래처럼 DTO (또는 Entity) 내에 변환 메소드를 구현하는 방식이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDto&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이런 방식으로 매퍼 역할을 수행하는 변환 메소드를 구현하는 것이다. 그런데 DTO 의 엄밀한 정의에 따르면 올바르지 않은 코드다.&lt;/p&gt;
&lt;p&gt;그럼에도 뛰어난 개발자분들 코드를 살펴보면 대부분 Mapper 를 별도로 구현하지 않고도 위처럼 간단히 변환 메소드를 간단히 구현한다. 이 이유는 많은 고민을 해봤지만 아직  결론을 내리지 못했다. 추가 학습이 필요하다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/&quot;&gt;https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2020-08-31-dto-vs-entity/&quot;&gt;https://tecoble.techcourse.co.kr/post/2020-08-31-dto-vs-entity/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/java-dto-pattern&quot;&gt;https://www.baeldung.com/java-dto-pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://creampuffy.tistory.com/188&quot;&gt;https://creampuffy.tistory.com/188&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/data-transfer-object/&quot;&gt;https://hudi.blog/data-transfer-object/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[REST(Representational State Transfer) API 설계원칙]]></title><description><![CDATA[REST API 에 대해 이해하기 위해, 먼저 API 그 자체에 대한 이론부터 다시금 되짚어보자. API API 란 Application Programming Interface…]]></description><link>https://haon.site/network/rest-api/</link><guid isPermaLink="false">https://haon.site/network/rest-api/</guid><pubDate>Fri, 19 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;REST API 에 대해 이해하기 위해, 먼저 API 그 자체에 대한 이론부터 다시금 되짚어보자.&lt;/p&gt;
&lt;h2 id=&quot;api&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#api&quot; aria-label=&quot;api permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;API&lt;/h2&gt;
&lt;p&gt;API 란 Application Programming Interface 의 약자로, &lt;strong&gt;소프트웨어 사이에 요청과 응답을 주고받을 떄    데이터 통신을 하는 방법과 규칙을 뜻한다.&lt;/strong&gt;  웹 프로그래밍 차원에서의 API 는 서버와 클라이언트간에 어떻게 통신할 것인지에 대한 일정한 규칙에 따라 다양한 종류로 구분된다. SOAP, RPC, REST 등 다양한 타입이 존재한다. 그 중 우리는 REST API 라는 API 의 다양한 종류 중 하나를 살펴볼것이다.&lt;/p&gt;
&lt;p&gt;API 는 웹 애플리케이션 차원에서 제공하는 표준 스팩도 존재하지 만, 이외에도 API 는 OS 에서도 제공할 수 있고, 프로그래밍 언어에서도 제공할 수 있는 개념이다. 그 중 우리는 웹 애플리케이션 차원에서의 API 에 대해 학습하도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;rest-api&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rest-api&quot; aria-label=&quot;rest api permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;REST API&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/088e1da3ae1fc37a450e2e9d54c7aaaa/11b93/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.05521472392638%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACU0lEQVR42m1SXUhTYRj+Tm6KgRF1IfYD2Q9ZTi0YZFSsco3UsLnVcmbMaVYm2ayJ0qatX13Tam6py8UW6YrsonKVXQTVVaOujC4SL4qCroIIoth29vSdb2cHpr7w8sJ7vvN8z89HMKsS4ozzPCJTn+EJPYPv4SQ+zXxle55PIBaLI5FIYL4iaWDioZ+/fuOQ1QWyvgrcmkqQ1RWQF+lw1hVgYKmz84GS2cyE0rf2gCxXQ77JAFnJQcjozFDoGLCm6Twau714/vZDGok5gIJEoV5HpkDW7UNG8QFwlBVRJJsr0mOBsMsvZ5eRtZWYePM++W+cnwsYFaVcG3kEskJNAfXgCkXADfvBsallU77ZwECNrb2ir7ykkAHyIu0wvTFfcwyLt9YhW1mDLMqIbNQip/Qw5IXVWLSlFjm0FyqNyFXVY+m2I7BSX6PRmCSfpMBmvv1AZkEVCsqOwj4who7rQdjc99DkGER7XwAnLw2jzeln/npDYbq/hcbOmyBZSvQHH0vSSSyelBoYfwmSuxOGMy7UWfsweD8M3/gkuj0hWHr82GW2wz36FKW1HbA47yBvhwkObwhk1V6oTTYpWJIy9MmrdwxQVW9He38QZpsbJy4MUraj6LxxFyrTOQyMTdCUHaihT6pYZ4GNfiNLtsPY5pTeKEmlLqSsbuhCNg1gj9kGsqwM3EoNOJqmTFGNTBqSnFoi7EnebijKm1FS0Uy91SLycVoKhyTNTIL++fsPQw9eoKHLw2Sd7vXj1NURtFz2oeWK0LfZXujjF4eYt9Nfvqe9x//NP3GfyPFzKwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/088e1da3ae1fc37a450e2e9d54c7aaaa/a6d36/image.png&quot;
        srcset=&quot;/static/088e1da3ae1fc37a450e2e9d54c7aaaa/222b7/image.png 163w,
/static/088e1da3ae1fc37a450e2e9d54c7aaaa/ff46a/image.png 325w,
/static/088e1da3ae1fc37a450e2e9d54c7aaaa/a6d36/image.png 650w,
/static/088e1da3ae1fc37a450e2e9d54c7aaaa/e548f/image.png 975w,
/static/088e1da3ae1fc37a450e2e9d54c7aaaa/11b93/image.png 1124w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;REST API 는 &lt;code class=&quot;language-text&quot;&gt;Representational State Transfer API&lt;/code&gt; 의 약자다. REST 란 소프트웨어 프로그래밍 아키텍처의 한 형식이다. 쉽게말해, REST 란 웹 애플리케이션 차원에서 클라이언트와 서버 사이에 데이터를 통신시 정해놓은 특정한 통신 규약이라고 할 수 있다.&lt;/p&gt;
&lt;p&gt;REST API 란 REST 라는 아키텍처의 제약 조건을 준수한 API 를 뜻한다. 이 REST 제약 조건을 준수한 API 를 Restful 하다고 표현한다.&lt;/p&gt;
&lt;p&gt;그리고 유의할 점은, API 를 구현할 때 REST 라는 규칙에 따라서만 정의할 수 있는것이 아니다. REST 란 표준으로 정해진 것이 아니며, API 를 정의시 일반적으로 통용되는 규약이다. 따라서 API 를 구현시 REST 규약을 반드시 시킬 필요는 없으며, 단지 권장 사항이다.&lt;/p&gt;
&lt;p&gt;또한 REST API 는 HTTP 프로토콜을 규악한 내용이므로, Restful 한 API 는 반드시 &lt;code class=&quot;language-text&quot;&gt;Stateless&lt;/code&gt; 하게 동작해야한다. 따라서 GET 요청들은 서로 연결되어 있지 않은 상태이어야 한다. 즉 요청들간에는 서로 독립적으로 동작해야한다.&lt;/p&gt;
&lt;h3 id=&quot;rest-의-구성-요소&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rest-%EC%9D%98-%EA%B5%AC%EC%84%B1-%EC%9A%94%EC%86%8C&quot; aria-label=&quot;rest 의 구성 요소 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;REST 의 구성 요소&lt;/h3&gt;
&lt;h4&gt;자원(Resource) by URI&lt;/h4&gt;
&lt;p&gt;REST API 는 URI 를 통해 사용할 자원을 명시한다. 보통은 이떄 URI 에 명시되는 자원은 PK 와 같은 고유한 ID 값이다.&lt;/p&gt;
&lt;h4&gt;행위(Verb) by HTTP Method&lt;/h4&gt;
&lt;p&gt;REST API 는 HTTP Method (GET, POST, PUT, DELETE 등) 를 사용하여 URI 에 명시된 자원에 대해 어떠한 행위를 수행할 것인지를 명시한다.&lt;/p&gt;
&lt;h4&gt;표현(Representation of Resource)&lt;/h4&gt;
&lt;p&gt;REST API 에서 리소스는 다양한 형태(JSON, XML, TEXT, RSS 등)로 표현될 수 있다. 보통은 JSON 또는 XML 을 통해 데이터를 주고받는 것이 일반적이다.&lt;/p&gt;
&lt;h2 id=&quot;uri-설계-규칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#uri-%EC%84%A4%EA%B3%84-%EA%B7%9C%EC%B9%99&quot; aria-label=&quot;uri 설계 규칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;URI 설계 규칙&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 슬래시 구분자 &quot;/&quot; 는 계층 관계를 나타내는데 사용한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; URI 마지막 문자로 슬래시 &quot;/&quot; 를 포함하지 않는다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;REST API 는 분명한 URI 를 만들어 통신해야 하기 떄문에 혼동을 주지 않도록 URI 마지막에는 슬래시를 사용하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 하이픈 &quot;-&quot; 은 URI 가독성을 높이는데 사용한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;불가피하게 긴 URI 경로를 사용하게 된다면 하이픈을 사용하여 가득성을 높이도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(4)&lt;/code&gt; 밑줄 &quot;_&quot; 은 URI 에 사용하지 않는다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;밑줄은 보기 어렵거나 밑줄 떄문에 문자가 가려지기도 한다. 따라서 가독성을 위해 밑줄은 사용하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(5)&lt;/code&gt; URI 경로에는 소문자가 적합하다. 대문자 사용은 피하도록 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RFC 3986(URI 문법 형식) 은 URI 스키마와 호스트를 제외하고는 대소문자를 구별하도록 규정하기 떄문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(6)&lt;/code&gt; REST API 에서는 Message Body 내용의 포맷을 나타내기 위한 파일 확장자를 URI 안에 포함시키지 않는다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Accept Header 를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;http-메소드-설계-규칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%84%A4%EA%B3%84-%EA%B7%9C%EC%B9%99&quot; aria-label=&quot;http 메소드 설계 규칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP 메소드 설계 규칙&lt;/h2&gt;
&lt;p&gt;HTTP Method 는 어떻게 설계하는가? 앞서 말했듯이, HTTP Method 는 URI 에 명시된 해당 리로스에 대한 행위를 표현하는 것이 주 목적이다. 따라서 URI 에 get, post, .. 등을 명시하여 행위를 표현하지 않아야 한다. 이 대신 행위는 HTTP Method 에 표현하는 것이다.&lt;/p&gt;
&lt;p&gt;만약 유저 리스트를 받아오고 싶은 상황에서 API 를 설계한다면, URI 를 &quot;/getUser&quot; 로 명시하는 대신에 &quot;/user&quot; 로 명시하고, HTTP 메소드로 GET 요청을 선택하자.&lt;/p&gt;
&lt;p&gt;이어서 HTTP 메소드를 아래와 같은 행위에 맞도록 선택하여 디자인하자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt; : URI 에 명시된 리소스에 대한 정보를 가져올 때 사용한다. 일반적인 경우 HTTP 상태코드 값을 200을 반환한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;POST&lt;/code&gt; : 명시된 URI 에 새로운 리소스를 생성한다. 리소스가 성공적으로 생성되었다면 HTTP 상태코드 값을 201을 반환한다. 딱히 반환할 결과가 없다면 204를 반환한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;PUT&lt;/code&gt; : 명시된 URI 를 새로운 리소스로 대체하거나, 없다면 새로운 리소스를 생성한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;PATCH&lt;/code&gt; : 명시된 URI 에 해당하는 리소스의 일부분 업데이트 한다. 또한 PUT 은 리소스의 모든 정보를 업데이트한다는 특징이 있는 반면, PATCH 는 리소스의 일부분만을 업데이트한다는 특징이 존재한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DELETE&lt;/code&gt; : 명시된 URI 에 해당하는 리소스를 제거한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;삭제 성공시 빈 본문을 반환하고, 상태코드값 204를 반환한다. 존재하지 않는 리소스에 요청한 경우 404를 반환한다.&lt;/p&gt;
&lt;h2 id=&quot;rest-api에서의-http-상태-코드-상태-메시지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rest-api%EC%97%90%EC%84%9C%EC%9D%98-http-%EC%83%81%ED%83%9C-%EC%BD%94%EB%93%9C-%EC%83%81%ED%83%9C-%EB%A9%94%EC%8B%9C%EC%A7%80&quot; aria-label=&quot;rest api에서의 http 상태 코드 상태 메시지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;REST API에서의 HTTP 상태 코드, 상태 메시지&lt;/h2&gt;
&lt;h3 id=&quot;200번대&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#200%EB%B2%88%EB%8C%80&quot; aria-label=&quot;200번대 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;200번대&lt;/h3&gt;
&lt;p&gt;200번대 상태 코드들은 서버가 클라이언트의 요청을 성공적으로 처리했다는 뜻이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;200&lt;/code&gt; OK : 클라이언트 요청을 서버가 정상적으로 처리했다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;201&lt;/code&gt; Created : 클라이언트의 요청을 서버가 정상적으로 처리했고, 새로운 리소스가 생겼다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;202&lt;/code&gt; Accepted : 클라이언트의 요청은 정상적이지만, 서버가 요청을 완료하지 못했다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;204&lt;/code&gt; No Content : 클라이언트의 요청은 정상적이다. 하지만 컨텐츠를 제공하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;400번대&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#400%EB%B2%88%EB%8C%80&quot; aria-label=&quot;400번대 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;400번대&lt;/h3&gt;
&lt;p&gt;400번대 상태 코드들은 클라이언트의 요청이 유효하지 않아서, 서버가 해당 요청을 수행하지 않았다는 뜻이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;400&lt;/code&gt; Bad Request : 클라이언트의 요청이 유효하지 않아서, 더 이상 작업을 진행하지 않는다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;401&lt;/code&gt; Unauthorized : 클라이언트가 인증이 되지 않아서 작업을 진행할 수 없는 경우&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;403&lt;/code&gt; Forbidden : 클라이언트가 인증이 됐으나 권한이 없기 때문에 작업을 진행할 수 없는 경우&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;404&lt;/code&gt; Not Found : 클라이언트가 요청한 자원이 존재하지 않는다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;405&lt;/code&gt; Method Not Allowed : 클라이언트의 요청이 허용되지 않는 메소드인 경우&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;409&lt;/code&gt; Conflict : 클라이언트의 요청이 서버의 상태와 충돌이 발생한 경우&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;429&lt;/code&gt; Too Many Requests : 클라이언트아 일정 시간동안 너무 많은 요청을 보낸 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;500번대&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#500%EB%B2%88%EB%8C%80&quot; aria-label=&quot;500번대 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;500번대&lt;/h3&gt;
&lt;p&gt;500번대 상태 코드들은 서버 오류로 인해 요청을 수행할 수 없다는 뜻이다. 클라이언트의 요청은 유효하여 작업을 진행했는데, 도중에 오류가 발생한 경우다.&lt;/p&gt;
&lt;p&gt;API 서버의 응답에서 500번대 오류가 발생해서는 안되다. 보통 개발 과정에서 유효하지 않은 요청을 사전에 처리하지 않은 경우 400번대에서 많이 발생한다. &lt;strong&gt;API 서버 차원에서 완벽한 예외처리를 통해 500 번대 서버 오류 상태 코드를 방지해야 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;500&lt;/code&gt; Internal Server Error : 서버에 대한 클라이언트와 소통을 하지 않는것이 보안적인 측면에서 더 좋기 떄문에, 서버 에러를 대부분 &lt;code class=&quot;language-text&quot;&gt;500&lt;/code&gt; 으로 통일해서 응답한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://jaeseongdev.github.io/development/2021/04/22/REST_API%EC%97%90%EC%84%9C%EC%9D%98_HTTP_%EC%83%81%ED%83%9C%EC%BD%94%EB%93%9C_%EC%83%81%ED%83%9C%EB%A9%94%EC%8B%9C%EC%A7%80.md/&quot;&gt;https://jaeseongdev.github.io/development/2021/04/22/REST_API%EC%97%90%EC%84%9C%EC%9D%98_HTTP_%EC%83%81%ED%83%9C%EC%BD%94%EB%93%9C_%EC%83%81%ED%83%9C%EB%A9%94%EC%8B%9C%EC%A7%80.md/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/rest-api/&quot;&gt;https://hudi.blog/rest-api/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://khj93.tistory.com/entry/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-REST-API%EB%9E%80-REST-RESTful%EC%9D%B4%EB%9E%80&quot;&gt;https://khj93.tistory.com/entry/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-REST-API%EB%9E%80-REST-RESTful%EC%9D%B4%EB%9E%80&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://junvelee.tistory.com/107&quot;&gt;https://junvelee.tistory.com/107&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.redhat.com/ko/topics/api/what-is-a-rest-api&quot;&gt;https://www.redhat.com/ko/topics/api/what-is-a-rest-api&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[데이터베이스 트랜잭션(Transaction)]]></title><description><![CDATA[학습동기 최근 카카오테크 교육과정의 실습 미션을 진행하면서, 이전부터 사용해왔던  어노테이션을 다시금 사용하게 되었다. 사실 트랜잭션을 다시 깊게 학습하라면 트랜잭션 격리수준, 전파, 동기화 및 추상, HikariCP…]]></description><link>https://haon.site/database/transaction/</link><guid isPermaLink="false">https://haon.site/database/transaction/</guid><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습동기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%8F%99%EA%B8%B0&quot; aria-label=&quot;학습동기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습동기&lt;/h2&gt;
&lt;p&gt;최근 카카오테크 교육과정의 실습 미션을 진행하면서, 이전부터 사용해왔던 &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 어노테이션을 다시금 사용하게 되었다. 사실 트랜잭션을 다시 깊게 학습하라면 트랜잭션 격리수준, 전파, 동기화 및 추상, HikariCP 등 학습할 내용이 무긍무진하지만, 점진적으로 천천히 학습을 이어가도록 한다. 이번 본 과정에 참여하면서 결심했던 마음가짐 중 하나이니깐 🙂&lt;/p&gt;
&lt;h2 id=&quot;트랜잭션이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;트랜잭션이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션이란?&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;트랜잭션(Transaction)&lt;/code&gt; 이란 &lt;strong&gt;데이터베이스의 하나의 논리적 기능을 수행하기 위해 여러개의 쿼리를 하나로 묶은 작업의 단위&lt;/strong&gt;를 뜻한다. 다시말해, 데이터베이스의 무결성이 보장되는 상태에서 요청된 작업을 완수하기 위한 작업의 기본 단위를 뜻한다.&lt;/p&gt;
&lt;p&gt;트랜잭션은 &lt;code class=&quot;language-text&quot;&gt;ACID&lt;/code&gt; 라는 특징을 가진다. 이는 &lt;strong&gt;원자성(Atomicity), 일관성(Consistency), 격리성(Isolation), 지속성(Durability)&lt;/strong&gt; 4가지 특성의 앞 글자를 따서 만들어진 용어이다. 이러한 ACID 각 특징에 대해 알아보자.&lt;/p&gt;
&lt;h2 id=&quot;acid&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#acid&quot; aria-label=&quot;acid permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ACID&lt;/h2&gt;
&lt;h3 id=&quot;원자성atomicity&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%9E%90%EC%84%B1atomicity&quot; aria-label=&quot;원자성atomicity permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원자성(Atomicity)&lt;/h3&gt;
&lt;p&gt;한 트랜잭션내에 담긴 모든 연산들은 데이터베이스에 모두 정상 반영되던지, 또는 전혀 반영되지 않는 것을 보장하는 특징이다. &lt;strong&gt;즉, 트랜잭션을 구성하는 모든 연산 전체가 성공(commit) 또는 실패하는 것(rollback)을 보장하는 특징이다.&lt;/strong&gt; 만약 연산중에 단 하나라도 오류가 발생하면 트랜잭션 전부가 롤백되어야 한다.&lt;/p&gt;
&lt;h3 id=&quot;일관성consistency&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%BC%EA%B4%80%EC%84%B1consistency&quot; aria-label=&quot;일관성consistency permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;일관성(Consistency)&lt;/h3&gt;
&lt;p&gt;트랜잭션이 실행을 성공적으로 완료(commit) 시 언제나 일관성있는 데이터베이스로 상태가 변환된다는 것을 뜻한다. &lt;strong&gt;즉, 시스템에 가지고있는 고정 요소는 트랜잭션 수행전과 트랜잭션 수행 완료후에 상태가 일관적으로 같아야한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;격리성isolation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%A9%EB%A6%AC%EC%84%B1isolation&quot; aria-label=&quot;격리성isolation permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;격리성(Isolation)&lt;/h3&gt;
&lt;p&gt;둘 이상의 트랜잭션이 동시에 수행되는 경우, &lt;strong&gt;실행중인 트랜잭션의 중간 결과에 또 다른 트랜잭션이 접근할 수 없다는 특징이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;기본적인 개념은 위와 같으나, 각 DBMS 의 트랜잭션 &lt;code class=&quot;language-text&quot;&gt;격리수준&lt;/code&gt;에 따라 수행 결과 참조 가능 여부가 달리잔다. 트랜잭션 격리수준에는 &lt;strong&gt;Read Uncommited, Read Commited, Repeatable Read, Serializable&lt;/strong&gt; 등이 존재하는데, Serializable 쪽으로 갈수록 트랜잭션 간의 고립성 정도가 높아지고 성능이 저하된다. 격리수준에 대한 내용은 추후에 자세히 다루도록 한다.&lt;/p&gt;
&lt;h3 id=&quot;영구성durability&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%81%EA%B5%AC%EC%84%B1durability&quot; aria-label=&quot;영구성durability permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;영구성(Durability)&lt;/h3&gt;
&lt;p&gt;성공적으로 완료(Commit)된 트랜잭션의 결과는 시스템이 고장나더라도 영구적으로 반영되어야 한다는 뜻이다.&lt;/p&gt;
&lt;h2 id=&quot;트랜잭션-상태&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%83%81%ED%83%9C&quot; aria-label=&quot;트랜잭션 상태 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 상태&lt;/h2&gt;
&lt;p&gt;트랜잭션의 연산과정 단계를 도식화하고 각 단계의 상태를 아래와 같이 정리할 수 있다. 각 상태에 대해서도 알아보자.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5a3fdedc26fb7b73dce24e75f3bee656/0f882/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 28.22085889570552%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAABIElEQVR42m1R2W7CMBDM/39aqx4SiBYCqIIGmhA7ia/4ZLo2vR460kjWrr2zM65QcMVfpJSgrYeeHYZJYhK6nGcb4L1HCAHGOqL/6WWGmFBFenyleTFd6WKElJKaHj0f0XUdFosl6noLzjkmaXBuezDGwSYNYwxWqxc0zQmWhISaUX10PZarV0htC7OKJ+b64dCgvTDs3w5Yb7ZUY0U4QxlX7rTE4/sJfBRwPqJqTi3W65oKZO17IG0q6GxDwoVNODYdmnOHgexJpWgzXcRnF9D2DNLMRcD6gCpPvQ1JiPFmWWqHkew5yurx6Rmb7b5slevFMh/IssEwjLi7f0C925cMp2wZ/yDnqr6CFspQdr/B515G/hBBolJTXyja0BZnn5dBzOhJL2/jAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/5a3fdedc26fb7b73dce24e75f3bee656/a6d36/image.png&quot;
        srcset=&quot;/static/5a3fdedc26fb7b73dce24e75f3bee656/222b7/image.png 163w,
/static/5a3fdedc26fb7b73dce24e75f3bee656/ff46a/image.png 325w,
/static/5a3fdedc26fb7b73dce24e75f3bee656/a6d36/image.png 650w,
/static/5a3fdedc26fb7b73dce24e75f3bee656/e548f/image.png 975w,
/static/5a3fdedc26fb7b73dce24e75f3bee656/3c492/image.png 1300w,
/static/5a3fdedc26fb7b73dce24e75f3bee656/0f882/image.png 1584w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;활성active&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%99%9C%EC%84%B1active&quot; aria-label=&quot;활성active permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;활성(Active)&lt;/h3&gt;
&lt;p&gt;트랜잭션이 정상적으로 실행중인 상태를 의미한다. 트랜잭션이 시작되면, 해당 트랜잭션의 상태는 활성(Active) 상태가 된다. 해당 상태는 설계자가 설계한 대로 연산들이 정상적으로 실행중인 상태를 뜻한다.&lt;/p&gt;
&lt;h3 id=&quot;부분-완료partially-commited&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%80%EB%B6%84-%EC%99%84%EB%A3%8Cpartially-commited&quot; aria-label=&quot;부분 완료partially commited permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;부분 완료(Partially Commited)&lt;/h3&gt;
&lt;p&gt;트랜잭션의 마지막까지 실행되었지만, 커밋 연산이 실행되기 직전이 상태다.&lt;/p&gt;
&lt;p&gt;설계된 트랜잭션대로 명령들을 성공적으로 수행하면, 그 다음 상태는 부분적 완료(Partially Commited) 상태가 된다. 즉, 설계된 작업대로 작업이 성공하였다고 하여 무조건 반영하는 것이 아니라, 설계자의 최종 승인이 있을 때 까지 실제 데이터베잇에 작업 내용을 반영하지 않고 대기하는 상태가 된다.&lt;/p&gt;
&lt;h3 id=&quot;실패failed-와-철회aborted&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%ED%8C%A8failed-%EC%99%80-%EC%B2%A0%ED%9A%8Caborted&quot; aria-label=&quot;실패failed 와 철회aborted permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실패(Failed) 와 철회(Aborted)&lt;/h3&gt;
&lt;p&gt;트랜잭션이 비정상적으로 종료되었을 때 실패(Failed) 상태가 되며, 그로 인해 작업 내용을 롤백하는 상태를 철회(Aborted) 라고 한다.&lt;/p&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;트랜잭션 격리수준&lt;/li&gt;
&lt;li&gt;트랜잭션 동기화 및 추상화&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/transaction/&quot;&gt;https://hudi.blog/transaction/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/MYSQL-%F0%9F%93%9A-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98Transaction-%EC%9D%B4%EB%9E%80-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC&quot;&gt;https://inpa.tistory.com/entry/MYSQL-%F0%9F%93%9A-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98Transaction-%EC%9D%B4%EB%9E%80-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@yu-jin-song/DB-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98Transaction&quot;&gt;https://velog.io/@yu-jin-song/DB-트랜잭션Transaction&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[대역을 활용한 외부 상황 테스트, Mock 객체의 활용을 어떻게 해야하는데? 🤷‍]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/tdd/mock/</link><guid isPermaLink="false">https://haon.site/haon/tdd/mock/</guid><pubDate>Wed, 17 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Mock&lt;/code&gt; 객체를 사용하거나, 팀원들과 소통하면서 이 테스트를 어떻게 진행할지 고민하다보면 항상 등장하는 키워드가 바로 &lt;code class=&quot;language-text&quot;&gt;Mock, 대역&lt;/code&gt; 이다. 다만 아쉬운점은, 제대로 이해하지 못하고 사용해봤던지라 모호한 이해도로 테스트를 진행했다는 점이다. 이번 기회에 확실하게 대역에 대해 정리하며, 향후 테스트 작성에도 도움이 되고자 작성한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;대역이-왜-필요한데-️&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8C%80%EC%97%AD%EC%9D%B4-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%9C%EB%8D%B0-%EF%B8%8F&quot; aria-label=&quot;대역이 왜 필요한데 ️ permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;대역이 왜 필요한데? 🤷‍♂️&lt;/h2&gt;
&lt;p&gt;우선 대역을 &quot;왜&quot; 써야하는지에 대해 간단한 상황을 가정하며 되짚어본다. 가령 사용자의 계좌 잔액을 조회하는 서비스를 개발중에 있다고 해보자. 이를 위해, 하나은행에서 제공하는 오픈 API 를 활용하여 현 사용자의 계좌 잔액을 조회해야한다. 즉, 외부 요인이 현 우리의 서비스에 영향을 끼치는 상황이며, 이는 곧 외부 요인이 테스트에 관여하는 것이라 할 수 있다.&lt;/p&gt;
&lt;p&gt;하지만 외부 요인은 테스트 작성을 어렵게 만들뿐만 아니라 테스트 결과도 예측할 수 없게 만든다. 하나은행에서 제공해주는 API 의 응답속도가 1달이라면, 우리의 테스트는 1달뒤에나 성공 여부를 판단할 수 있을 것이다.&lt;/p&gt;
&lt;h3 id=&quot;그래서-대역이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%9E%98%EC%84%9C-%EB%8C%80%EC%97%AD%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;그래서 대역이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그래서, 대역이란?&lt;/h3&gt;
&lt;p&gt;우리는 &lt;strong&gt;대역을 활용함으로써 테스트 코드가 외부 요인에 대한 의존성을 낮출 수 있게된다. 대역이란 특정 객체의 동작을 흉내내는 객체를 말한다.&lt;/strong&gt; 앞선 상황에 대역을 활용한다면, 하나은행의 오픈 API 활용하는 것이 아니라 우리 서비스 내부에 임의의 가짜 하나은행 API 를 활용함으로써 신속하게 응답을 테스트 결과를 확인할 수 있게된다. 즉, 대역으로 실제 대상 대신에 가짜 객체를 이용해서 테스트를 진행하는 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;대역의-종류&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8C%80%EC%97%AD%EC%9D%98-%EC%A2%85%EB%A5%98&quot; aria-label=&quot;대역의 종류 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;대역의 종류&lt;/h2&gt;
&lt;p&gt;대역의 종류는 다음과 같이 4가지로 구분된다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;대역 종류&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;스텁(Stub)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;구현을 단순한 것으로 대체한다.&lt;/strong&gt; 테스트에 맞게 단순히 원하는 동작을 수행한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;가짜(Fake)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;제품에는 적합하지 않지만, 실제 동작하는 구현을 제공한다.&lt;/strong&gt; 예를들어 실제 DB 대신에 메모리를 이용해서 구현한 레포지토리가 가짜 대역에 해당한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;스파이(Spy)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;호출된 내역을 기록한다.&lt;/strong&gt; 기록한 내역은 테스트 결과를 검증할 때 사용한다. 스텁이기도 하다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;모의(Mock)&lt;/td&gt;
&lt;td&gt;기대한대로 상호작용하는지 행위를 검증한다. 기대한대로 동작하지 않으면 예외를 터뜨릴 수 있다. 모의 객체는 스텁이자 스파이도 된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&quot;상황-가정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%81%ED%99%A9-%EA%B0%80%EC%A0%95&quot; aria-label=&quot;상황 가정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;상황 가정&lt;/h2&gt;
&lt;p&gt;원활한 이해를 위해, 회원가입 기능 구현을 하나 가정할 것이다. 이 상황에 알맞게 설계된 클래스에 각각 &lt;code class=&quot;language-text&quot;&gt;Stub&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Fake&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Spy&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Mock&lt;/code&gt; 을 적용해보자. 각 클래스(인터페이스)에 대한 설계 및 설명을 요약하자면 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SignUpService : 회원가입에 대한 핵심 로직을 수행한다.&lt;/li&gt;
&lt;li&gt;PasswordLengthChecker : 암호(비밀번호) 가 8자 이하인지 체크한다.&lt;/li&gt;
&lt;li&gt;UserRepository : 회원 정보 저장 DB&lt;/li&gt;
&lt;li&gt;EmailNotifier : 이메일 발송 기능을 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SignUpService 클래스에 회원 가입의 가장 핵심인 비즈니스 로직이 담겨있으며, 이 서비스 클래스에서 나머지 3개의 인터페이스 필드를 보유하고 있는 구조를 취한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1-passwordlengthchecker-에-스텁stub-적용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-passwordlengthchecker-%EC%97%90-%EC%8A%A4%ED%85%81stub-%EC%A0%81%EC%9A%A9&quot; aria-label=&quot;1 passwordlengthchecker 에 스텁stub 적용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. PasswordLengthChecker 에 스텁(Stub) 적용&lt;/h2&gt;
&lt;p&gt;회원가입 테스트를 위해 SignUpTest 라는 테스트 클래스를 하나 생성했다. 우리가 당장 테스트할 것은 &lt;code class=&quot;language-text&quot;&gt;PasswordLengthChecker&lt;/code&gt; 이다. 비밀번호가 약하다면 RunTimeException 예외가 터지는 기능을 구현한다고 가정했다. 아직 UserRepository, EmailNotifier 에 대한 멤버는 없는 것을 볼 수 있는데, 이들은 추가 테스트를 구현할 때 마다 추가될 예정이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignUpTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignUpService&lt;/span&gt; signUpService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StubPasswordLengthChecker&lt;/span&gt; stubPasswordLengthChecker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StubPasswordLengthChecker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@BeforeEach&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        signUpService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignUpService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stubPasswordLengthChecker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;비밀번호가 약하다면 회원가입에 실패한다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checkUnderLengthPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        stubPasswordLengthChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setIsSuccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Assertions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertThrows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            signUpService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;msung99&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pw1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;msung99@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또 앞서 말했듯이, WeakPasswordChecker 는 비밀번호를 체킹하는 기능을 수행하고 스텁을 활용한다고 했었다. 이에 대한 구현체로 StubWeakPasswordChecker 를 생성해줬다. 이 안의 isSuccess 멤버변수는 비밀번호가 정상인지 아닌지를 체크하는 필드이며, 비밀번호 상태를 설정해주도록 setter 를 생성했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PasswordLengthChecker&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checkWeekPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StubPasswordLengthChecker&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PasswordLengthChecker&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; isSuccess&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checkWeekPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; isSuccess&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setIsSuccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; isSuccess&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isSuccess &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; isSuccess&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 이런 의문이 들 수 있다. PasswordLengthChecker 는 비밀번호가 8자리 이하인지 아닌지를 체크한다고 했는데, 위처럼 간단히 isSuccess 를 저장하고 리턴하는 것만으로 구현해도 충분한가?&lt;/p&gt;
&lt;p&gt;StubPasswordLengthChecker 의 checkWeekPassword() 메소드는 단순히 isSuccess 필드 값을 리턴해도 된다. 이 정도만 구현해도 SignUpService 의 register() 가 약한 암호인 경우와 그렇지 않은 경우에 대해 올바르게 동작하는지 충분히 검증 가능하다.&lt;/p&gt;
&lt;h3 id=&quot;스텁stub-정의-에-부합하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%85%81stub-%EC%A0%95%EC%9D%98-%EC%97%90-%EB%B6%80%ED%95%A9%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;스텁stub 정의 에 부합하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스텁(Stub) 정의 에 부합하는가?&lt;/h3&gt;
&lt;p&gt;또한 이 정도의 구현은 &lt;code class=&quot;language-text&quot;&gt;스텁(Stub)&lt;/code&gt; 의 정의에도 부합한다. 스텁은 테스트에 맞게 단순히 원하는 동작을 수행한다고 했었다. 현 테스트는 비밀번호가 약하면 예외가 터지는지에 대한 것이다. 따라서 스텁 대역인 StubPasswordLengthChecker 은 약한 암호 인지 여부만을 알려주기만 하면 제 역할을 충분히 수행한 것이다.&lt;/p&gt;
&lt;p&gt;실제로 동작하는 구현은 대역에서 구현하지 않는다. 즉, StubPasswordLengthChecker 에선 단순히 비밀번호가 약하다는 것만 알려주는 역할을 수행하면 되고, 실제로 동작하는 구현인 &quot;비밀번호가 8자 이하인지 체킹하는 구현 코드&quot; 는 대역에서 구현하는 것이 아니라는 것이다. 이는 대역이 아닌, 향후 실제 도메인 코드에서 상세히 구현해야한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2-userrepository-에-가짜fake-구현-적용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-userrepository-%EC%97%90-%EA%B0%80%EC%A7%9Cfake-%EA%B5%AC%ED%98%84-%EC%A0%81%EC%9A%A9&quot; aria-label=&quot;2 userrepository 에 가짜fake 구현 적용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. UserRepository 에 가짜(Fake) 구현 적용&lt;/h2&gt;
&lt;p&gt;다음은 DB 인 UserRepository 에 Fake 구현을 적용해보겠다. 여기서 테스트할 것은 &lt;strong&gt;&quot;중복 ID 를 가진 회원이 이미 존재할 경우 예외가 터지는지 테스트&quot;&lt;/strong&gt; 해보자.&lt;/p&gt;
&lt;p&gt;이를 위한 테스트 코드를 먼저 작성해보면 아래와 같다. 이 코드는 SignUpTest 에 추가로 작성해줬으며, 아직 필요한 클래스, 필드등을 작성하지 않았으므로 컴파일 에러가 발생할 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;이미 같은 아이디를 가진 회원이 DB에 존재하면 가입에 실패한다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;duplicateExist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// 이미 같은 ID 가 DB 에 존재하는 상황을 만들기&lt;/span&gt;
    fakeRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;msung99&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pw1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;msung99@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Assertions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertThrows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            signUpService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;msung99&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;origin-password&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;msung1234@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이어서 기존 SignUpService 의 &lt;code class=&quot;language-text&quot;&gt;register()&lt;/code&gt; 메소드를 아래와 같이 일반화 시켜줬다. 코드를 보면 알겠지만, &lt;code class=&quot;language-text&quot;&gt;findById()&lt;/code&gt; 를 통해 아이디 값을 기반으로 유저를 데이터를 가져오도록 시도한다. 만약 가져오는데 성공한 경우라면 이미 동일한 아이디를 가진 유저가 회원가입된 상태이므로 오류를 발생시킨다.&lt;/p&gt;
&lt;p&gt;아직은 UserRepository 에 관해 구현된 실제 코드가 없기 떄문에 컴파일 오류가 발생할 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignUpService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PasswordLengthChecker&lt;/span&gt; passwordLengthChecker&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRepository&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 추가&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignUpService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PasswordLengthChecker&lt;/span&gt; passwordLengthChecker&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRepository&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;passwordLengthChecker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; passwordLengthChecker&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;passwordLengthChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;checkWeekPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        userRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;다음으로 UserRepository 에 대한 가짜 대역을 추가해보자. 실제 DB 와 동일하게 기능을 수행 가능한 UserRepository 의 가짜(Fake) 객체를 생성하는 것이다. 이 가짜 객체를 이용해서 이미 중복된 아이디를 가진 사용자가 존재하는 상황을 만들면 된다.&lt;/p&gt;
&lt;p&gt;즉, UserRepository 가 구현할 실제 DB 가 JpaRepository, 기타 DB 이던간에 그와 별개로 이를 대체하는 가짜 DB 객체를 만드는 것이다. 이는 간단히 메모리상에 Map 컬렉션을 활용하여 간단히 유저 데이터를 저장하는 방식으로 구현해보겠다. 이에 대한 코드는 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemoryUserRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; users &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        users&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; users&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또 유저 클래스도 아래와 같이 간단히 구현해주자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이를 염두하며 새롭게 코드를 추가하여 만들어낸 SignUpTest 의 전체 코드는 아래와 같다.&lt;code class=&quot;language-text&quot;&gt;MemoryUserRepository&lt;/code&gt; 를 생성한 모습을 볼 수 있는데, 이는 앞서 말했듯이 실제 데이터베이스 대신에 가짜(Fake) 데이터베이스로 동작하는 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignUpTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignUpService&lt;/span&gt; signUpService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StubPasswordLengthChecker&lt;/span&gt; stubPasswordLengthChecker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StubPasswordLengthChecker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemoryUserRepository&lt;/span&gt; fakeRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemoryUserRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@BeforeEach&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        signUpService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignUpService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stubPasswordLengthChecker&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fakeRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;비밀번호가 약하다면 회원가입에 실패한다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checkUnderLengthPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        stubPasswordLengthChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setIsSuccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Assertions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertThrows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            signUpService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;msung99&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pw1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;msung99@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;이미 같은 아이디가 DB 에 존재하면 가입에 실패한다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;duplicateExist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 이미 같은 ID 가 DB 에 존재하는 상황을 만들기&lt;/span&gt;
        fakeRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;msung99&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pw1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;msung99@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Assertions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertThrows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            signUpService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;msung99&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;origin-password&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;msung1234@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;가짜fake-정의에-부합하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%80%EC%A7%9Cfake-%EC%A0%95%EC%9D%98%EC%97%90-%EB%B6%80%ED%95%A9%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;가짜fake 정의에 부합하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;가짜(Fake) 정의에 부합하는가?&lt;/h3&gt;
&lt;p&gt;앞서 말했길, 가짜 대역이란 제품에는 적합하진 않아도 실제로 동작하는 기능을 제공한다고 했었다. 위 가짜 데이터베이스 MemoryUserRepository 는 JpaRepository 와 같은 실제 데이터베이스의 다양한 기능에는 훨씬 미치지 못한다. 하지만, 유저를 찾고, 저장하는 기능을 충분히 소화내고 있음을 알 수 있다. 따라서 가짜 대역 정의에 충분히 부합한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3-이메일-발송-여부-확인을-위해-스파이spy-활용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%EC%9D%B4%EB%A9%94%EC%9D%BC-%EB%B0%9C%EC%86%A1-%EC%97%AC%EB%B6%80-%ED%99%95%EC%9D%B8%EC%9D%84-%EC%9C%84%ED%95%B4-%EC%8A%A4%ED%8C%8C%EC%9D%B4spy-%ED%99%9C%EC%9A%A9&quot; aria-label=&quot;3 이메일 발송 여부 확인을 위해 스파이spy 활용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 이메일 발송 여부 확인을 위해 스파이(Spy) 활용&lt;/h2&gt;
&lt;p&gt;다음으로 &lt;code class=&quot;language-text&quot;&gt;EmailNotifier&lt;/code&gt; 에 대해서 &lt;code class=&quot;language-text&quot;&gt;스파이(Spy)&lt;/code&gt; 이메일 발송 기능을 적용해 볼 것이다.&lt;/p&gt;
&lt;p&gt;이메일 발송 여부를 확인하기 위해, SignUpService 에서 EmailNotifier 의 메일 발송 기능을 실행할 때 이메일 주소로 &quot;&lt;a href=&quot;mailto:msung99@gmail.com&quot;&gt;msung99@gmail.com&lt;/a&gt;&quot; 을 사용했는지 확인하는 것이다. 이런 용도로 사용할 수 있는것이 스파이 대역이다. EmailNotifier 의 스파이 대역을 이용한 테스트 코드를 만들어보자.&lt;/p&gt;
&lt;p&gt;회원 가입시 이메일을 올바르게 전송했는지 확인할 수 있으려면 EmailNotifier 의 &lt;strong&gt;스파이 대역이&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; &lt;strong&gt;이메일 발송 여부와&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; &lt;strong&gt;발송을 요청할 때 사용한 이메일 주소를 제공할 수 있어야한다.&lt;/strong&gt; 이를 위한 테스트 코드는 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;가입하면 메일을 전송함&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;whenRegisterThenSendEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	signUpService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;msung99&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;msung1234&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;msung99@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Assertions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;spyEmailNotifier&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isCalled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Assertions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;msung99@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spyEmailNotifier&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;우선 EmailNotifier 코드가 새롭게 추가된 모습을 볼 수 있다. 이는 외부에서 생성자 주입을 통해 SpyEmailNotifier 라는 스파이 객체를 주입받을 것이다. 또한 &lt;code class=&quot;language-text&quot;&gt;register()&lt;/code&gt; 구현 코드가 변경되었다. 가장 눈여거 볼 부분은 맨 아래줄에 emailNotifier 를 활용한 부분이다. 이를 통해 이메일 전송 여부와, 전송한 이메일 계정을 조회할 수 있게된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignUpService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PasswordLengthChecker&lt;/span&gt; passwordLengthChecker&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRepository&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 추가&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmailNotifier&lt;/span&gt; emailNotifier&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignUpService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PasswordLengthChecker&lt;/span&gt; passwordLengthChecker&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRepository&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmailNotifier&lt;/span&gt; emailNotifier&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;passwordLengthChecker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; passwordLengthChecker&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;emailNotifier &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; emailNotifier&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;passwordLengthChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;checkWeekPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        userRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        emailNotifier&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendRegisterEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 이메일 전송 상태 저장&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;EmailNotifier 와 스파이 구현 내용은 아래와 같이 정해줬다. SpyEmailNotifier 를 보면 이메일 호출 여부인 &lt;code class=&quot;language-text&quot;&gt;called&lt;/code&gt; 필드를 보유하고 있고, 호출된 이메일 정보를 저장하는 &lt;code class=&quot;language-text&quot;&gt;email&lt;/code&gt; 필드를 보유한 것을 볼 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmailNotifier&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendRegisterEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpyEmailNotifier&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmailNotifier&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; called&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isCalled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; called&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendRegisterEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;called &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;스파이spy-정의에-부합하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%8C%8C%EC%9D%B4spy-%EC%A0%95%EC%9D%98%EC%97%90-%EB%B6%80%ED%95%A9%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;스파이spy 정의에 부합하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스파이(Spy) 정의에 부합하는가?&lt;/h3&gt;
&lt;p&gt;스파이의 정의를 다시 되짚어보면, &lt;strong&gt;&quot;호출된 내역을 기록한다.&quot;&lt;/strong&gt; 라고 내렸다. 위 SpyEmailNotifier 코드를 보면 &lt;code class=&quot;language-text&quot;&gt;called&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;email&lt;/code&gt; 필드를 통해 이메일 호출 여부 및 정보를 저장하고 있으므로, 스파이의 정의에 알맞게 구현된 것이라 할 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;4-모의-객체mocktio-를-활용하여-기존의-스텁과-스파이-객체를-대체하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-%EB%AA%A8%EC%9D%98-%EA%B0%9D%EC%B2%B4mocktio-%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%98%EC%97%AC-%EA%B8%B0%EC%A1%B4%EC%9D%98-%EC%8A%A4%ED%85%81%EA%B3%BC-%EC%8A%A4%ED%8C%8C%EC%9D%B4-%EA%B0%9D%EC%B2%B4%EB%A5%BC-%EB%8C%80%EC%B2%B4%ED%95%98%EA%B8%B0&quot; aria-label=&quot;4 모의 객체mocktio 를 활용하여 기존의 스텁과 스파이 객체를 대체하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 모의 객체(Mocktio) 를 활용하여 기존의 스텁과 스파이 객체를 대체하기&lt;/h2&gt;
&lt;p&gt;마지막으로 앞서 작성한 테스트 코드를 모의 객체를 활용하여 다시 작성해 볼 것이다. 모의 객체 구현을 위해 &lt;code class=&quot;language-text&quot;&gt;Mockito&lt;/code&gt; 를 활용한다. 또한 &lt;strong&gt;Mock 객체는 스텁과 스파이를 지원한다(대체 가능하다)는 점을 알아두자.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;우선 기존 PasswordLengthChecker 에 대한 테스트 코드를 모의 객체를 활용한 코드로 변경해 볼 것이다. 그 코드는 아래와 같다.&lt;/p&gt;
&lt;h3 id=&quot;비밀번호-체킹-테스트-코드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%B2%B4%ED%82%B9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C&quot; aria-label=&quot;비밀번호 체킹 테스트 코드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비밀번호 체킹 테스트 코드&lt;/h3&gt;
&lt;p&gt;PasswordLengthChecker 에 대한 객체를 &lt;code class=&quot;language-text&quot;&gt;Mock&lt;/code&gt; 객체로 생성해줬다. 이어서 BDDMockito 를 활용했는데, checkWeekPassword( ) 메소드가 &quot;pw&quot; 라는 인자를 넘겨받은 경우 true 를 리턴하도록 설정해줬다. 즉. &quot;pw&quot; 파라미터를 사용해서 Mock 객체의 checkPasswordWeak 메소드를 호출하면 리턴값으로 true 를 리턴하도록 했다. 즉, 앞서 살펴봤던 PasswordLengthChecker 의 경우 &lt;code class=&quot;language-text&quot;&gt;스텁(Stub)&lt;/code&gt; 객체를 주입해줬다면, 이번엔 &lt;code class=&quot;language-text&quot;&gt;모의(Mock)&lt;/code&gt; 객체로 주입해주는 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRegisterMockTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignUpService&lt;/span&gt; signUpService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PasswordLengthChecker&lt;/span&gt; mockPasswordLengthChecker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Mockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PasswordLengthChecker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemoryUserRepository&lt;/span&gt; fakeRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemoryUserRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmailNotifier&lt;/span&gt; mockEmailNotifier &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Mockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;EmailNotifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@BeforeEach&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        signUpService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SignUpService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mockPasswordLengthChecker&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fakeRepository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mockEmailNotifier&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;비밀번호가 약하다면 회원가입에 실패한다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checkUnderLengthPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;BDDMockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;given&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mockPasswordLengthChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;checkWeekPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pw1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;willReturn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Assertions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertThrows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            signUpService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;msung99&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pw1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;msung99@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;checkpassword-메소드-호출-여부-테스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#checkpassword-%EB%A9%94%EC%86%8C%EB%93%9C-%ED%98%B8%EC%B6%9C-%EC%97%AC%EB%B6%80-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;checkpassword 메소드 호출 여부 테스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;checkPassword 메소드 호출 여부 테스트&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;대역 객체가 기대하는 대로 상호작용했는지 확인하는 것이 모의 객체의 주요 기능이다.&lt;/strong&gt; Mockito 를 사용하면 아래 테스트처럼 Mock 객체가 기대한대로 호출되었는지 검증할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에서 인자로 전달한 mockPasswordLengthChecker 모의 객체의 특정 메소드가 호출되었는지를 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 에서 검증하는데, &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 에서 보듯이 임의의 String 타입 파라미터를 이용해서 checkPasswordWeak() 메소드 호출 여부를 확인한다. 즉, 아래 코드는 SignUpService 의 register 메소드를 호출시 PasswordLengthChecker 모의 객체의 checkWeekPassword 메소드가 호출되는지를 검증하는 테스트 코드이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;회원 가입시 비밀번호 검사를 수행한다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checkPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	signUpService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;msung99&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pw&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;BDDMockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mockPasswordLengthChecker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
    	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;checkWeekPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BDDMockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;anyString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (3)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;emailnotifier-테스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#emailnotifier-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;emailnotifier 테스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EmailNotifier 테스트&lt;/h3&gt;
&lt;p&gt;Mock 객체를 사용하면 스파이도 가능하다. 모의 객체의 &lt;code class=&quot;language-text&quot;&gt;sendRegisterEmail()&lt;/code&gt; 메소드를 호출할 때 사용한 파라미터 값을 구하는 코드는 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;가입하면 메일을 전송함&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;whenRegisterThenSendEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	signUpService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;msung99&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;msung1234&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;msung99@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;ArgumentCaptor&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; captor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArgumentCaptor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;BDDMockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mockEmailNotifier&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                   &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendRegisterEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;captor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;capture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;

	&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; realEmail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; captor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;Assertions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;msung99@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; realEmail&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Mockito 의&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;ArgumentCaptor&lt;/code&gt; &lt;strong&gt;는 Mock 객체를 호출할 때 전달한 객체를 담는 기능을 제공한다.&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;BDDMockito.then().should()&lt;/code&gt; 로 Mock 객체의 메소드가 호출되었는지 확인할 때 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 처럼 &lt;code class=&quot;language-text&quot;&gt;ArgumentCatpor.capture()&lt;/code&gt; 를 사용하면 메소드을 호출시 전달한 파라미터가 &lt;code class=&quot;language-text&quot;&gt;ArgumentCatpor&lt;/code&gt; 에 담긴다. 또한 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 처럼 &lt;code class=&quot;language-text&quot;&gt;ArugmentCaptor.getValue()&lt;/code&gt; 메소드를 사용해서 보관한 파라미터를 얻어낼 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;대역의-활용에-대해&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8C%80%EC%97%AD%EC%9D%98-%ED%99%9C%EC%9A%A9%EC%97%90-%EB%8C%80%ED%95%B4&quot; aria-label=&quot;대역의 활용에 대해 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;대역의 활용에 대해&lt;/h2&gt;
&lt;h3 id=&quot;why&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why&quot; aria-label=&quot;why permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why?&lt;/h3&gt;
&lt;p&gt;다시금 대역을 사용해야하는 필요성에 대해 되짚어보자. 앞서 예시 들기를, 하나은행 API 가 있을때 다양한 외부 요인이 테스트틀 방해할 수 있다고 했었다.&lt;/p&gt;
&lt;p&gt;앞서 만들어봤던 회원 가입 예시도 다시 생각해보자. 메일이 발송되는지 확인하려면 실제 이메일 주소를 이용해서 테스트를 진행해야한다. 또한, 메일이 도착할 때 까지 메일함을 확인해야 한다. 이메일 특성상 테스트를 진행하고 몇분뒤에 메일이 도착하기도 한다. 이런 외부 요인에 구애받지 않고 원할한 테스트 진행을 가능케하는것이 바로 대역이다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;대역은 페어 프로그래밍에도 도움된다.&lt;/strong&gt; 대역이 없었다면, 개발자 1이 기능 A를 개발 완료하기 전까지 개발자 2는 기능 A 를 활용한 본인의 도메인 개발이 힘들다. A 가 개발이 완료되기 전까지 대기해야할 것이다. 하지만 대역을 활용한다면 A 에 대한 실제 구현이 없이도 다양한 상황에 대해 테스트할 수 있다.&lt;/p&gt;
&lt;p&gt;또한 대역을 활용하면 실제 구현 없이도 실행 결과를 확인할 수 있다. DB 가 없어도 데이터가 올바르게 저장되는지 확인 가능하고, 메일 서버가 없어도 이메일 발송 요청을 하는지 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;즉, 대역은 의존하는 대상을 구현하지 않아도 테스트 대상을 완성할 수 있게 만들어준다. 이는 대기 시간을 줄여주며 개발 속도를 높이는데 도움이 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;mock-객체의-과한-사용을-지양하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mock-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EA%B3%BC%ED%95%9C-%EC%82%AC%EC%9A%A9%EC%9D%84-%EC%A7%80%EC%96%91%ED%95%98%EC%9E%90&quot; aria-label=&quot;mock 객체의 과한 사용을 지양하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Mock 객체의 과한 사용을 지양하자&lt;/h3&gt;
&lt;p&gt;앞서 말했듯이 &lt;strong&gt;Mock 객체는 스텁과 스파이를 지원한다(대체 가능하다).&lt;/strong&gt; 때문에 대역으로 Mock 객체를 많이 사용한다. 하지만 Mock 객체를 과하게 사용하면 되려 테스트 코드가 복잡해지는 경우도 발생하니 주의해서 사용해야한다.&lt;/p&gt;
&lt;p&gt;Mock 객체를 이용하면 대역 클래스를 만들지 않아도 되니깐 처음에는 편할 수 있다. 하지만 결과값을 확인하는 수단으로 Mock 을 사용하기 시작하면 결과 검증 코드가 길어지고 복잡해진다.&lt;/p&gt;
&lt;p&gt;이런 이유로 Mock 객체의 메소드 호출 여부를 결과 검증 수단으로 사용하는 것은 주의해야한다. &lt;strong&gt;특히 DAO 나 Repository 와 같이 저장소에 대한 대역은 Mock 객체를 사용하는 것 보다 메모리를 이용한 가짜(Fake) 구현을 사용하는 것이 테스트 코드 관리애 유리할 것이다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;E2E Test&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Mocktio 란 무엇이고, 어떻게 사용하는걸까?]]></title><description><![CDATA[Mockito…]]></description><link>https://haon.site/haon/tdd/mockito/</link><guid isPermaLink="false">https://haon.site/haon/tdd/mockito/</guid><pubDate>Wed, 17 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Mockito 는 모의 객체 생성 검증, 스텁을 지원하는 프레임워크이다. 지금은 모의 객체에 대한 자세한 설명을 생략하고, 기본 사용법에 대해 중점으로 다룰 것이다. 추후 포스트에서 대역에 대한 전반 내용을 다룰 때 이론을 더 다루고자 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;모의-객체-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A8%EC%9D%98-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;모의 객체 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;모의 객체 생성&lt;/h2&gt;
&lt;p&gt;우선 &lt;code class=&quot;language-text&quot;&gt;Mockito.mock()&lt;/code&gt; 메소드를 이용하면 특정 타입의 모의 객체를 생성할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GameGenMockTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mockTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;GameNumGen&lt;/span&gt; genMock &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Mockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GameNumGen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;스텁-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%85%81-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;스텁 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스텁 설정&lt;/h2&gt;
&lt;p&gt;모의 객체를 생성한 뒤에는 BDDMockito 클래스를 이용해서 모의 객체에 &quot;스텁(Stub)&quot;을 구성할 수 있다. 스텁이란 인스턴스화하여 구현한 가짜 객체(Dummy, 기능 구현이 없음) 를 이용해 실제로 동작하는 것처럼 보이게 만드는 객체이다. &lt;code class=&quot;language-text&quot;&gt;BDDMockito.given()&lt;/code&gt; 메소드를 이용하면 모의 객체의 메소드가 특정 값을 리턴하도록 설정할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;bddmocktio&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bddmocktio&quot; aria-label=&quot;bddmocktio permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BDDMocktio&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GameGenMockTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mockTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;GameNumGen&lt;/span&gt; genMock &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Mockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GameNumGen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;BDDMockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;given&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;genMock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GameLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EASY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;willReturn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; num &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; genMock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GameLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EASY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Assertions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; num&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드에서 &lt;code class=&quot;language-text&quot;&gt;BDDMockito.given()&lt;/code&gt; 메소드는 스텁을 정의할 모의 객체의 메소드 호출을 전달한다.&lt;/p&gt;
&lt;h4&gt;willReturn&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;given()&lt;/code&gt; 메소드에 이어 &lt;code class=&quot;language-text&quot;&gt;willReturn()&lt;/code&gt; 으로 스텁을 정의한 메소드가 리턴할 값을 지정한다.&lt;/p&gt;
&lt;p&gt;즉, 위 코드의 경우 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에서 genMock.generate(GameLevel.EASY) 메소드가 불리면 &quot;123&quot; 을 리턴하도록 설정한 것이다. 또 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 에선 모의 객체의 &lt;code class=&quot;language-text&quot;&gt;generate()&lt;/code&gt; 메소드를 실행한다. 이때 인자로 GameLevel.EASY 를 전달하고 있는데, 이는 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 의 given() 에서 지정한 메소드 인자와 일치하므로 genMock.generate(GameLevel.EASY) 코드는 &quot;123&quot; 을 리턴한다.&lt;/p&gt;
&lt;h4&gt;willThrow&lt;/h4&gt;
&lt;p&gt;지정한 값을 리턴하는 대신에 Exception 을 터뜨리고 싶다면 &lt;code class=&quot;language-text&quot;&gt;willThrow()&lt;/code&gt; 를 사용하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mockTest2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;GameNumGen&lt;/span&gt; genMock &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Mockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GameNumGen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;BDDMockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;given&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;genMock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GameLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EASY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;willThrow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Assertions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertThrows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; genMock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 예제를 보면 Mock 객체가 willThrow() 를 통해 예외를 터뜨리는 모습을 볼 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mockTest3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; mockList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Mockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;BDDMockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;willThrow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;given&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mockList&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Assertions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertThrows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; mockList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;BDDMockito.willThrow()&lt;/code&gt; 메소드는 발생한 예외 타입이나 예외 객체를 파라미터로 전달받는다. &lt;code class=&quot;language-text&quot;&gt;given()&lt;/code&gt; 메소드는 모의 객체를 전달받는다. 모의 객체의 메소드 실행이 아닌 모의 객체임에 유의하자.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;given()&lt;/code&gt; 메소드는 인자로 전달받은 모의 객체 자신을 리턴하는데 이떄 예외를 터뜨릴 메소드를 설정한다.&lt;/p&gt;
&lt;p&gt;위 예제를 더 해석해보면, 모의 객체인 mockList 의 타입을 보면 &lt;code class=&quot;language-text&quot;&gt;List&lt;/code&gt; 이다. mockito 객체의 내장함수, 즉 List 타입의 내장 함수인 &lt;code class=&quot;language-text&quot;&gt;clear()&lt;/code&gt; 를 호출하는 경우 IllegalArgumentException 예외가 터지도록 설정한 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;인자-매칭-정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EC%9E%90-%EB%A7%A4%EC%B9%AD-%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;인자 매칭 정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인자 매칭 정리&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;BDDMockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;given&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GameLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EASY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;willReturn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; num &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GameLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NORMAL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드는 스텁을 설정할 때 &lt;code class=&quot;language-text&quot;&gt;generate()&lt;/code&gt; 의 인자로 GameLevel.EASY 를 전달하고 있는데, 실제로 generate() 메소드를 호출할 때는 GameLevel.NORMAL 을 인자로 전달했다. 이 경우 mock.generate(GameLevel.NORMAL) 코드는 스텁을 설정할 때 사용한 인자와 일치하지 않으므로 null 을 리턴한다.&lt;/p&gt;
&lt;h3 id=&quot;argumentmatchers&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#argumentmatchers&quot; aria-label=&quot;argumentmatchers permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ArgumentMatchers&lt;/h3&gt;
&lt;p&gt;ArgumentMatchers 클래스를 사용하면 정확하게 일치하는 값 대신 임의의 값에 일치하도록 설정할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mockTest4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;GameNumGen&lt;/span&gt; mock &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Mockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GameNumGen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;BDDMockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;given&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ArgumentMatchers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;willReturn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;456&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; num &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GameLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EASY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Assertions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;456&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; num&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; num2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GameLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NORMAL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (3)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Assertions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;456&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; num2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에서 스텁을 설정할 때 &lt;code class=&quot;language-text&quot;&gt;ArgumentMatchers.any()&lt;/code&gt; 메소드를 파라미터로 전달했다. any() 메소드를 사용하면 모든 값에 일치하도록 스텁을 설정한다. &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;generate()&lt;/code&gt; 메소드는 모두 &quot;456&quot; 을 리턴한다.&lt;/p&gt;
&lt;h4&gt;ArgumentMatcher 의 세부 메소드들&lt;/h4&gt;
&lt;p&gt;ArgumentMatcher 가 제공하는 세부 기능(메소드) 들은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;기본 원시타입에 대한 임의 값 일치 : anyInt(), anyLong(), anyChar(), anyDouble(), ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;임의 타입에 대한 일치 : any()&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;임의 컬렉션에 대한 일치 : anyList(), anySet(), anyMap(), anyCollection()&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;정규표현식을 이용한 String 값 일치 여부 : matches(String), matches(Pattern)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;특정 값과 일치 여부 : eq(값)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ArgumentMatchers 의 &lt;code class=&quot;language-text&quot;&gt;anyInt()&lt;/code&gt; 나 &lt;code class=&quot;language-text&quot;&gt;any()&lt;/code&gt; 등의 메소드는 내부적으로 인자의 일치 여부를 판단하기 위해 ArgumentMatchers 를 등록한다. Mockito 는 한 파라미터라도 ArgumentMatchers 를 사용해서 설정한 경우, 모든 인자를 ArgumentMatchers 를 이용해서 설정하도록 하고있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mockTest5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; mockList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Mockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;BDDMockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;given&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mockList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ArgumentMatchers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;anyInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArgumentMatchers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;willReturn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;456&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; old &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mockList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;Assertions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;456&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; old&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;임의의 값과 일치하는 인자와 정확하게 일치하는 일치하는 인자를 함께 사용하고싶다면 &lt;code class=&quot;language-text&quot;&gt;ArgumentMatchers.eq()&lt;/code&gt; 를 사용하자.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;행위-검증&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%96%89%EC%9C%84-%EA%B2%80%EC%A6%9D&quot; aria-label=&quot;행위 검증 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;행위 검증&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mockTest6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;GameNumGen&lt;/span&gt; mock &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Mockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GameNumGen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Game&lt;/span&gt; game &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mock&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    game&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GameLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EASY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;BDDMockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mock&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GameLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EASY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;모의 객체의 역할 중 하나는 실제로 모의 객체가 호출됐는지 검증하는 것이다. 모의 객체의 특정 메소드가 호출되었는지 확인하는 코드는 위와 같다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;BDDMockito.then()&lt;/code&gt; 은 메소드 호출 여부를 검증할 모의 객체를 전달받는다. &lt;code class=&quot;language-text&quot;&gt;should()&lt;/code&gt; 메소드는 모의 객체의 메소드가 호출되어야 한다는 것을 설정하고 should() 메소드 다음에 실제로 불려야 할 메소드를 지정한다. 위 예제에서 mock 객체의 generate() 메소드가 GameLevel.EASY 인자를 사용해서 호출되었는지를 검증한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;BDDMockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mock&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 이때 GameLevel.EASY 처럼 정확한 값이 아니라 메소드 호출 여부가 중요하다면 any(), anyInt() 등을 사용해서 인자를 지정하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;BDDMockito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mock&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ArgumentMatchers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;only&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ArgumentMatchers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;정확하게 1번만 호출된 것을 검증하고 싶다면 should() 메소드에 Mockito.only() 를 인자로 전달한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;인자 캡쳐&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Spring IoC Container 과 스프링 빈(Spring Bean)]]></title><description><![CDATA[💡 현 포스트 학습동기는 지난 스프링 컨테이너의 제어의 역전(IoC) 과 의존성 주입(DI) 에서 이어지는 내용이다. 지난 포스트에서  과  에 대해 다루었다. 다만 스프링부트에서 제공하는 IoC 컨테이너에 관한 내용을 자세히 다루지 않고, IoC…]]></description><link>https://haon.site/spring/bean-and-ioc/</link><guid isPermaLink="false">https://haon.site/spring/bean-and-ioc/</guid><pubDate>Tue, 16 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 현 포스트 학습동기는 지난 &lt;a href=&quot;https://haon.blog/spring/loc/&quot;&gt;스프링 컨테이너의 제어의 역전(IoC) 과 의존성 주입(DI)&lt;/a&gt; 에서 이어지는 내용이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;지난 포스트에서 &lt;code class=&quot;language-text&quot;&gt;IoC(Inversion Of Control)&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;DI(Dependency Injection)&lt;/code&gt; 에 대해 다루었다. 다만 스프링부트에서 제공하는 IoC 컨테이너에 관한 내용을 자세히 다루지 않고, IoC 와 DI 라는 그 자체의 개념에 대해서만 집중적으로 다루었다. 이번에는 스프링 컨테이너에 어떻게 IoC 를 제공하는지, 개발자가 어떻게 DI 를 제공할 수 있는지에 대해 다루어보고자 한다.&lt;/p&gt;
&lt;h2 id=&quot;스프링-빈-spring-bean&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88-spring-bean&quot; aria-label=&quot;스프링 빈 spring bean permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링 빈 (Spring Bean)&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/75fee8275f574dda78c62c0c569fa0a1/80521/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.69325153374233%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABu0lEQVR42m2S608aQRTF9///3DTpB9Nqa2tSaYyP1ITyqKg8GrFoUFy3wd2FLcVERNmwM7s7vw4TEERvcrJzH3P23nvGYmpKwVWzzMWvbzSqO3S73WlcaaQ81T3Vq4X8HNasMNX51lmJ5vEXzmtbHB0fIePY/CmSYzqXV/ilOv5hnU7LQSaxYZ8Rz8xadJJEMZapIXddFykEk96cQpXhahYxwVqO4VqW63wZkSZ4nofn+3idDsHf4Dnh4ih/HIcbTXpSreGt7MJOneTtV5KVPdg9ZbBR5GehSKNSoVc/o9+yca7tOeGERiUJSsbGb7fbhGGI73q032vCTJX03ZYBm2X6G3ls26ZW0msqljgvHHBSrjzvMB49cuvYmjTC9T1EFJm427ik+3Gf8ec84085c7753TQ5odcidSNCQ0qJleoAoxE9vYPM+h7rbzb5njkgm82Rpum0e8XgboB/YeM1bXNWs6exZJaaKKk7Gd7fk9/Os/9hm/KPU4KgN9/py3uvx5ZVVmKMuLvVHT/M16CVjBKhFZVEcYSIpR5PGl+9wmotK6xmAk3HeZCPeGFAEPbphv8MOubb18/rJeF/OKnxBGNgdFgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/75fee8275f574dda78c62c0c569fa0a1/a6d36/image-1.png&quot;
        srcset=&quot;/static/75fee8275f574dda78c62c0c569fa0a1/222b7/image-1.png 163w,
/static/75fee8275f574dda78c62c0c569fa0a1/ff46a/image-1.png 325w,
/static/75fee8275f574dda78c62c0c569fa0a1/a6d36/image-1.png 650w,
/static/75fee8275f574dda78c62c0c569fa0a1/e548f/image-1.png 975w,
/static/75fee8275f574dda78c62c0c569fa0a1/3c492/image-1.png 1300w,
/static/75fee8275f574dda78c62c0c569fa0a1/80521/image-1.png 2416w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;제어의 역전(IoC)&lt;/code&gt; 은 제 3자인 외부 라이브러리에게 객체의 생명주기에 대한 제어권을 넘겨주는 것을 뜻한다고 했다. 이때 스프링 프레임워크 생태계에서 외부 라이브러리란 스프링 컨테이너를 뜻한다. 스프링은 스프링 컨테이너 내에서 제어의 역전(IoC) 로 객체를 관리하는데, 이때 &lt;strong&gt;스프링이 제어 권한을 가져 직접 생성하고 관계를 부여하는 객체를&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;Bean&lt;/code&gt; &lt;strong&gt;이라고 한다.&lt;/strong&gt; 즉, Spring Bean 은 스프링 컨테이너가 생성, 관계성정, 사용등을 제어해주는 제어의 역전이 적용된 객체를 뜻한다.&lt;/p&gt;
&lt;p&gt;스프링은 내부에 스프링 컨테이너라는 것이 구현되어있다. 이 스프링 컨테이너는 IoC Container 라고도 불리는데, 말 그대로 IoC 를 제공해주는 특정한 컨테이너 공간으로 구현되어있다. 이 컨테이너 공간 안에는 스프링에서 자체적으로 관리하는 Spring Bean 이 존재한다. 우리는 객체간의 의존성 정보를 &lt;code class=&quot;language-text&quot;&gt;생성자 주입&lt;/code&gt; 을 통해 제공해주기만 하면, 스프링 컨테이너내에 Bean 객체가 자동으로 생성되고, 관리된다. 이후 클라이언트가 필요할 떄 마다 매번 개발자가 직접 객체를 생성하여 서비스를 제공하는 방식이 아닌, Spring IoC Container 내에 생성된 스프링 Bean 객체를 불러와 사용할 수 있게 된다.&lt;/p&gt;
&lt;p&gt;또한 스프링 컨테이너라는 외부의 제 3자에게 객체의 생명주기 관리 제어권을 원활히 도맡기기 위해선, 우리가 스프링 컨테이너에서 관리될 각 객체들간의 최소한의 관계(relationship) 정보, 즉 의존관계 정보를 넘겨줘야한다. 그래야 스프링 컨테이너가 어떤 각 객체들간의 의존관계를 파악하고, 상황에 알맞는 의존성을 컨테이너내에 등록하여 이후 클라이언트에게 서비스를 제공할 수 있을 것이다. 스프링 프레임워크는 프로그래머가 코드를 작성하여 제공해준 최소한의 의존관계 정보를 바탕으로, 스프링 컨테이너내에 객체(Bean) 를 등록하고, 관리한다. 이후 클라이언트에게 필요한 의존관계를 자동으로 제공해준다. 이를 &lt;code class=&quot;language-text&quot;&gt;DI(Dependency Injection)&lt;/code&gt; 이라고 한다. &lt;strong&gt;즉, DI 란 의존성 주입이라는 개념으로, 객체간의 의존성을 외부(스프링 컨테이너)에서 주입하는 방식을 뜻한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;최소한의 의존관계 정보만을 제공하면 Spring IoC Container 내에서 자동으로 객체(Bean) 을 관리하고, 클라이언트에게 필요한 유연한 의존성을 주입해준다. 이 의존관계 정보를 스프링에게 제공하는 방법은 아주 간단한 코드(어노테이션 등)를 작성하기만 하면 된다. 이에 대한 내용은 아래에서 다루어보겠다.&lt;/p&gt;
&lt;p&gt;기본적으로 Spring IoC Container 내에 등록되는 Bean 객체들은 &lt;code class=&quot;language-text&quot;&gt;싱글톤(SingleTon)&lt;/code&gt; 으로 관리된다. 또한 Spring IoC Container 로는 &lt;code class=&quot;language-text&quot;&gt;Bean Factory&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;Application Context&lt;/code&gt;  가 사용된다. 요즘은 Bean Factory 를 확장한 &lt;code class=&quot;language-text&quot;&gt;Application Context&lt;/code&gt; 를 관례적으로 사용하는 추세라고 한다. 이들에 대한 이해도는 아직 높지 않기 떄문에, 추가 학습이 필요하다.&lt;/p&gt;
&lt;h2 id=&quot;스프링-컨테이너에-bean-등록하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%97%90-bean-%EB%93%B1%EB%A1%9D%ED%95%98%EA%B8%B0&quot; aria-label=&quot;스프링 컨테이너에 bean 등록하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링 컨테이너에 Bean 등록하기&lt;/h2&gt;
&lt;p&gt;백문이불어일타. 직접 스프링 프레임워크에서 제공하는 기능들로 어떻게 Bean 을 스프링 컨테이너에 등록하고 관리하는지 간단한 코드를 작성해보자.&lt;/p&gt;
&lt;h3 id=&quot;bean-으로-의존성-주입하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bean-%EC%9C%BC%EB%A1%9C-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85%ED%95%98%EA%B8%B0&quot; aria-label=&quot;bean 으로 의존성 주입하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Bean 으로 의존성 주입하기&lt;/h3&gt;
&lt;p&gt;Spring Bean 을 스프링 컨테이너에 등록하는 방법에는 크게 3가지로 나뉜다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;(1) xml에 직접 등록&lt;/li&gt;
&lt;li&gt;(2) 수동으로 직접 의존관계 등록 : Bean 설정파일에 직접 Bean 을 등록&lt;/li&gt;
&lt;li&gt;(3) 자동으로 의존관계 등록 : 이미 제공되는 어노테이션 활용함. @Component, @Controller, @Service, @Repository 어노테이션을 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;과거에는 XML 로 빈을 설정하고 관리하는 방식이 이루어졌지만, 이젠  어노테이션을 활용하여 빈을 간편히 등록하고, DI 를 할 수 있도록 개선되었다. 사실상 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 은 요즘 거의 사용되지 않는 방법이니, 가볍게만 다루도록 한다.&lt;/p&gt;
&lt;h3 id=&quot;xml-에-직접-등록하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#xml-%EC%97%90-%EC%A7%81%EC%A0%91-%EB%93%B1%EB%A1%9D%ED%95%98%EA%B8%B0&quot; aria-label=&quot;xml 에 직접 등록하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;XML 에 직접 등록하기&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;bean&gt;&lt;/code&gt; 태그를 직접 사용하는 방식이다. application-config.xml 을 resource 아래에 만들어 준다. 그리고 &lt;beans&gt; 태그 안에 &lt;bean&gt;을 만들어 준다. 위의 코드는 의존성 주입까지 한 코드이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;bean id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;helloService&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;com.example.myapp.di.HelloService&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;bean id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;helloController&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;com.example.myapp.di.HelloController&quot;&lt;/span&gt; p&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;helloService&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ref&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;helloService&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;		
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;bean&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;bean-설정파일에-수동으로-직접-bean-을-등록&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bean-%EC%84%A4%EC%A0%95%ED%8C%8C%EC%9D%BC%EC%97%90-%EC%88%98%EB%8F%99%EC%9C%BC%EB%A1%9C-%EC%A7%81%EC%A0%91-bean-%EC%9D%84-%EB%93%B1%EB%A1%9D&quot; aria-label=&quot;bean 설정파일에 수동으로 직접 bean 을 등록 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Bean 설정파일에 수동으로 직접 Bean 을 등록&lt;/h3&gt;
&lt;p&gt;스프링 Bean 을 등록하는 가장 근본적인 코드이다. 자바 클래스를 생성하고, 그 위에 &lt;code class=&quot;language-text&quot;&gt;@Configuration&lt;/code&gt; 어노테이션을 명시해준다. 바로 아래에서 살펴볼 &lt;code class=&quot;language-text&quot;&gt;@Controller&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@Component&lt;/code&gt; 와 같은 어노테이션을 활용한 방법은 의존관계가 자동으로 등록되는 방식이라면, 이 방식은 Bean 의존관계를 수동으로 직접 등록하고자 할때 사용하는 방식이다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@Configuration&lt;/code&gt; 안에는 &lt;code class=&quot;language-text&quot;&gt;@Component&lt;/code&gt; 를 내부적으로 사용하기 떄문에 &lt;code class=&quot;language-text&quot;&gt;@Configuration&lt;/code&gt; 어노테이션을 붙인 해당 클래스는 &lt;code class=&quot;language-text&quot;&gt;@ComponentScan&lt;/code&gt; 의 대상이 된다. 그리고 그에따라 Bean 설정 정보가 읽힐 때, 그 안에 정의된 Bean 들이 IoC 컨테이너가 등록된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AppConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Item&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;item1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ItemImpl1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Store&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Store&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;item1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 아래처럼 Main 에서 어노테이션을 사용하기 위해 &lt;code class=&quot;language-text&quot;&gt;AnnotationConfigApplicationContext&lt;/code&gt; 를 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloMain&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;AbstractApplicationContext&lt;/span&gt; context &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnnotationConfigApplicationContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AppConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 

		&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-----------------------&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt; controller &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;helloController&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		controller&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;홍길동&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;=====================&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;이미-제공되는-어노테이션으로-자동으로-의존관계를-등록&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%AF%B8-%EC%A0%9C%EA%B3%B5%EB%90%98%EB%8A%94-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9C%BC%EB%A1%9C-%EC%9E%90%EB%8F%99%EC%9C%BC%EB%A1%9C-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84%EB%A5%BC-%EB%93%B1%EB%A1%9D&quot; aria-label=&quot;이미 제공되는 어노테이션으로 자동으로 의존관계를 등록 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이미 제공되는 어노테이션으로 자동으로 의존관계를 등록&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/df90a0b60dc8a79383f24a05ff94af80/29007/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.987730061349694%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACK0lEQVR42o2Ty08TURTGB4Nr/wUTE4krjTHGhQtW/jPu3RgWxoXxkbgxEhOXxgUpJRgU8EGikVBEAyJQsFBa2pnplM6zM9POo/PzzlDDIyy4ycmcOed+3zn3O/dKnFi9XkKjvopcmUetFlD3CiiVBZTqAs36D7TaIr5n93cnJ+FIh+5BMooiiitjrC2+oPjzFWuFl2wsjVJaeY1dz2HujWEZ8gEiOQNhEIRUtmZQd3LI23nkUvodR9nJi1ie+t8chn4GwjSZmmVZOI6N77t0fA/HNmk0FLpdP4t5Xhtd14U0PU5bxwgtqy2A0bHqYRhmRY7GOt0A23FPJ0ySiCj0UOWK6MgRqDA7ftpBLxJduSZKfTfzw8Aj7nem6/vi6K1sbxwLjigmrSepS3cpvbtIaeoK9c/XKM9cp21W8PQ1ml8u05wbQp69hPltiHbhJo2tt9Tmhql+ukF5+ira19vo87cwFoYJfQWp+f0O+pSEMTtI672EPHEOs7GO21ykOi6hTEo0JgcwpoU6Sxeo/nrMbu48tXyaG0CZkNA/SDgfBwmcbSSz/IbWn3toqyPo6yNYW48IOwZ+W0Vdvo/2ewSz+BBj4wHm5hMsbRl987nwn6IXD8zYfIZZGiUKHCRt32NPdVFbXQwnJO6LG8Ux5aqBZgSkY4r7lmq2U2lRrtnodkzLirDc+HAobtvGc53/7+QQJhRuO5YQPDwSF5b0xPDEbeh4/bvby2Ikcfb/D/SCINhjqIbUAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/df90a0b60dc8a79383f24a05ff94af80/a6d36/image-2.png&quot;
        srcset=&quot;/static/df90a0b60dc8a79383f24a05ff94af80/222b7/image-2.png 163w,
/static/df90a0b60dc8a79383f24a05ff94af80/ff46a/image-2.png 325w,
/static/df90a0b60dc8a79383f24a05ff94af80/a6d36/image-2.png 650w,
/static/df90a0b60dc8a79383f24a05ff94af80/e548f/image-2.png 975w,
/static/df90a0b60dc8a79383f24a05ff94af80/3c492/image-2.png 1300w,
/static/df90a0b60dc8a79383f24a05ff94af80/29007/image-2.png 1600w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;앞선 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 방식은 스프링 컨테이너에 개발자가 직접 추가적으로, 직접 수동으로 등록하고 싶은 특별한 객체가 존재할 떄 주로 사용할 것이다. 그러나, 대부분의 경우 스프링에서 이미 제공하고 있는 어노테이션들이 존재한다. 이들을 활용하면 자동으로 Bean 이 등록된다. 또한 이 어노테이션들을 중심으로 Bean 을 자주 등록하게 될 것이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@Controller&lt;/code&gt; : 해당 클래스가 Presentation Layer 에서 컨트롤러임을 명시함.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@Service&lt;/code&gt; : 해당 클래스가 Application Layer 에서 Service 임을 명시함.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@Repository&lt;/code&gt; : 해당 클래스가 Persistence Layer 에서 DAO 임을 명시한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@Component&lt;/code&gt; : 위 3가지 경우 이외에 Bean 으로 등록하고 싶은 클래스에 명시한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위 어노테이션들을 붙이면 스프링이 자동으로 &lt;code class=&quot;language-text&quot;&gt;컴포넌트 스캔&lt;/code&gt; 을 진행하여 각 객체들간의 의존관계를 읽어들인다. 그리고 그들을 기반으로 자동으로 Spring Bean 객체로 등록한다.&lt;/p&gt;
&lt;p&gt;추가적으로 &lt;code class=&quot;language-text&quot;&gt;@ComponentScan&lt;/code&gt; 이라는 어노테이션이 존재한다. 이 어노테이션이 붙어있는 클래스가 있는 패키지에서부터 모든 하위 패키지의 모든 클래스를 훑어보며 &lt;code class=&quot;language-text&quot;&gt;@Component&lt;/code&gt; 가 붙은 클래스를 찾는다.  또한 &lt;code class=&quot;language-text&quot;&gt;@Controller&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@Service&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@Repository&lt;/code&gt; 들은 모두  내부적으로 &lt;code class=&quot;language-text&quot;&gt;@Component&lt;/code&gt; 를 포함하고 있다는 점을 알고있자.&lt;/p&gt;
&lt;h4&gt;@Autowired 로 의존성 주입하기&lt;/h4&gt;
&lt;p&gt;스프링에서 Bean 인스턴스가 생성되면, @Autowired 를 설정한 메서드가 자동으로 호출되고, 파라미터에 명시한 Bean 인스턴스가 자동으로 주입된다. 즉, 이 어노테이션이 달린 생성자, setter, 필드는 직접 해당 객체를 생성할 필요없이 Spring IoC Container 에 등록된 Bean 을 자동으로 주입(DI) 해준다.&lt;/p&gt;
&lt;p&gt;위 어노테이션이 달리는 위치에 따라 &lt;code class=&quot;language-text&quot;&gt;생성자 주입&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;setter 주입&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;필드 주입&lt;/code&gt; 3가지로 나뉜다. 다만 생성자에 어노테이션을 단 경우, 생성자의 파라미터가 Bean 으로 등록되어 있어야 하며, setter 와 필드 또한 Bean 으로 등록되어 있어야 IoC Container 가 Bean 을 자동으로 주입해준다.&lt;/p&gt;
&lt;h4&gt;생성자 주입&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;setter 주입&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;필드 주입&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ApplicationContext, Bean Factory&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/reference/core/beans/introduction.html&quot;&gt;https://docs.spring.io/spring-framework/reference/core/beans/introduction.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev-wnstjd.tistory.com/440&quot;&gt;https://dev-wnstjd.tistory.com/440&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/spring-bean-and-spring-ioc-container/&quot;&gt;https://hudi.blog/spring-bean-and-spring-ioc-container/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@rnqjaah1536/Spring-Bean%EC%9D%B4%EB%9E%80&quot;&gt;https://velog.io/@rnqjaah1536/Spring-Bean이란&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://f-lab.kr/insight/understanding-spring-framework-and-ioc-di-container?gad_source=1&amp;#x26;gclid=CjwKCAjwy8i0BhAkEiwAdFaeGPMHTnKlKL1k0Xh4G3r6Vjbql71d7ns8CAlIiy0M5ffGK9SIGz1vtRoCzr8QAvD_BwE&quot;&gt;https://f-lab.kr/insight/understanding-spring-framework-and-ioc-di-container?gad_source=1&amp;#x26;gclid=CjwKCAjwy8i0BhAkEiwAdFaeGPMHTnKlKL1k0Xh4G3r6Vjbql71d7ns8CAlIiy0M5ffGK9SIGz1vtRoCzr8QAvD_BwE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@seasame_oil/Spring-bean&quot;&gt;https://velog.io/@seasame_oil/Spring-bean&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[POJO (Plain Old Java Object) 를 왜 지향해야 하는가?]]></title><description><![CDATA[POJO  란 말 그대로 오랜된 방식의 간단한 자바 객체를 뜻한다. 자세히는 특정 기술에 종속되어 있지 않은 상태의 객체를 말한다. 필드에 getter, setter 와 같은 정말 기본 기능만을 갖는 기본 객체가 바로 POJO…]]></description><link>https://haon.site/java/pojo/</link><guid isPermaLink="false">https://haon.site/java/pojo/</guid><pubDate>Mon, 15 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;pojo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pojo&quot; aria-label=&quot;pojo permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;POJO&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;POJO(Plain Old Java Object)&lt;/code&gt; 란 말 그대로 오랜된 방식의 간단한 자바 객체를 뜻한다. 자세히는 특정 기술에 종속되어 있지 않은 상태의 객체를 말한다. 필드에 getter, setter 와 같은 정말 기본 기능만을 갖는 기본 객체가 바로 POJO 를 만족하는 객체에 해당한다.&lt;/p&gt;
&lt;h2 id=&quot;pojo-는-왜-등장했는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pojo-%EB%8A%94-%EC%99%9C-%EB%93%B1%EC%9E%A5%ED%96%88%EB%8A%94%EA%B0%80&quot; aria-label=&quot;pojo 는 왜 등장했는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;POJO 는 왜 등장했는가?&lt;/h2&gt;
&lt;p&gt;POJO 가 왜 등장했는지를 이해하면, POJO 를 왜 지향해야하는지 이해할 수 있다. 이를 위해 등장배경을 살펴보자.  2000년 9월에 마틴 파울러, 레바카 파슨, 조시 맥킨지등이 사용하기 시작한 용어로써, 마틴 파울러는 다음과 같이 그 기원을 밝히고 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;우리는 사람들이 자기네 시스템에 보통의 객체를 사용하는 것을 왜 그렇게 반대하는지 궁금하였는데, 간단한 객체는 폼 나는 명칭이 없기 때문에 그랬던 것이라고 결론지었다. 그래서 적당한 이름을 하나 만들어 붙였더니, 아 글쎄, 다들 좋아하더라고. - 마틴 파울러 -&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;당시 Java EE 등의 중량 프레임워크를 사용하게 되면서 해당 프레임워크에 종속된 무거운 객체를 만들게 된 것에 반발해서 사용하게 된 용어다. 2000년 초반 당시 IT 기술이 발전하면서, 다양한 기술, 서비스가 점차 등장하게 된 시기였다. 하지만 문제점은, 생산성 그 자체에 집중하다보니 객체지향적인 설계를 포기하고 특정 기술에 의존하여 급급하게 개발하는 방식이 늘어났다. 이는 결과적으로 유연한 확장성을 낮추는, 유지보수가 어렵게 만드는 문제를 야기했다.&lt;/p&gt;
&lt;p&gt;이를 해결하기 위해 &lt;code class=&quot;language-text&quot;&gt;POJO&lt;/code&gt; 라는 개념이 등장했다. 본래 자바의 장점을 살리는 &lt;code class=&quot;language-text&quot;&gt;오래된&lt;/code&gt; 방식의 &lt;code class=&quot;language-text&quot;&gt;순수한&lt;/code&gt; 자바 객체의 중요성이 필요하여 등장했다. POJO 를 다시 정의하자면, &lt;strong&gt;자바 언어의 규약에 의한 제한사항 외의 그 어떠한 제한사항에도 구속받지 않는 (의존성을 최소화한) 자바 객체를 뜻한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;pojo-프로그래밍-규칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pojo-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EA%B7%9C%EC%B9%99&quot; aria-label=&quot;pojo 프로그래밍 규칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;POJO 프로그래밍 규칙&lt;/h2&gt;
&lt;p&gt;POJO 프로그래밍이란 POJO 객체를 만들기 위한 프로그래밍 설계 기법이다. 즉, 객체지향적인 원리에 충실하면서 특정 환경과 기술에 종속되지 않고, 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 개발하는 방식이다. 이 프로그래밍 규칙을 준수하기 위해선, 애플리케이션의 핵심 로직과 기능을 담아 설계하고, 개발해야한다.&lt;/p&gt;
&lt;p&gt;더 자세히 들어가, POJO 프로그래밍 규약에는 어떠한 것들이 존재할까?&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;JAVA 스팩외에 다른 기술이나 규약에 종속되지 않아야 한다. (미리 지정된 특정 기술, 클래스, 인터페이스등을 extend/implement 해선 안된다.)&lt;/li&gt;
&lt;li&gt;객체지향적인 설계를 해야한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;아래 코드는 &lt;code class=&quot;language-text&quot;&gt;getter&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;setter&lt;/code&gt; 만 가지고 있는 코드다. 이는 자바에서 제공하는 순수 기능만을 사용하므로, 즉 자바 언어 이외의 특정 기술에 종속되어 있지 않은 순수한 객체다. 따라서 POJO 라고 부를 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;반대로 아래는 POJO 라고 부르기 힘든 경우다. 즉, 특정 기술에 종속적인 코드다. &lt;code class=&quot;language-text&quot;&gt;ActionForm&lt;/code&gt; 클래스는 과거 Status 라는 웹 프레임워크에서 지원하는 클래스다. 이 클래스에서 제공하는 기능을 사용하기위해 &lt;code class=&quot;language-text&quot;&gt;extend&lt;/code&gt; 로 상속받고 있다. 이런 방식으로 특정 기술을 상속받게 되면, 애플리케이션 요구사항에 따라 다른 기술로 변경해야 하는 상황이 오면 Status 클래스를 활용한 코드를 모두 변경해야하기 때문에, 객체지향적인 설계라고 보기 힘들다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MessageForm&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ActionForm&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MessageAction&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ActionForward&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ActionMapping&lt;/span&gt; mapping&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ActionForm&lt;/span&gt; form&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;MessageForm&lt;/span&gt; messageForm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MessageForm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; form&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        messageForm &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello World&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; mapping&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findForward&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;결론적으로 JAVA 외의 다른 기술에 얽매이지 않고, 종속적이지 않으며, 특정 환경에 의존하지 않도록 해야 POJO 프로그래밍이라고 할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;진정한-pojo-란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%84%EC%A0%95%ED%95%9C-pojo-%EB%9E%80&quot; aria-label=&quot;진정한 pojo 란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;진정한 POJO 란&lt;/h3&gt;
&lt;p&gt;그렇다면 위 규약을 모두 지킨 객체는 &apos;진정한 의미의&apos; POJO 라고 할 수 있을까? 토비의 스프링에서는 진정한 POJO에 대하여 아래와 같이 이야기 한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 진정한 POJO란 객체지향적인 원리에 충실하면서, 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 말한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;토비님은 왜 이런 머리 아픈 이슈를 마지막에 던진것일까?자바가 객체지향적 설계의 장점을 포기하고 특정 기술과 환경을 사용하기에 급급했던 예전 시절의 고통을 잊지말자는 교훈을 던진것이 아닐까? 🙂&lt;/p&gt;
&lt;h2 id=&quot;pojo-프로그래밍으로-어떤-이점을-제공받는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pojo-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9C%BC%EB%A1%9C-%EC%96%B4%EB%96%A4-%EC%9D%B4%EC%A0%90%EC%9D%84-%EC%A0%9C%EA%B3%B5%EB%B0%9B%EB%8A%94%EA%B0%80&quot; aria-label=&quot;pojo 프로그래밍으로 어떤 이점을 제공받는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;POJO 프로그래밍으로 어떤 이점을 제공받는가?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;특정 환경이나 기술에 종속적이지 않으면 재사용이 가능하고, 유지보수 및 확장 가능한 코드를 작성할 수 있다.&lt;/li&gt;
&lt;li&gt;비즈니스 로직과 특정 환경 종속적인 코드를 분리하기 때문에 코드가 단순해진다.&lt;/li&gt;
&lt;li&gt;특정 환경에 종속적인 로직이 포함된 객체는 테스트가 어렵다. 하지만 POJO 는 테스트가 단순하고 쉽다.&lt;/li&gt;
&lt;li&gt;객체지향적인 설계를 제한없이 적용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;스프링-프레임워크는-어떻게-pojo-를-제공하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-pojo-%EB%A5%BC-%EC%A0%9C%EA%B3%B5%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;스프링 프레임워크는 어떻게 pojo 를 제공하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링 프레임워크는 어떻게 POJO 를 제공하는가?&lt;/h2&gt;
&lt;p&gt;이번에 POJO 를 학습하면서 새롭게 꺠달은 사실인데, 스프링 프레임워크는 POJO 프로그래밍 또한 유지하면서 여러 기술에 종속적이지 않도록 개발된 유연한 프레임워크라는 것을 세삼 알게 되었다.&lt;/p&gt;
&lt;p&gt;스프링 프레잌워크 등장 이전에는 원하는 기술이 있다면 그 기술을 직접 사용하는 객체를 설계해야했다. 그리고 이는 특정 기술과 환경에 의존하게 되는 문제로 이어졌고, 자바에서 제공하는 객체지향 설계의 이점들을 잃어버리게 되었다.&lt;/p&gt;
&lt;p&gt;그래서 POJO 라는 개념이 등장하게 된 것이다. 본디 자바의 객체지향이라는 장점을 살리기 위함이다. 보통 스프링 프레임워크를 사용할 때 &lt;code class=&quot;language-text&quot;&gt;ORM&lt;/code&gt; 이라는 기술을 사용하기 위해 &lt;code class=&quot;language-text&quot;&gt;Hibernate&lt;/code&gt; 를 프레임워크 기술을 사용한다. 하지만 Hibernate 라는 기술을 직접 사용하면 앞서 말했듯이 특정 기술에 종속적인 문제가 발생한다. 따라서 Hibernate 를 비롯한 여러 ORM 프레임워크들은 &lt;code class=&quot;language-text&quot;&gt;JPA&lt;/code&gt; 표준 인터페이스 아래에서 동작한다. 즉, Hibernate 를 비롯한 여러 ORM 프레임워크들은 이 JPA 라는 표준 인터페이스 아래에서 구현되고 실행된다. 따라서 프로그래머는 JPA 라는 표준 인터페이스 만으로 프로그래밍하고, Hibernate 를 비롯한 특정 ORM 기술에 직접 의존하지 않고 유연한 프로그래밍이 가능하다.&lt;/p&gt;
&lt;h2 id=&quot;더-학습해-볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4-%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해 볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해 볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Java Bean 과 POJO 의 차이점&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://siyoon210.tistory.com/120&quot;&gt;https://siyoon210.tistory.com/120&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ittrue.tistory.com/211&quot;&gt;https://ittrue.tistory.com/211&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wooj-coding-fordeveloper.tistory.com/80&quot;&gt;https://wooj-coding-fordeveloper.tistory.com/80&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[스프링 컨테이너의 제어의 역전(IoC) 과 의존성 주입(DI)]]></title><description><![CDATA[학습배경 카카오테크 교욱과정에 참여하면서, 정말 좋은 학습 키워드들을 만나고있다. 다음주부터 백엔드 과정을 본격적으로 진행하게 될텐데, 그 전에 스프링부트 프레임워크의 가장 근본이자 핵심 키워드인 IoC, Bean…]]></description><link>https://haon.site/spring/loc/</link><guid isPermaLink="false">https://haon.site/spring/loc/</guid><pubDate>Sun, 14 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;카카오테크 교욱과정에 참여하면서, 정말 좋은 학습 키워드들을 만나고있다. 다음주부터 백엔드 과정을 본격적으로 진행하게 될텐데, 그 전에 스프링부트 프레임워크의 가장 근본이자 핵심 키워드인 IoC, Bean 에 대해 제대로 되짚으며, 이들에 대한 생각을 확실히 정교화하고자 한다.&lt;/p&gt;
&lt;h2 id=&quot;제어의-역전-inversion-of-control&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%9C%EC%96%B4%EC%9D%98-%EC%97%AD%EC%A0%84-inversion-of-control&quot; aria-label=&quot;제어의 역전 inversion of control permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제어의 역전 (Inversion Of Control)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Don&apos;t call us. We&apos;ll call you.&quot; - Hollywood Principle&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Inversion Of Control, 우리 말로 번역했을 때 &lt;strong&gt;제어의 역전&lt;/strong&gt; 으로 번역할 수 있다. 무엇이 역전된다는 걸까? 흔히 IoC 를 설명할 때 위에서 설명했던 &lt;code class=&quot;language-text&quot;&gt;할리우드 원칙&lt;/code&gt; 을 들어서 설명하곤 한다. 이는 제어의 역전에 대한 비유적 표현이다. 말 그대로 배우들(객체) 에게 영화사에서 필요하면 연락할테니 먼저 연락하지 말라는 뜻이다.&lt;/p&gt;
&lt;p&gt;본론 내용으로 들어가보자. 우리가 프레임워크 사용없이 일반적인 코딩으로 작성해왔던 프로그램을 생각해보자. 객체의 생명주기(객체의 생성, 초기화, 소멸, 메소드 호출 등) 를 프로그래머가 직접 관리한다. 또한 다른 사람이 작성한 외부 코드(라이브러리) 를 호출하더라도 해당 코드의 호출 시점 역시 직접 관리한다.&lt;/p&gt;
&lt;p&gt;하지만, 프레임워크를 사용한다면 객체의 생명 주기를 모두 프레임워크에 위임할 수 있다. &lt;strong&gt;즉, 프로그램의 제어 흐름을 프로그래머가 직접 제어하는 것이 아니라, 외부 라이브러리(스프링 컨테이너)에서 프로그래머가 작성한 코드를 호출하고, 흐름을 제어하는 것을 제어의 역전(IoC) 라고 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;프레임워크의-ioc-기반-제어권&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC%EC%9D%98-ioc-%EA%B8%B0%EB%B0%98-%EC%A0%9C%EC%96%B4%EA%B6%8C&quot; aria-label=&quot;프레임워크의 ioc 기반 제어권 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프레임워크의 IoC 기반 제어권&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ce85361efe273d4590f48adfffb48b96/2aa89/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.48466257668711%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACHElEQVR42pWT22sTURDG+/f6JCL4LkLwpYrgg+AFX3zxQq20RQShSEMvia023aSbxDbbJJtsLpu9nr3m59nTKtgbdWA4e2Znvp1vvtkFztl8PscNPBzHxvEc9eyHPl4Rc2fKvcDnKls4H0izHNNqM/WPsd1fNNs7bG594bizyyzoMHZadPtN8nyuPl74tYB5njPxbE6qm1imwcQZY41Npt6UfmMfq6nhJ6KgcrMOJSKpPIZ37+FoDYoyEUaE8hy8eE31QYmN/Rpb5TJl6dVqlSRJLgcs2hehwJN07Pfv6OgGB6aPiCL13tve5mjtMy2zz4lhYJomo9FIsboAWIB5YUiSpuoepTlPVpvcebXL15qlYr2Z4OV6l6WdAa6Iic9yL6Ucyi6iOGboxNh+rGa0XBmwuNJG63kqp9qecvt5hdJSS+akqok4zS4CFnNK0ph61+H+W41Hn3ScIMG2ban4BMsaMp1OCEVErRewsdfisNGgLr2maf8o/bfDOIlpD1xKHzSerun4UYbZO8HoHHFQ+4kmC13XVbkjy0TXD9HqdTQJymWAQtINhSBMckRySmPsJaz9GKMPAnU/MGY8XGrwUY4CxSpTe3vl2gRyjoWi2ZlqyxWTW88qLMoRFLauWVKkPR6v6IxnLpFkdYM/JZNqCylQxNHQ4c23Y3aaloynuHILvrdHGJbzH4v9Z79lQZYX1HNZm6t1Ol2TOdfZb8dc2uf+Dd3nAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/ce85361efe273d4590f48adfffb48b96/a6d36/image.png&quot;
        srcset=&quot;/static/ce85361efe273d4590f48adfffb48b96/222b7/image.png 163w,
/static/ce85361efe273d4590f48adfffb48b96/ff46a/image.png 325w,
/static/ce85361efe273d4590f48adfffb48b96/a6d36/image.png 650w,
/static/ce85361efe273d4590f48adfffb48b96/e548f/image.png 975w,
/static/ce85361efe273d4590f48adfffb48b96/3c492/image.png 1300w,
/static/ce85361efe273d4590f48adfffb48b96/2aa89/image.png 1698w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;스프링부트와 같은 프레임워크를 사용할 때를 생각햅면, &lt;code class=&quot;language-text&quot;&gt;Controller&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Service&lt;/code&gt; 와 같은 객체들의 동작을 우리가 직접 구현하긴 하지만, 해당 객체들이 어느 시점에 호출될지는 전혀 신경쓰지 않는다. &lt;strong&gt;단지 프레임워크가 요구하는대로 객체를 생성하면, 프레임워크가 해당 객체들을 가져다가 알아서 생성하고, 메소드를 호출하고, 소멸시킨다.&lt;/strong&gt; 프로그램의 제어권이 역전된 것이다.&lt;/p&gt;
&lt;p&gt;이처럼 개발자가 작성한 객체가 메소드의 제어를 개발자가 아닌, 외부의 제 3자에게 위임하는 설계 원칙을 &lt;code class=&quot;language-text&quot;&gt;제어의 역전&lt;/code&gt; 이라고 한다. 즉, 프레임워크는 제어의 역전 개념이 적용된 대표적인 기술이라고 할 수 있다.&lt;/p&gt;
&lt;p&gt;제어의 역전은  원칙의 이름 그대로 제어에 대한 권한이 개발자에서 외부 환경으로 역전되는 것이다.  만약 전통적인 방식으로 라이브러리를 사용했다면, 이는 우리의 서비스의 일부분으로써 라이브러리를 가져와 우리가 직접 제어하는 것이다. 반면 &lt;strong&gt;IoC 는 우리의 코드가 프레임워크의 일부분이 되어, 프레임워크에 의해 제어되는 것이라 생각할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;ioc-를-통해-얻는-이점은-무엇인가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ioc-%EB%A5%BC-%ED%86%B5%ED%95%B4-%EC%96%BB%EB%8A%94-%EC%9D%B4%EC%A0%90%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot; aria-label=&quot;ioc 를 통해 얻는 이점은 무엇인가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;IoC 를 통해 얻는 이점은 무엇인가?&lt;/h3&gt;
&lt;p&gt;그렇다면 IoC 개념을 도입함으로써 무엇을 얻을 수 있을까? 가장 핵심적인 이점은 바로, 애플리케이션의 제어 책임이 프로그래머에서 프레임워크로 위임되므로, &lt;strong&gt;개발자가 핵심 비즈니스 로직 개발에 더 집중할 수 있게 된다.&lt;/strong&gt; 객체의 생멍주기 관리 (객체의 생성, 설정, 초기화, 메소드 호출, 소멸 등) 을 개발자가 아닌 프레임워크가 제어하기 떄문에, 우리는 비즈니스 로직 코드 작성에 집중하고 이 외의 객체 관리 부분을 프레임워크에게 맡기면 되기 때문이다.&lt;/p&gt;
&lt;h2 id=&quot;di-dependency-injection&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#di-dependency-injection&quot; aria-label=&quot;di dependency injection permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DI (Dependency Injection)&lt;/h2&gt;
&lt;p&gt;IoC 는 &lt;code class=&quot;language-text&quot;&gt;DI(Dependency Injection)&lt;/code&gt; 과 밀접한 연관이 있다. DI 는 IoC 원칙을 실현하기 위한 여러 디자인패턴 중 하나이다. IoC 와 DI 모두 객체간의 결합을 느슨하게 만들어, 유연하게 확성이 있는 코드를 작성하게 만들어준다.&lt;/p&gt;
&lt;h3 id=&quot;di-는-ioc-를-구현하기-위한-패턴이다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#di-%EB%8A%94-ioc-%EB%A5%BC-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%8B%A4&quot; aria-label=&quot;di 는 ioc 를 구현하기 위한 패턴이다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DI 는 IoC 를 구현하기 위한 패턴이다.&lt;/h3&gt;
&lt;p&gt;내가 IoC 와 DI 를 학습하면서 가장 혼동스러웠던 점은, DI 와 IoC 가 서로 동일한 개념이 아닌가였다. 나를 제외한 다른 사람들도 흔히들 IoC 와 DI 를 햇갈려하거나, 동일시한다. 하지만 IoC 와 DI 는 엄밀히 다른 개념이다. &lt;strong&gt;IoC 는 프로그램의 제어권을 제 3자인 외부 라이브러리(스프링 컨테이너 등) 에게 역전시키는 개념이다. 반면 DI 는 해당 개념을 구현하기 위해 사용하는 패턴 중 하나로, 이름 그대로 객체의 의존관계를 외부(스프링 컨테이너)에서 주입시키는 패턴을 뜻한다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;의존성-주입&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85&quot; aria-label=&quot;의존성 주입 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;의존성 주입&lt;/h3&gt;
&lt;p&gt;DI 란 이름 그대로 객체의 의존관계를 외부에서 주입시키는 패턴을 뜻한다고 했다. 즉, &lt;code class=&quot;language-text&quot;&gt;의존성 주입&lt;/code&gt; 이라는 개념이 들어가는데, 이 개념을 이해하기 위해 아래 코드를 살펴보자.&lt;/p&gt;
&lt;h4&gt;A는 B에게 의존한다&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같은 클래스 Controller 에선 Service 클래스 타입을 필드로 가진다. 그런데 만약 UserService 에 final 필드가 추가되는 변경이 일어난다면 어떨까? &lt;code class=&quot;language-text&quot;&gt;new&lt;/code&gt; 키워드로 UserService 객체를 생성하는 부분에서 오류가 발생한다. UserService 내부의 변경이 일어났는데, 이 변경이 A 에도 영향을 미치는 상황이다. 이런 경우를 UserController 가 UserService 에 의존한다고 표현한다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;의존성 주입&lt;/code&gt;이란, 이러한 의존성을 외부에서 주입해준다는 것을 뜻한다.  위 코드는 UserController 내부에서 UserService 객체를 직접 생성하고 이씨 떄문에, Controller 가 반드시 내부에서 생성한 Service 객체에 의존하는 것으로 의존관계가 항상 고정되어 있다. 하지만, 이렇게 의존성이 고정되는 것이 아닌, 외부에서 그때그떄 유연하게 의존성, 의존관계를 변경할 수 있도록 코드를 변경해보자.&lt;/p&gt;
&lt;h4&gt;외부에서 (생성자를 통한) 의존성 주입하기&lt;/h4&gt;
&lt;p&gt;외부에서 의존성을 주입받도록 변경했다. Controller 의 생성자를 통해 의존성을 주입받는다. 의존 대상을 직접 생성(결정)하는 것이 아니라, 생성자를 통해 외부로부터 주입받는 방식으로 변했다. 지금은 주입 대상인 UserService 가 단순 클래스 타입이지만, 클래스가 아닌 인터페이스 타입으로 추상화하면 다양한 구현체가 생성자를 통해 들어옴으로써 다양한 타입의 의존성을 주입시킬 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이처럼 &lt;code class=&quot;language-text&quot;&gt;생성자 주입&lt;/code&gt; 을 통해 외부에서 의존성을 주입시키는 방식을 &lt;code class=&quot;language-text&quot;&gt;DI(Dependency Injection)&lt;/code&gt; 이라고 하며, 이 DI 패턴을 통해 IoC 를 구현할 수 있는 것이다. 제 3자인 외부 라이브러리(스프링 컨테이너) 에게 객체의 생명주기 관리 제어권을 맡기기 위해선, 각 객체간의 의존관계 정보를 제공해줘야 하고, 따라서 의존성 주입을 해줘야한다. 이 의존성 주입은 추후 포스트에서 다루어 볼 스프링 어노테이션을 통해 주입해줄 수 있다.&lt;/p&gt;
&lt;p&gt;사실 생성자 주입외에도 setter, 인터페이스를 통한 의존성 주입 방식도 존재한다. 하지만 생성자 주입외에는 관례적으로 그리 권장되는 방식은 아니기에, 자세한 설명 및 학습은 생략한다.&lt;/p&gt;
&lt;h2 id=&quot;더-학습해-볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4-%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해 볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해 볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;스프링 Bean, 어노테이션 등&lt;/li&gt;
&lt;li&gt;의존성 주입을 어떻게 제공하는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/inversion-of-control/&quot;&gt;https://hudi.blog/inversion-of-control/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@ohzzi/Spring-DIIoC-IoC-DI-%EA%B7%B8%EA%B2%8C-%EB%AD%94%EB%8D%B0&quot;&gt;https://velog.io/@ohzzi/Spring-DIIoC-IoC-DI-그게-뭔데&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-04-27-dependency-injection/&quot;&gt;https://tecoble.techcourse.co.kr/post/2021-04-27-dependency-injection/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[스프링의 독립적인 계층화 아키텍처 (Layered Architecture)]]></title><description><![CDATA[계층형 아키텍처 (Layered Architecture…]]></description><link>https://haon.site/spring/layered-architecture/</link><guid isPermaLink="false">https://haon.site/spring/layered-architecture/</guid><pubDate>Sat, 13 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;계층형-아키텍처-layered-architecture&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%84%EC%B8%B5%ED%98%95-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-layered-architecture&quot; aria-label=&quot;계층형 아키텍처 layered architecture permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;계층형 아키텍처 (Layered Architecture)&lt;/h2&gt;
&lt;p&gt;계층형 아키텍처는 소프트웨어 개발 진영에서 널리 사용되는 아키텍처이다. 필자는 스프링부트 기반 소프트웨어를 개발중에 있지만, 단순 프레임워크에 국한되지 않고 여러 진영에서 사용될 수 있는 유용한 아키텍처 패턴이라 생각한다.&lt;/p&gt;
&lt;p&gt;구분되는 계층 숫자 갯수에 따라 N 계층 아키텍처 (N-tier Architecture) 이라고 한다. 각 계층은 애플리케이션 내에서 서로 독립적으로 구성되어 있기 때문에, 한 계층의 변경이 다른 계층에 영향을 주지않게 설계할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;n-tier-layered-architecture&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#n-tier-layered-architecture&quot; aria-label=&quot;n tier layered architecture permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;N-Tier Layered Architecture&lt;/h3&gt;
&lt;p&gt;필자가 계층형 아키텍처를 학습하며 가장 혼란스러웠던 점은, 각 계층을 정확히 명시하고 있지 않다는 점이다. 즉, 계층형 아키텍처의 구성은 딱 1가지로 정의되어 있지 않고, &lt;strong&gt;애플리케이션의 크기, 복잡도, 요구사항에 따라 자율적으로 구성하면 된다는 점이다.&lt;/strong&gt;  또한 계층형 아키텍처는 정확히 어떤 계층으로 구성했는지보단, &lt;strong&gt;계층을 분리하여 각 계층 사이의 의존성을 줄임&lt;/strong&gt;으로써 외부 변화로부터 비즈니스 로직의 변화를 박고, 애플리케이션의 유지보수와 확장성을 높이려는 목적으로 설계된 기법임이 더 중요하다.&lt;/p&gt;
&lt;h2 id=&quot;4-tier-layered-architecture&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-tier-layered-architecture&quot; aria-label=&quot;4 tier layered architecture permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4-Tier Layered Architecture&lt;/h2&gt;
&lt;p&gt;그럼에도 필자는 가장 일반적으로 구성되는 계층 아키텍처에 대해 학습하도록 한다. 전형적인 구성 방법을 학습하여, 이 학습이 향후 아키텍처 구성에 도움이 되기 위한 목적으로 작성해본다. 각 계층별 특징에 대해 학습해보자.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/07cf652fa01d48421d1b1f64d25c81cb/669eb/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 76.07361963190185%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAADh0lEQVR42mWU7U+bVRjG+0WI87s6P09nTCSyfwNfsvgZjWOoi5FlJsb4ElEXptvYxhwwcGR0HYwBLe1YihRYC12gLW0ppQP6Qnlr56CshdKHlhban+d5mILzPLly3899X7lyn1znHNX29japzU12d3cp5AskUglcS3Zcy3vQWjq53lHPLaOG611N1LfVobdqlZ474sC5ZCOxuYa8Cvk8Klkol8tRKBQUwa2MxNxKkLnVIO4ZJ46pMcam7IyP6XHa9Iz5HDh8NjwBt8IJrQSQtrf2BMWn4uAqwLqUYMDfhylgxDw/gMHVze0Ht9EYalHrLqAZ1HB/Uo85bGIg0Ce4RiLzHuaNo0Rtj/4vGEuuoJtWc8fdzA3LFZqHLnG5p4Zzmu+41FlNrfYsDX9eUOrtrmZ6Zlrxj99npk7LnHYElbzVf/UKebHlLN6FOJPzCSbDcTxzMRz+KNahAR6ajIwHnuAJxfCEn3EW11nfSDN07iaeDtP+hHuyeZZiaX7qifOjdk3B93cf83FNP5/8aqLi/BCVtVa+bV+mWnB+6Frl5541fMODtH1VjanpLqpsNks6nX7mcp709i4T00GcXh/OSS92t4epUBjHkB6bSYc3tIjLOyV6PmxOF57ZEKlkmo5vLjLaakAlOywL7uxkxXyQjfvIW0opWN6hMHyMQHsJ5qslOOqPYL1yhIHLbxNoKyE/XAojpSIeIxMaRPP1RYZbug+aklcEt1bsbN17EclQzPi1YjyNRdjqirBfO4S3+RD2qy/g+L1I1IsVTureS6yOtdByugbzja7/uiyOIblMks1ZA6lZHX9N6Fmwa3niNRLqPc+i8TeR9/HYpRPQk5rRIQV7ySRi9NfcxNc5eEBQPtgipOaCRE+dJPp5BcunTuCvLEd7/D16jr9L5wdlGD58H//JcqUnc6JfVBLp72a6UU9YZ31uQoHM0xhxrYanXWqm1c34WhuZ1rQw23AWf8MveNR/MHuriUfqJtYEJ67TsO6fINhrZXl0as+UTCZDXnZZCEpLC8xXVbD45QmWT4spq0Q88ymW8jKsH5UROfMZS1V7PZkTFlwpHNq/y8rjkEqxu5NTBJOTbsbfOkz/6y/z4OirWARGjr5C5xuvcUfALPKHbx7GLCD3nYKbdNr2BZ+7eeRFMZPcYGtjg/Q/EP+5jXV2BORcSiRYjUYFIsQiEZKCI0mS8sj8DdaI3ujmfrBfAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/07cf652fa01d48421d1b1f64d25c81cb/a6d36/image-1.png&quot;
        srcset=&quot;/static/07cf652fa01d48421d1b1f64d25c81cb/222b7/image-1.png 163w,
/static/07cf652fa01d48421d1b1f64d25c81cb/ff46a/image-1.png 325w,
/static/07cf652fa01d48421d1b1f64d25c81cb/a6d36/image-1.png 650w,
/static/07cf652fa01d48421d1b1f64d25c81cb/e548f/image-1.png 975w,
/static/07cf652fa01d48421d1b1f64d25c81cb/669eb/image-1.png 1244w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;내가 학습해볼 계층형 아키텍처는 &lt;code class=&quot;language-text&quot;&gt;Presentation Layer&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Application Layer&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Domain Layer&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Infrastructure Layer&lt;/code&gt; 총 4가지로 분류된다. 각 계층에 대해 학습해보자.&lt;/p&gt;
&lt;h3 id=&quot;presentation-layer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#presentation-layer&quot; aria-label=&quot;presentation layer permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Presentation Layer&lt;/h3&gt;
&lt;p&gt;서비스 사용자와 시스템간의 상호작용을 처리하는 것이 주 관심사인 계층이다. 사용자 인터페이스(UI) 를 담당하며, 사용자의 &lt;strong&gt;(1) 입력값을 검증&lt;/strong&gt;하고, &lt;strong&gt;(2) 비즈니스 로직이 어떻게 수행되는지 알 필요가 없도록 개발&lt;/strong&gt;해야한다. 대표적인 구성요소로는 View 와 Controller 가 있다. 즉, Presentation Layer 에선 비즈니스 로직을 처리하지 않아야하며, 단순히 UI 를 표시하는 역할만을 수행해야힌다.&lt;/p&gt;
&lt;h3 id=&quot;application-layer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#application-layer&quot; aria-label=&quot;application layer permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Application Layer&lt;/h3&gt;
&lt;p&gt;Presentation Layer 와 Domain Layer 비즈니스 로직을 수행하는 것이 주 관심사인 계층이다. &lt;strong&gt;그저 Persistence Layer 에서 데이터를 가져와 비즈니스 로직을 수행하고, 그 결과를 Presentation Layer 로 전달하는 역할이다.&lt;/strong&gt; 이 계층으로 구성되는 대표적인 구성요소로 &lt;code class=&quot;language-text&quot;&gt;Service&lt;/code&gt; 가 해당한다.&lt;/p&gt;
&lt;p&gt;이 계층에선 데이터베이스와 직접적으로 상호작용하지 않아야하며, &lt;strong&gt;Domain Layer 의 엔티티와 데이터를 처리하는 일만을 수행&lt;/strong&gt;야한다.&lt;/p&gt;
&lt;p&gt;종종 내가 현재 학습중인 현재 계층 아키텍처가 아니라, 프레젠테이션 계층, 비즈니스 계층, 데이터 자장소 계층 3가지 계층으로 구분하는 계층 아키텍처 패턴도 존재하는데, 이 패턴에선 Application Layer  비즈니스 로직과 비슷한 역할을 수행한다고 볼 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;domain-layer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#domain-layer&quot; aria-label=&quot;domain layer permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Domain Layer&lt;/h3&gt;
&lt;p&gt;비즈니르 로직을 처리하는 핵심 계층으로, 시스템의 핵심 로직이 담겨있다. 주요 역할로 &lt;strong&gt;데이터의 유효성 검증, 엔티티간의 관계(Relationship) 처리, 비즈니스 로직 수행&lt;/strong&gt;등을 수행한다. 또한 Domain Layer 에선 Infrastrcture Layer 계층을 직접 참조해선 안되며, 이 계층은 순수한 비즈니스 로직만을 수행해야한다.&lt;/p&gt;
&lt;h4&gt;Entity 는 Domain Layer 에 속하는것인가?&lt;/h4&gt;
&lt;p&gt;이전에 학습을 하며 항상 혼동되었던 부분은 JPA 로 설계한 ORM 기반 Entity 가 Domain Layer 로 볼 수 있는가였는데, 내가 현재까지 이해한바로는 이 아키텍처에선 Domain Layer 에 속하는것으로 바라보는 것이 올바르다.  이전에 &lt;a href=&quot;https://haon.blog/spring/dao-repository/&quot;&gt;DAO 와 Repository 패턴, 차이점은 무엇인가 🤷
&lt;/a&gt; 에서 다루었듯이 Repository 는 DAO 와 달리 도메인 객체의 상태를 관리 및 저장하는 개념에 가깝다고 했었는데, 이때 도메인 객체를 바로 엔티티의 개념으로 충분히 바라볼 수 있다. 즉, 상황에 따라 충분히 엔티티는 도메인 객체의 개념이 될 수 있다.&lt;/p&gt;
&lt;p&gt;또한 Repository 는 객체의 정보를 가진 저장소에 대한 관리 에 대한 책임을 위임받은 인터페이스이다. Spring Data JPA 로 구현한 Repository 는 Domain 객체인 엔티티에 접근하는 메소드를 제공하는 방식인 것이다. 결론적으로 ORM 기반의 엔티티와 레포지토리 모두 Domain Layer 에 배치시키는 것이 적합하다.&lt;/p&gt;
&lt;p&gt;아무것도 몰랐을 땐 엔티티와 Repository 는 Persistence Layer 에 속하는 개념이라고 생각했는데, 큰 오산이었다 🥲&lt;/p&gt;
&lt;h3 id=&quot;infrastructure-layer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#infrastructure-layer&quot; aria-label=&quot;infrastructure layer permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Infrastructure Layer&lt;/h3&gt;
&lt;p&gt;시스템의 &lt;strong&gt;하부 기술적인 부분을 담당하며&lt;/strong&gt;, &lt;strong&gt;데이터베이스, 네트워크 파일 시스템등과 관련한 로직을 수행&lt;/strong&gt;한다. 주요 역할은 &lt;strong&gt;시스템의 기술적인 문제 해결&lt;/strong&gt;, 그리고 &lt;strong&gt;데이터의 영속성을 보장하는 것&lt;/strong&gt;이다. Spring JDBC, DAO, &lt;code class=&quot;language-text&quot;&gt;@Repository&lt;/code&gt; 어노테이션이 명시된 특정 레포지토리등을 활용하여 데이터베이스와 통신함으로써 애플리케이션의 영속성을 구현할 수 있다. 반면 Spring Messaging 을 사용하여 외부 메세징 시스템과 상호작용할 수도 있다.&lt;/p&gt;
&lt;p&gt;이 계층에선 도메인 계층과의 상호작용을 최소화해야 한다. 자칫 데이터베이스 중심적인 설계방식은 유지보수, 확장성에 용이한 코드를 만들지 못하게 하므로, 유의해야 할 부분이다.&lt;/p&gt;
&lt;h2 id=&quot;구성은-자유롭게-단-반방향으로-구성할-것&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B5%AC%EC%84%B1%EC%9D%80-%EC%9E%90%EC%9C%A0%EB%A1%AD%EA%B2%8C-%EB%8B%A8-%EB%B0%98%EB%B0%A9%ED%96%A5%EC%9C%BC%EB%A1%9C-%EA%B5%AC%EC%84%B1%ED%95%A0-%EA%B2%83&quot; aria-label=&quot;구성은 자유롭게 단 반방향으로 구성할 것 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;구성은 자유롭게, 단 반방향으로 구성할 것&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1be1d50fab6ec4b458c53d3a67afd604/37048/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.71165644171779%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAADgUlEQVR42n2Sy28bVRTG538AVqwQGzYskIqQWja8BEKsKWKNith0w4JFUUlRixAFAS1NrTSJiOPHxM8Zx7HH9ozfdvyM04bEbuI0zjtO4mdSUJSQHzehbFl8+s45d+4395zvSO1ujzN0un1agmcrs9SqNfoHT+mKvNvr/y86Ar2DQ3qH4vv+AdL+H2H+Wk5wuBjj6UqKdspLO+mhW4/TqUbP0a3q/8a12LPYeBZH6dUMWo9C7M0EaAstqaEPshE1sW7cZ68wxp/euxAxEdAVBsZ83JW9FAyZ720+blr8xINWxlWF62Y/Vr+PVmSQxKuXyL58keR7HyDt5u0cPvTQr7jO+fixn9Oqyvy0Hz0RIZMK0igoxJNhYkmdpaxCORskktCZyWn0cnbKX16lcOVzKje+QlpKO9kq+VjPK6wJXlJGzrE7I9rOWWiLH27nXXTyNjoibxbd7Ofl87N9IbZWUllbjdFYi7LxxEBaj4/QKVjFRQsnSyp9928cBU1kM37MAQOPFmAu6cQeDGPVopSiE4T0KcaCUcKxIK34MLMff8rcR5eZ/eIzIZgYFmJm2jkzJzUvp0KUukImrvJA1XAEppjPejH7g4xOapTjTjQjwJCioQnBbmqE9Nsfkr7wLplPLiPV0y62ypOsF1QaeZUnUZnlsJXtkodWwSmMcrKZF20K3i+K8RQ87OSdtIoOmoIb4t5KLUR9IUBjXkPaTI/TLTtoF2UOhRkt+68cqIPUSpPEIgrTMS+rORepqDDGUKlnnFSSXqK6SiWliFnaWPzhOks3r1EfvIW0FhuimR5lR6BfsXP6yAULTrE2fm65E5jUMPm4m5+8MW4rSWIhMU8tLM5SyBGdfd1E6pWL5F58g+Rb74s9TMtiKb3sCPd2SoKnHWyL2o5osTltYyM7QafkZDsrs56R6ZWdbExPiNzObt4hRuBmPjZOJTRKNW5B2hJz6M9N0p1VxeYH2Jy4x546xEJxEofPxWregz/oJh31UMt6kEWtIWpTmpuE4aZz9hhdrFPITDNuF6aE73PW9pZw+6jq5ihjgYqFq6MepK+n+ebBGC98a/Dmz0Gu3BEvGChwzfQ7z98weP0XnaZ2j8hLFzCee43IpXeQOkUrx/MejuZcnAj+e9EHyz6Kup3bo2LgKZkRmxWfYmfmv1rSzojVhtcrc/xwgvrwdzy+M8CK9Uf+AeJFhwiY6yhsAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/1be1d50fab6ec4b458c53d3a67afd604/a6d36/image-2.png&quot;
        srcset=&quot;/static/1be1d50fab6ec4b458c53d3a67afd604/222b7/image-2.png 163w,
/static/1be1d50fab6ec4b458c53d3a67afd604/ff46a/image-2.png 325w,
/static/1be1d50fab6ec4b458c53d3a67afd604/a6d36/image-2.png 650w,
/static/1be1d50fab6ec4b458c53d3a67afd604/e548f/image-2.png 975w,
/static/1be1d50fab6ec4b458c53d3a67afd604/3c492/image-2.png 1300w,
/static/1be1d50fab6ec4b458c53d3a67afd604/37048/image-2.png 1352w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;계층형 아키텍처에서 각각의 분할된 수평 계층은 수직적으로 배치된다. 이런 구조에서 특정 레이어는 바로 하위 레이어에만 연결된다. 즉, 계층형 아키텍처에서는 계층간의 의존성이 최소화되어야 하므로, &lt;strong&gt;각 계층은 단방향으로 상위 계층의 기능만 사용할 수 있도록 구현해야 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;각 계층은 의존성을 최소화하고, 격리되어 있어야한다. 각 계층은 다른 계층의 내부 동작을 모르도록 설계해야한다. 각 계층은 캡슐화되어있고, 단일 책임을 가져야한다. 따라서 따라서 특정 레이어는 다른 레이어에 영향을 주지 않고 변경될 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/cs/layered-architecture&quot;&gt;https://www.baeldung.com/cs/layered-architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/layered-architecture/&quot;&gt;https://hudi.blog/layered-architecture/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://engineerinsight.tistory.com/63&quot;&gt;https://engineerinsight.tistory.com/63&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Object.equals의 일반 규약을 지켜서 재정의하라]]></title><description><![CDATA[학습베경 equals…]]></description><link>https://haon.site/haon/java/equals-override/</link><guid isPermaLink="false">https://haon.site/haon/java/equals-override/</guid><pubDate>Sat, 13 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습베경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B2%A0%EA%B2%BD&quot; aria-label=&quot;학습베경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습베경&lt;/h2&gt;
&lt;p&gt;equals() 를 공부했었지만, 간혹 필요에 따라 재정의를 해줄때가 있었습니다. 하지만 깊은 이해 없이 사용하다보니, 어떻게 정의를 하는것이 좋으며, 컴파일 및 문법상의 에러는 없는것인지 항상 고민이 따랐는데, 이번 기회에 제대로 학습해본 후에 적용해보고자 합니다.&lt;/p&gt;
&lt;h2 id=&quot;objectequals&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#objectequals&quot; aria-label=&quot;objectequals permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Object.equals&lt;/h2&gt;
&lt;p&gt;일반적으로 활용했던 &lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 를 떠올렸을땐, 동등성 비교를 위해 &quot;String&quot; 과 같은 레퍼 클래스에서 많이 사용해봤을 겁니다. 하지만 주의해야 할 점은 &lt;code class=&quot;language-text&quot;&gt;Obects.equals()&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;String.equals()&lt;/code&gt; 는 엄연히 다른것임 동작 방식도 큰 차이를 보입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Object.class&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Object 클래스에서 정의하고 있는 &lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 정의부는 위와 같습니다. &lt;code class=&quot;language-text&quot;&gt;==&lt;/code&gt; 비교를 통해 두 객체를 비교하고 있는 모습으로, &lt;code class=&quot;language-text&quot;&gt;주소값 비교&lt;/code&gt; 를 수행하는 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EqualTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; bookName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; year&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; bookName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; year&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bookName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bookName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;year &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; year&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; 동등성을_테스트한다&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;my-book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2023&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;my-book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2023&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Assertions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;book1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;book2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 테스트 실패&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;때문에, 위와 같은 사용자 정의 클래스를 정의후 동등성을 비교했을때 테스트에 실패하게 됩니다. 기본적으로 사용자 정의 클래스는 &lt;code class=&quot;language-text&quot;&gt;Object&lt;/code&gt; 를 상속하고 있으며, 때문에 equals() 를 호출시 &lt;code class=&quot;language-text&quot;&gt;Object.equals()&lt;/code&gt; 가 호출되므로 &quot;주소값 비교&quot; 가 수행될 것이고, 두 객체의 주소값은 다르기 때문에 비교결과가 false 로 출력됩니다. 즉, 같은 값을 가진 객체더라도 따로 생성됐다면 다른 객체로 인식되는 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;stringequals&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stringequals&quot; aria-label=&quot;stringequals permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;String.equals&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; str1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; str2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;str1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;str2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;반면 값 클래스(String, Integer, ..) 들은 Object.equals() 를 재정의하여 사용하도록 구현되어 있기 때문에 동작방식의 차이를 보입니다. String 을 사용할때 자주 경험했겠지만, equals() 는 주소값이 다름에도 &lt;strong&gt;내부 값이 동일하다면 결과가 true&lt;/strong&gt; 이라는 반대의 특징을 지니고 있습니다.&lt;/p&gt;
&lt;p&gt;정리하자면, &lt;strong&gt;모든 클래스는 Object의 equals 를 상속 받고 있고, String 을 비롯한 값 클래스들은 Object의 equals를 재정의 했다고 할수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;equals-를-재정의하지-않는게-좋은-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#equals-%EB%A5%BC-%EC%9E%AC%EC%A0%95%EC%9D%98%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94%EA%B2%8C-%EC%A2%8B%EC%9D%80-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;equals 를 재정의하지 않는게 좋은 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;equals() 를 재정의하지 않는게 좋은 경우&lt;/h2&gt;
&lt;p&gt;Object 클래스의 &lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 는 객체의 동일성을 비교하는 공통 메소드라고 할 수 있습니다. 개발자가 필요에 따라서 equals 를 재정의 할 수 있을텐데, 무턱데고 정의하다가 예기치 못한 문제가 발생할 수 있으므로, 아래의 경우중 하나라도 해당한다면 메소드를 재정의 하지 않는것이 좋습니다.&lt;/p&gt;
&lt;h3 id=&quot;1-논리적-동일성을-검증할-필요가-없다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EB%85%BC%EB%A6%AC%EC%A0%81-%EB%8F%99%EC%9D%BC%EC%84%B1%EC%9D%84-%EA%B2%80%EC%A6%9D%ED%95%A0-%ED%95%84%EC%9A%94%EA%B0%80-%EC%97%86%EB%8B%A4&quot; aria-label=&quot;1 논리적 동일성을 검증할 필요가 없다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 논리적 동일성을 검증할 필요가 없다.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;equals() 메소드는 논리적 동일성을 검사하는 메소드입니다.&lt;/strong&gt; 때문에 논리적 동일성을 검삭할 필요가 없다면, equals() 를 재정의할 필요가 없습니다. 예를들어 java.utils 의 두 &lt;code class=&quot;language-text&quot;&gt;Pattern&lt;/code&gt; 클래스 타입의 인스턴스는 정규표현식을 나타내는지를 검사하는, 즉 논리적 동치성을 검사하는 방법이 있습니다. 이 클래스의 경우는 이미 equals() 없이도 논리적 동일성을 검사할 필요가 없으므로, equals() 를 재정의할 필요가 없습니다.&lt;/p&gt;
&lt;h3 id=&quot;2-상위-클래스에서-재정의한-equals-는-하위-클래스에서도-그대로-사용-가능하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EC%83%81%EC%9C%84-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%97%90%EC%84%9C-%EC%9E%AC%EC%A0%95%EC%9D%98%ED%95%9C-equals-%EB%8A%94-%ED%95%98%EC%9C%84-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%97%90%EC%84%9C%EB%8F%84-%EA%B7%B8%EB%8C%80%EB%A1%9C-%EC%82%AC%EC%9A%A9-%EA%B0%80%EB%8A%A5%ED%95%98%EB%8B%A4&quot; aria-label=&quot;2 상위 클래스에서 재정의한 equals 는 하위 클래스에서도 그대로 사용 가능하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 상위 클래스에서 재정의한 equals 는 하위 클래스에서도 그대로 사용 가능하다.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;equals 메소드는 Object 클래스에서부터 정의되어있고, 상위 클래스부터 하위 클래스까지 상속됩니다.&lt;/strong&gt; 따라서 상위 클래스에서 정의한 equals 를 하위 클래스에서도 그대로 활용해도 문제 없다면, 이 메소드를 재정의할 필요가 없습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Parent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
   &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; o&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;token comment&quot;&gt;// ... (1)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Child&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Parent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// ... 부모의 (1) 에서 재정의한 equals() 내용이 그대로 상속된다.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;양질의-equals-메소드-구현방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%91%EC%A7%88%EC%9D%98-equals-%EB%A9%94%EC%86%8C%EB%93%9C-%EA%B5%AC%ED%98%84%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;양질의 equals 메소드 구현방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;양질의 equals 메소드 구현방법&lt;/h2&gt;
&lt;p&gt;올바른 equals() 를 구현하기 위한 방법은 아래와 같습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; o&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;o &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;o &lt;span class=&quot;token keyword&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;o&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;o&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;book&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;book&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;year &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;1--연산자를-활용하여-입력이-자기-자신인지-확인한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1--%EC%97%B0%EC%82%B0%EC%9E%90%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%98%EC%97%AC-%EC%9E%85%EB%A0%A5%EC%9D%B4-%EC%9E%90%EA%B8%B0-%EC%9E%90%EC%8B%A0%EC%9D%B8%EC%A7%80-%ED%99%95%EC%9D%B8%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;1  연산자를 활용하여 입력이 자기 자신인지 확인한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. == 연산자를 활용하여 입력이 자기 자신인지 확인한다.&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에 해당하는 내용으로, 입력받은 인스턴스가 자기 자신이라면 true 를 반환하는 로직을 작성해주면 됩니다. 이는 단순한 성능 최적화 용도로, 비교적 작업이 복잡한 상황일 때 값어치를 합니다.&lt;/p&gt;
&lt;h3 id=&quot;2-instaneof-연산자로-입력이-올바른-타입인지-확인한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-instaneof-%EC%97%B0%EC%82%B0%EC%9E%90%EB%A1%9C-%EC%9E%85%EB%A0%A5%EC%9D%B4-%EC%98%AC%EB%B0%94%EB%A5%B8-%ED%83%80%EC%9E%85%EC%9D%B8%EC%A7%80-%ED%99%95%EC%9D%B8%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;2 instaneof 연산자로 입력이 올바른 타입인지 확인한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. instaneof 연산자로 입력이 올바른 타입인지 확인한다.&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 에 해당하는 내용으로, &lt;code class=&quot;language-text&quot;&gt;instanceof&lt;/code&gt; 연산자로 입력이 올바른 타입인지 확인하고, 그렇지 않다면 false 를 반환하는 로직을 작성해줍시다. 이때 올바른 타입이란 equals 가 정의된 클래스(위의 경우는 Book) 인 것이 보통이지만, 가끔은 그 클래스가 구현한 특정 인터페이스가 될 수도 있습니다. 어떤 인터페이스는 상황에 따라 자신을 구현한 (서로 다른) 클래스끼리도 비교할 수 있도록 equals 규약을 수정하기도 합니다.&lt;/p&gt;
&lt;h3 id=&quot;3-입력을-올바른-타입으로-형변환한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%EC%9E%85%EB%A0%A5%EC%9D%84-%EC%98%AC%EB%B0%94%EB%A5%B8-%ED%83%80%EC%9E%85%EC%9C%BC%EB%A1%9C-%ED%98%95%EB%B3%80%ED%99%98%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;3 입력을 올바른 타입으로 형변환한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 입력을 올바른 타입으로 형변환한다.&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 에서 instanceof 검사를 했기 때문에, 위 예제에서 이 단계는 문제없이 성공합니다.&lt;/p&gt;
&lt;h3 id=&quot;4-타입의-비교에-필요한-핵심필드를-모두-비교한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-%ED%83%80%EC%9E%85%EC%9D%98-%EB%B9%84%EA%B5%90%EC%97%90-%ED%95%84%EC%9A%94%ED%95%9C-%ED%95%B5%EC%8B%AC%ED%95%84%EB%93%9C%EB%A5%BC-%EB%AA%A8%EB%91%90-%EB%B9%84%EA%B5%90%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;4 타입의 비교에 필요한 핵심필드를 모두 비교한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 타입의 비교에 필요한 핵심필드를 모두 비교한다.&lt;/h3&gt;
&lt;p&gt;입력 객체와 자기 자신이 대응되는 &quot;핵심&quot; 필드들이 모두 일치하는지 하나씩 검사하도록 구현합시다. 모든 필드가 일치하면 true 를, 하나라도 다르면 false 를 반환하면 됩니다. 위 경우는 Book 클래스의 멤버 필드중에 name 와 year 를 모두 비교했습니다.&lt;/p&gt;
&lt;p&gt;또한 너무 복잡하게 필드들을 비교할 필요도 없습니다. 필드들의 동치성만 검사해도, equals 재정의가 원활하게 이루어진 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;6-object-외의-타입을-매개변수로-받지말자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-object-%EC%99%B8%EC%9D%98-%ED%83%80%EC%9E%85%EC%9D%84-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EB%A1%9C-%EB%B0%9B%EC%A7%80%EB%A7%90%EC%9E%90&quot; aria-label=&quot;6 object 외의 타입을 매개변수로 받지말자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. Object 외의 타입을 매개변수로 받지말자.&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; o&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; o&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Object&lt;/code&gt; 타입 외의 타입을 매개변수로 받는 equals() 메소드는 선언하지 않는게 좋습니다. 예를들어 위와 같은 경우가 많은 개발자들이 자주 범하는 실수라고 하는데, 두 케이스 모두 입력타입이 Object 가 아니므로 Object 클래스의 equals 를 재정의한게 아니라, &lt;code class=&quot;language-text&quot;&gt;다중상속&lt;/code&gt; 한 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 의 경우 Object.equals 를 재정의한 것이 아님에도, 하위 클래스에서의 &lt;code class=&quot;language-text&quot;&gt;@Override&lt;/code&gt; 가 Object.equals 를 재정의한것을 올바르게 상속 받았다고 잘못 인식하게 되는 &quot;긍정 오류&quot; 를 내게 됩니다.&lt;/p&gt;
&lt;p&gt;그렇다고 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 의 경우도 올바른 정의 방법이 아닙니다. 재정의를 위해선 상위 클래스의 &lt;code class=&quot;language-text&quot;&gt;매개변수 타입&lt;/code&gt; 까지 모두 동일해야 재정의가 되는 것인데, Object.equals 의 매개변수 타입은 &quot;Object&quot; 이고, 위 경우는 구체 클래스인 Book 타입이므로 재정의가 불가능하고 컴파일 에러가 발생합니다.&lt;/p&gt;
&lt;h3 id=&quot;7-hashcode-도-함께-재정의하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#7-hashcode-%EB%8F%84-%ED%95%A8%EA%BB%98-%EC%9E%AC%EC%A0%95%EC%9D%98%ED%95%98%EC%9E%90&quot; aria-label=&quot;7 hashcode 도 함께 재정의하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7. hashCode 도 함께 재정의하자.&lt;/h3&gt;
&lt;p&gt;이와 관련한 내용은 아직 잘 이해하지 못했으므로, 추후 포스팅에서 충분한 학습을 거친후에 다시 다루도록 하겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;꼭 필요한 경우가 아니라면 equals 를 재정의하지 맙시다. 대다수의 경우에 Object 의 equals 는 저희가 원하는 비교를 정확히 수행해주기 때문입니다. 재정의가 필요할때는, 그 클래스의 핵심 필드를 모두 빠짐없이 앞서 언급한 사항들을 준수하면서 정의해주는게 좋습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Effective Java (Joshua Bloch 지음)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nankisu.tistory.com/99&quot;&gt;https://nankisu.tistory.com/99&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.appletong.com/entry/JAVA-object-equals-String-equals-%EB%8A%94-%EB%AD%90%EA%B0%80-%EB%8B%A4%EB%A5%BC%EA%B9%8C&quot;&gt;https://www.appletong.com/entry/JAVA-object-equals-String-equals-는-뭐가-다를까&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lcs1245.tistory.com/entry/Java-equals-%EC%B4%9D-%EC%A0%95%EB%A6%AC-%EA%B0%92-%EB%B9%84%EA%B5%90%ED%95%98%EA%B8%B0-Objectsequals&quot;&gt;https://lcs1245.tistory.com/entry/Java-equals-총-정리-값-비교하기-Objectsequals&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[DAO 와 Repository 패턴, 차이점은 무엇인가 🤷]]></title><description><![CDATA[학습배경 이번 카카오테크 교육과정에 참여하면서 내가 세운 목표중 하나는 이미 잘 알고있을 것 같은 지식도, 조금이라도 모호하게 알고있는 지식들도 글쓰기를 통해 생각을 머리 밖으로 인출(Output…]]></description><link>https://haon.site/spring/dao-repository/</link><guid isPermaLink="false">https://haon.site/spring/dao-repository/</guid><pubDate>Fri, 12 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;이번 카카오테크 교육과정에 참여하면서 내가 세운 목표중 하나는 이미 잘 알고있을 것 같은 지식도, 조금이라도 모호하게 알고있는 지식들도 글쓰기를 통해 생각을 머리 밖으로 인출(Output) 하는 것이다. 메타인지 활성화를 위해 글을 하나 또 적을것이 생겼는데, 바로 DAO 와 Repository 의 명확한 차이점이다. 둘의 차이점을 명확히 알기위해 글을 작성해본다.&lt;/p&gt;
&lt;h2 id=&quot;dao-repository-패턴은-왜-등장했는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dao-repository-%ED%8C%A8%ED%84%B4%EC%9D%80-%EC%99%9C-%EB%93%B1%EC%9E%A5%ED%96%88%EB%8A%94%EA%B0%80&quot; aria-label=&quot;dao repository 패턴은 왜 등장했는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DAO, Repository 패턴은 왜 등장했는가?&lt;/h2&gt;
&lt;p&gt;우리가 하나의 애플리케이션을 구현할 때, &lt;code class=&quot;language-text&quot;&gt;영속성(Data Persistence)&lt;/code&gt; 을 가진 영구저장소를 필요로 하는 경우가 많다. 영구 저장소라 함은 대표적으로 MySQL, MongoDB 와 같은 데이터베이스가 해당 될 것이다.&lt;/p&gt;
&lt;p&gt;애플리케이션에서 영구저장소에 접근하기 위해선, 각 영구저장소 표준에서 제공하는 API 를 사용해야한다. 이때, 만약 영구저장소의 API 를 직접 활용하는 로직이 우리 서비스의 핵심 비즈니스 로직과 함께 존재한다면 어떠할까?&lt;/p&gt;
&lt;p&gt;아래와 같이 UserService 에서 회원가입을 수행하는 signup 메소드를 정의했다고 해보자. 또한 보듯이 Service 계층에서 쿼리문을 직접 실행하고, 영구저장소 표준 API 에 따라 정확한 쿼리문과 로직을 수행해야한다. 이는 무엇이 문제가 될까?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;signup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ... &lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;INSERT INTO MEMBER(name, password) VALUES(?, ?)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ... &lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;우선 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; &lt;strong&gt;구현체와 로직이 지나치게 강한 결합성을 가지게 된다.&lt;/strong&gt; 즉, 애플리케이션의 핵심인 Service 레이어에서 영구저장소의 API 를 직접 사용하기 떄문에, 강한 결합을 가진다는 것이다. 만약 영구저장소를 MySQL 에서 MongoDB 로 변경헀을 때, 기존 비즈니스 로직 코드에서 영구저장소 API 를 사용한 모든 비즈니스 로직 코드를 변경해야한다. 즉, &lt;code class=&quot;language-text&quot;&gt;OCP(개방 폐쇄 원칙)&lt;/code&gt; 을 워반하는 사례가 된다.&lt;/p&gt;
&lt;p&gt;또한 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; &lt;strong&gt;레이어드 계층간 간섭 문제가 발생한다.&lt;/strong&gt; 전통적인 레이어드 아키텍처는 보통 아래와 같은 방식으로 구성되는데, 영구저장소에 대한 API 활용 코드가 애플리케이션 계층에 존재하면 계층 구조가 무너진다. 이는 곧 데이터베이스 중심적인 개발로 이어질 수 있게 된다. 레이어드 아키텍처의 이점은 모듈화, 유연성, 확장성을 제공하기 위함인데, 이 이점을 누리기 위해선 &lt;strong&gt;비즈니스 로직과 영구저장소의 API 를 분리할 필요가 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e9f606e457ca00bbf380325a8d9c1edd/9239a/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 76.07361963190185%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAADo0lEQVR42l2U20+bdRjHuZs4b4wxTk280qHemIiZF/4BJmZqNN4t6mDgNqPLvNEEMwXN5li2iRQCDIw4Wg6lpQLjMKG2tBRaSo8DVko7SukoFFqgR3r8+L5j6uT35pv88jxvPnmOv6JkMkksFiOby1LIF4gmolj8Jmb9RiyrJlQ6BW2KZqSjMprlLTR1SVBq5JhX9v0mn4GtaAjxFAoFitLpNJlMhnwhTz6fJ7WXxLPhxhNyY3GZmXYY0NsNTOpVzEz2ondMYbDpsCzOsrS+iDvoIpqM7gOFryibzfLPESNMpBL85b7NuHsE7fIYQ3YVnRoZPaMN3Oy9RMe4lGFnPxOCT+0eZcx1C7/PgV9tZtXgPAiEnfgOyoXf6bS10Kq9TrP6KleVNVzsqOKyrIpaeTUNI7U0/XkFqbkZ+Vwrc+Z+Fhv7WJSN/x8opryXzuJcDmPzhrF6trAuhTDeDaDTaNEP92F0rWFxrwv2TezeLWzLEcLbKbQ/dzDbPvgfUKiniGRzJ8mlgRA1iiA/KIN81+On7OItKn4aobJ2lIrLt6mSevlRtUG1Yk3QfewTWrq/qWa4roMiscuJRAIRLEaYzuSwLSxhtDow2RwYzBYcix5MmkGmhruwubyY7Q6MD3yzzDgX2I3EUHxfj65JTlEqlUKEZrNipyEdDZDXvUVeWwr6Y7i732CyqZTpxleZkpSglpTibH8ddMcE/5vkNKXsLQ3TdaEBdYPs0RoWhIQhGfGQGHiS+B/FOJoex9pYjLHuMYy/HMbe/ATTdYcw1RdjEexx1WFiqkNsGiS0f32FMYn0YFMKZDMpdl0DxBYUBK1K7k3LCdgG8A5dY7m/hjXbEPfNCvwzSqLzCqJ3+0hFgoxdk2G/OfQIsLAfYWI9SODcaQJnTuE/W4739KeoPnwX5QfH6X3/HQY+eo87p04Q+Lyc1bMVgsoIDHWz8OsgS53qAxEi1nCXzZ7f2JAJ89XWgP1GPY62Ru7U1zB//VvMLRLm2yQ4WyWsSW8Q6mxle24G74gBn9a63xRROQEsTk5yM8S9ryrxfXGSlS/LBZ3Ef76SiU+Ooz3xNqvnPxNsZfjPleMT/J4zHwupOx8uRl4YG2FkHjwO4j4LxrhvGetrLzD60tOMlxxBI0hX8gzyo88hO/o8WuGuf/kIakGaV57F+OJT7GjH9oG53IHVE+ooKrWzTTwSJvGvIqS3w2Qe3uPhLTb8ftb9K6z7fOxuC//H44gPzd/vXd3Np2Q71AAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e9f606e457ca00bbf380325a8d9c1edd/a6d36/image.png&quot;
        srcset=&quot;/static/e9f606e457ca00bbf380325a8d9c1edd/222b7/image.png 163w,
/static/e9f606e457ca00bbf380325a8d9c1edd/ff46a/image.png 325w,
/static/e9f606e457ca00bbf380325a8d9c1edd/a6d36/image.png 650w,
/static/e9f606e457ca00bbf380325a8d9c1edd/e548f/image.png 975w,
/static/e9f606e457ca00bbf380325a8d9c1edd/9239a/image.png 1246w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;dao-패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dao-%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;dao 패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DAO 패턴&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DAO(Data Access Object)&lt;/code&gt; 는 위와 같은 문제들을 해결하기 위해 등장한 패턴이다. DAO 는 &lt;strong&gt;영속성 벤더들의 API 와 비즈니스 로직 그 중간에 위치하여 서로를 연결해주는 어댑터와 같은 역할을 수행한다.&lt;/strong&gt; 직접적인 DB 와의 상호작용을 추상화하고 쿼리를 실행하는 객체로, 이러한 DAO 를 이용하여 데이터에 접근할 수 있게된다.&lt;/p&gt;
&lt;p&gt;DAO 는 &lt;code class=&quot;language-text&quot;&gt;어댑터&lt;/code&gt; 와 같은 역할을 수행한다. 애플리케이션 계층에선 벤더의 API 구현체를 직접 사용하지 않는 대신에 DAO 객체를 이용한다. 이로 인해 데이터 소스가 변경되더라도 비즈니스 로직을 해치지 않는, 변화를 없도록 하는 강한 결합 문제를 해결했다. 또한 각 벤더별 구현의 차이점을 극복했다.&lt;/p&gt;
&lt;h2 id=&quot;repository-패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#repository-%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;repository 패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Repository 패턴&lt;/h2&gt;
&lt;p&gt;반면 Repository 는 단순히 &quot;저장소&quot; 라는 의미를 지니고 있다. 지금까지 내가 작성해왔던 JPA 에서 제공하는 Repository, &lt;code class=&quot;language-text&quot;&gt;@Repository&lt;/code&gt; 어노테이션이 붙은 클래스를 보면 대부분 &lt;code class=&quot;language-text&quot;&gt;Domain&lt;/code&gt; 패키지에 배치시켜왔다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Repository&lt;/code&gt; 패턴은 앞서 설명햇듯이 단순히 저장소라는 의미이다. Repository 는 단순히 객체의 Collection 을 저장하고, 검색하는등의 동작을 캡슐화한 개념이다. 여기서 Collection 이란 자바 진영의 Collection 클래스라기 보단, &lt;code class=&quot;language-text&quot;&gt;객체의 모임(저장 검색등이 가능한 )&lt;/code&gt; 의 개념에 가깝다.&lt;/p&gt;
&lt;h2 id=&quot;그래서-둘의-차이점은-무엇인가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%9E%98%EC%84%9C-%EB%91%98%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot; aria-label=&quot;그래서 둘의 차이점은 무엇인가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그래서, 둘의 차이점은 무엇인가&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DAO(Data Access Object)&lt;/code&gt; 패턴은 이름 그대로 데이터에 접근하기 위한 객체이다. 그 자체가 영속성의 추상화이기 떄문에, 애플리케이션(도메인) 계층이 아닌, &lt;strong&gt;영속성 계층에 속한다.&lt;/strong&gt; 이 떄문에 DAO 가 도메인 패키지에 위치하면 어색한 느낌을 준다.&lt;/p&gt;
&lt;p&gt;일반적으로 DAO 는 데이터베이스의 테이블과 일치한다. &lt;strong&gt;즉, 테이블 중심이라고 할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;반면 &lt;code class=&quot;language-text&quot;&gt;Repository&lt;/code&gt; 는 단순히 객체의 상태(State) 를 관리하는 저장소다. &lt;strong&gt;즉, Repository 는 영구 저장소를 의미하는 것이 아닌, 객체의 상태를 관리하고 저장하는 단순한 저장소&lt;/strong&gt;이다. 따라서, 도메인 정보를 가지고 있어야하는 Repository 는 DAO 와 같이 영속성 계층이 아닌, 도메인 계층으로 분류하는 것이 올바르다.&lt;/p&gt;
&lt;p&gt;즉, &lt;strong&gt;DAO 는 영속성 계층에서 직접적으로 데이터베이스 벤더 API, 테이블에 접근하는 개념이지만, Repository 는 도메인 객체의 상태를 관리 및 저장하는 개념에 가깝다는 점에서 차이를 보인다.&lt;/strong&gt; DAO 는 Storage System 에 더 가까운 개념이고 상대적으로 Low Level 이며, Repository 는 Domain 객체에 가까운 개념이고 상대적으로 High Level 개념이다.&lt;/p&gt;
&lt;p&gt;또한 Repository 는 &lt;strong&gt;객체의 정보를 가진 저장소에 대한 관리&lt;/strong&gt; 에 대한 책임을 위임받은 인터페이스이다. 이 떄문에 저장소는 인메모리에 위치할 수도 있고, 애플리케이션 외부의 어떤 단순한 파일이 될 수도 있고, MySQL 과 같은 RDBMS 가 될 수도 있다. &lt;strong&gt;객체의 상태를 어떤, 어디의 저장소에 저장하는것이 중요한 것이 아니라, 그냥 어떤 자장소에 데이터를 넣고, 읽어오고, 삭제하는 기능을 충실히 제공해주는 것이 Repository 의 역할이다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;레이어드 아키텍처 명학한 정리&lt;/li&gt;
&lt;li&gt;SQL 중심적인 개발의 문제점&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2023-04-24-DAO-Repository/&quot;&gt;https://tecoble.techcourse.co.kr/post/2023-04-24-DAO-Repository/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@maketheworldwise/DAO%EC%99%80-Repository%EC%9D%98-%EC%B0%A8%EC%9D%B4&quot;&gt;https://velog.io/@maketheworldwise/DAO와-Repository의-차이&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://isaac56.github.io/etc/2021/08/29/difference_DAO_Repository/&quot;&gt;https://isaac56.github.io/etc/2021/08/29/difference_DAO_Repository/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[자바의 레퍼 클래스 (Wrapper Class)]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/java/wrapper-class/</link><guid isPermaLink="false">https://haon.site/haon/java/wrapper-class/</guid><pubDate>Fri, 12 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습동기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%8F%99%EA%B8%B0&quot; aria-label=&quot;학습동기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습동기&lt;/h2&gt;
&lt;p&gt;최근부터 시작해서 자바에 대해 학습을 이어오면서, 이전에 무심코 활용했던 레퍼 클래스에 대하여 다시 주의해여 사용해야할 키워드들을 많이 발견했습니다. 이에 대하여 제대로 정리를해보고자, 이번에는 레퍼 클래스와 기본 타입에 대한 올바른 사용방법에 대하여 다루어보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;박싱boxing-과-데이터-타입&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%95%EC%8B%B1boxing-%EA%B3%BC-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85&quot; aria-label=&quot;박싱boxing 과 데이터 타입 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;박싱(Boxing) 과 데이터 타입&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; num1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 레퍼 클래스&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; num2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 기본 타입&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;박싱(Boxing)&lt;/code&gt; 은 원시타입에 해당하는 레퍼 클래스의 객체로 변환하는 행위를 뜻합니다. 이 반대는 언박싱(UnBoxing) 이겠죠. 또 레퍼 클래스의 경우 &lt;code class=&quot;language-text&quot;&gt;참조 데이터 타입(reference type)&lt;/code&gt; 에 해닿하며, &lt;code class=&quot;language-text&quot;&gt;기본 데이터 타입(primitive type)&lt;/code&gt; 은 int, long 과 같은 것들이 해당됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;레퍼-클래스보다는-기본-타입을-지향하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%8D%BC-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%B3%B4%EB%8B%A4%EB%8A%94-%EA%B8%B0%EB%B3%B8-%ED%83%80%EC%9E%85%EC%9D%84-%EC%A7%80%ED%96%A5%ED%95%98%EC%9E%90&quot; aria-label=&quot;레퍼 클래스보다는 기본 타입을 지향하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레퍼 클래스보다는 기본 타입을 지향하자.&lt;/h2&gt;
&lt;p&gt;레퍼 클래스는 객체지향인 관점에서 다양한 기능을 제공해주기 때문에, 어디서든 적재적소로 활용하고 싶은 유횩에 빠져들 수 밖에 없습니다. 그러나, 이에대한 사용시 고려사항도 여럿 존재합니다.&lt;/p&gt;
&lt;h3 id=&quot;연산-속도가-매우-느려질-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%B0%EC%82%B0-%EC%86%8D%EB%8F%84%EA%B0%80-%EB%A7%A4%EC%9A%B0-%EB%8A%90%EB%A0%A4%EC%A7%88-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;연산 속도가 매우 느려질 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;연산 속도가 매우 느려질 수 있다.&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; num &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	num &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드는 컴파일 오류도 발생하지 않고, 로직에 문제도 없어보입니다. 다만 매우 치명적인 단점이 있는데, 그건 바로 박싱(Boxing) 된 객체를 매번 언박싱 해야하는 비용이 계속해서 발생하기 때문에 성능이 매우 느립니다. &lt;strong&gt;레퍼 클래스를 기본 타입과 함께 혼용한 연산에서는 박상된 기본 타입이 자동으로 풀리므로, 이로 인해 연산 성능이 저하되지 않는지를&lt;/strong&gt; 항상 고려하는 것이 좋습니다.&lt;/p&gt;
&lt;p&gt;또한 기본 타입의 경우 모두 데이터 값을 직접 저장하므로 연산에 있어 좋은 성능을 보이지만, 참조 데이터 타입의 경우 메모리에 객체의 주소값을 저장합니다. 때문에 레퍼 클래스를 사용한다는 그 자체로 메모리 사용 측면 및 연산속도에서 비효율적일 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;nullpointerexception-가-발생할-수-있음에-대비하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nullpointerexception-%EA%B0%80-%EB%B0%9C%EC%83%9D%ED%95%A0-%EC%88%98-%EC%9E%88%EC%9D%8C%EC%97%90-%EB%8C%80%EB%B9%84%ED%95%98%EC%9E%90&quot; aria-label=&quot;nullpointerexception 가 발생할 수 있음에 대비하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;NullPointerException 가 발생할 수 있음에 대비하자.&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;오류 없이 진입성공!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;기본 타입의 값은 언제나 유효하지만, 레퍼 클래스의 경우 유효하지 않은 값 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; 을 가질 수 있음을 유념해야합니다. 위의 경우 int 가 아니라 Integer 로 아무값 지정없이 초기화했기 때문에 null 로 초기화 될 것입니다. 앞서 말했듯이 기본 타입과 레퍼 클래스 타입을 혼용한 연산에서는 박싱된 타입이 자동으로 언박싱 되므로 null 을 참조하는 값을 언박싱하면 &lt;code class=&quot;language-text&quot;&gt;NullPointerException&lt;/code&gt; 이 발생하게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;박싱된-값은-식별성identity-속성을-갖는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%95%EC%8B%B1%EB%90%9C-%EA%B0%92%EC%9D%80-%EC%8B%9D%EB%B3%84%EC%84%B1identity-%EC%86%8D%EC%84%B1%EC%9D%84-%EA%B0%96%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;박싱된 값은 식별성identity 속성을 갖는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;박싱된 값은 식별성(identity) 속성을 갖는다.&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// a == b   =&gt; false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;equals() 에 대한 자세한 내용은 &lt;a href=&quot;https://velog.io/@msung99/JAVA-Object.equals-%EC%9D%98-%EC%9D%BC%EB%B0%98-%EA%B7%9C%EC%95%BD%EC%9D%84-%EC%A7%80%EC%BC%9C%EC%84%9C-%EC%9E%AC%EC%A0%95%EC%9D%98%ED%95%98%EB%9D%BC&quot;&gt;[JAVA] Object.equals 의 일반 규약을 지켜서 재정의하라&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;기본 타입의 경우 값만 저장하게 되지만, 박싱된 기본 타입인 레퍼 클래스는 값 말고도 식별성(identity) 속성을 가집니다. 이전에 다루었듯이 &quot;==&quot; 연산은 참조를 비교하는 것이므로 같은 값을 보유했더라도 서로 다른 객체라면 동등성 비교 결과가 false 라고 했었습니다. 때문에 두 레퍼 객체의 값을 비교하고 싶다면, &lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 를 활용해야 함에 주의합시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;언제-레퍼-클래스-타입을-사용해야할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B8%EC%A0%9C-%EB%A0%88%ED%8D%BC-%ED%81%B4%EB%9E%98%EC%8A%A4-%ED%83%80%EC%9E%85%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;언제 레퍼 클래스 타입을 사용해야할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;언제 레퍼 클래스 타입을 사용해야할까?&lt;/h2&gt;
&lt;p&gt;그렇다면 레퍼 클래스보다 반드시 기본 타입의 사용을 지향해야하는가? 라는 생각이 들 수 있겠지만, 항상 그런것은 아닙니다. 박싱된 기본타입을 사용해야하는 경우를 나열해보면 다음과 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;컬렉션을 활용해서 값을 저장해야 하는경우&lt;/li&gt;
&lt;li&gt;리플랙션을 통해 메소드를 호출해야 할때&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;컬렉션의 경우는 기본타입을 담을 수 없기 때문에, 박싱된 레퍼 타입을 사용해야합니다. 리플랙션은 아직 이해도가 부족하기 떄문에, 추후 학습후에 이에대한 내용을 자세히 다루어보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://okeybox.tistory.com/437&quot;&gt;https://okeybox.tistory.com/437&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dahye-jeong.gitbook.io/java/java/effective_java/2021-07-25-prefer-primitivie-types-to-boxed-primitives&quot;&gt;https://dahye-jeong.gitbook.io/java/java/effective_java/2021-07-25-prefer-primitivie-types-to-boxed-primitives&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[테스트 더블 (Test Double) 로 Mocking 하기]]></title><description><![CDATA[💡 학습 동기는 지난 포스트 혼란스러운 단위, 통합, 인수, 시스템 테스트. 명확한 테스트 격리 수준 정리 😵‍💫 에서 이어지는 내용이다. Junit…]]></description><link>https://haon.site/test/double/</link><guid isPermaLink="false">https://haon.site/test/double/</guid><pubDate>Thu, 11 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;💡 학습 동기는 지난 포스트 &lt;a href=&quot;https://haon.blog/test/type/&quot;&gt;혼란스러운 단위, 통합, 인수, 시스템 테스트. 명확한 테스트 격리 수준 정리 😵‍💫&lt;/a&gt; 에서 이어지는 내용이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Junit 과 같은 자동화 테스트 도구를 활용하여 테스트를 작성할 때, 여러 객체들 사이에 의존성을 갖는 경우 테스트를 하기 까다로운 경우가 있다. 가령 프로덕션 코드의 핵심인 Service 레이어 객체에 대해 테스트가 Repository, Dao 에 직접 의존하고 있다면 테스트에 문제가 없을까?&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7ed5ddd5be55524c251bf87152bb0c09/844cc/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 22.085889570552148%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAABJklEQVR42iWP3ytDcRjGd6f9GS5wpfwDLly7cK8oTCS1SycUrSS3qykU2wVRIqFIrOXHdpaMnclBbdY4xtrFxnTO2c7345yztz69z/O+9fS+ntK7Qq2iUryN8jzqoyoncMrSTQpzAYrra66vJhO8TYxj5nOuj+9HCPn7OV5ZQli6PaljWQaegnpO5TtGejtItL2T1OIAqjzF3a6Pw9YWUgOD6D9lsstBtrxeQtI04YMjRnq78fd0EBjqI6OkUdUntI8CHvX+hJuzVa5ie+S1EvLpPIlwG/JmFxc7w7wmk2jyJXmbSHiDMWmGSWkWKbBALvvCQzqFklHsUKUZqGXj1L6uKX8+Ygnhni5EHYTpIizHG7Z2uoWh/7pYDedNYe8bmMYfdbPJP0bgCSGtcte4AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7ed5ddd5be55524c251bf87152bb0c09/a6d36/image.png&quot;
        srcset=&quot;/static/7ed5ddd5be55524c251bf87152bb0c09/222b7/image.png 163w,
/static/7ed5ddd5be55524c251bf87152bb0c09/ff46a/image.png 325w,
/static/7ed5ddd5be55524c251bf87152bb0c09/a6d36/image.png 650w,
/static/7ed5ddd5be55524c251bf87152bb0c09/e548f/image.png 975w,
/static/7ed5ddd5be55524c251bf87152bb0c09/3c492/image.png 1300w,
/static/7ed5ddd5be55524c251bf87152bb0c09/844cc/image.png 1306w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Service 레이어는 Repository 에 대해 의존성을 갖고있다. 또 Repository 계층은 관례적으로 데이터베이스와 의존하는 형태를 갖는다. 결국 Service 레이어 객체는 꼬리에 꼬리를 물어 의존관계가 길어지는 형태를 갖는다.&lt;/p&gt;
&lt;p&gt;맙소사. 우리가 테스트할 대상은 오로지 Service 객체만을 신경쓰는 것이었는데, 복잡한 의존관계로 인해 테스트 대상에 온전히 집중할 수 없게 되었다. 여기서 의존관계와 종속성을 신경쓰지 않고, 다른 객체와의 의존관계를 격리시켜 오로지 테스트 대상에만 집중할 수 있게 하는 기법이 바로 &lt;code class=&quot;language-text&quot;&gt;테스트 더블(Test Double)&lt;/code&gt; 이다.&lt;/p&gt;
&lt;h3 id=&quot;테스트-더블-test-double&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%8D%94%EB%B8%94-test-double&quot; aria-label=&quot;테스트 더블 test double permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;테스트 더블 (Test Double)&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;테스트 더블(Test Double)&lt;/code&gt; 이란 현재 테스트 대상과 의존관계, 종속성을 맺는 객체들을 최대한 신경쓰지 않고 격리된 방식으로 테스트할 수 있도록 도와주는 객체다. 즉, 테스트 더블을 테스트하고자 하는 대상만을 독립적으로 테스트할 수 있도록 별도로 구현한, 실제 객체를 대신하는 더 단순한 객체이다.&lt;/p&gt;
&lt;p&gt;정리하면, 현재 테스트 대상과 의존관계를 맺는 객체 대신에, 이와 동일하게 동작하는 별도의 가짜 객체가 테스트 더블이다. 현재 테스트 대상은 이 가짜 객체(Test Double)를 의존하게 된다.&lt;/p&gt;
&lt;p&gt;이때 용어를 잠시 정리하자면, 테스트 대상을 &lt;code class=&quot;language-text&quot;&gt;SUT(System Under Test)&lt;/code&gt; 라고하며, SUT 가 의존하는 실제 객체를 &lt;code class=&quot;language-text&quot;&gt;DOC(Depended On Component)&lt;/code&gt; 라고 한다. 가짜 객체인 테스트 더블은 이 DOC 를 모방한 객체로, DOC 과 동일한 입.출력을 제공하도록 동작한다.&lt;/p&gt;
&lt;h2 id=&quot;테스트-더블의-종류&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%8D%94%EB%B8%94%EC%9D%98-%EC%A2%85%EB%A5%98&quot; aria-label=&quot;테스트 더블의 종류 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;테스트 더블의 종류&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ac7cc550c07257ec16e956ca3de4db35/f868f/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.50920245398773%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABZklEQVR42p2Sh4rDQAxE/f8flu6QZsdJCKST4hSndx1vYIMJhw9uQVjSrkbjkTzLOOfz2W63m00mExuPx3a5XGRZx8u6pBjQarVqxWLRdrudGvwb8H6/C3A6nYrl8Xi0x+PxN+Dr9bLr9foxWLgvAEmS2Ha7le/y7o0zMAR4Op1ss9nIYIAtl0tbLBa23++N+ziOlcM/HA4Cn81makS8Xq9ttVoJ2ON38vm8NZtNFcGi1WpZp9MRIHG327Uoiuz5fCpHnMvl9J6Yu0KhIN+jSxiG1uv1lIAhfr/fFwO6MmFifHLEtVpN2sKOu3q9rj/x3u/3R1B8DCYMw+njpv2tMW/RztVhXhoofQB1K5Iu5JBnA76J/Lo2dEZo9BkOh9o9YrRkKPwy+fl8LolgnrmHPED0UqlklUpF4o9GI7FCI8RvNBpWLpfN932BZgJSOBgMLAgCa7fbEh+2MGddGCAbACj3sE4D/gBgxJltaXuepwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/ac7cc550c07257ec16e956ca3de4db35/a6d36/image-1.png&quot;
        srcset=&quot;/static/ac7cc550c07257ec16e956ca3de4db35/222b7/image-1.png 163w,
/static/ac7cc550c07257ec16e956ca3de4db35/ff46a/image-1.png 325w,
/static/ac7cc550c07257ec16e956ca3de4db35/a6d36/image-1.png 650w,
/static/ac7cc550c07257ec16e956ca3de4db35/e548f/image-1.png 975w,
/static/ac7cc550c07257ec16e956ca3de4db35/3c492/image-1.png 1300w,
/static/ac7cc550c07257ec16e956ca3de4db35/f868f/image-1.png 1382w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;테스트 더블은 구현하는 세부 방식에 따라 총 5가지의 종류로 분류한다. 이에 대한 종류로는 &lt;strong&gt;Dummy, Fake, Stub, Mock, Spy&lt;/strong&gt; 가 있는데, 한번 살펴보자.&lt;/p&gt;
&lt;h3 id=&quot;dummy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dummy&quot; aria-label=&quot;dummy permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dummy&lt;/h3&gt;
&lt;p&gt;더미 객체는 &lt;strong&gt;아무런 동작도 하지 않고, 겉으로 번 껍대기만 생성된 간단한 객체이다.&lt;/strong&gt; 테스트 대상 클래스는 이 더미 객체에 의존하지만, 절대 사용하지 않는다.&lt;/p&gt;
&lt;p&gt;예를들어 아래와 같은 UserService 클래스에서 회원가입을 수행하는 signup 메소드를 테스트한다고 해보자. 로직을 보면 알 수 있듯이, UserRepository 가 아닌 BookRepository 와는 전혀 관련없는 상황이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRepository&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookRepository&lt;/span&gt; bookRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;signup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SignUpDto&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        userRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;UserService 의 signup 메소드는 BookRepository 와 의존관계를 갖지 않기 때문에, 적어도 signup 테스트에 대해선 간단히 빈껍대기만을 가진, 아무런 동작도 수행하지 않는 BookRepository 더미 객체를 생성해줄 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DummyBookRepo&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findBookById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; bookId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;fake&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fake&quot; aria-label=&quot;fake permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Fake&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1d42655e02a6dc3354987d971b682933/e53e8/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.963190184049076%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABZklEQVR42o2SS07DMBCGu+dCXICbcBQOwAVYIrFhi4TUBWIBQiqCwqJUVCgo0OZRJ7XzcJ3Y+Zk8KClKIRPNwtH488w//wA9Ik0ThAHDer3e/Cua73cM/iQV9YUkkbCdFeI4bQHRgdsBNAQqSlhhqrPD7vEy3sdSzGCamvPnaxwNT5t3ix4dUpH2XAiZwfIe8To5hC8syFxD6QyX0xFO7i66gZK04ZGAoFR5huFkDmuVwLgLKNKuPf5mCq2Rqax75FimCCIOHgtYgYeD4xHORvb3/GCMYTabQuu8NUDPpZSFbwsHjLqNlayu8JWAbTMYY7aBrewEFq1xyoMmQEiW8SMXAb8l7WQ1uaZlyUzROa+TZDLNAv+xTV307l7h6WYP9vKBAABLeJU8DPFp20hJMt0L2PQsVYIPdwyV/Ri77NBnS3i+B5HEldV6AOuIRIzF3AfnfNurzZOmlw/bFiEtNenaXsqu+AJ2RKgbEi0/HQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/1d42655e02a6dc3354987d971b682933/a6d36/image-2.png&quot;
        srcset=&quot;/static/1d42655e02a6dc3354987d971b682933/222b7/image-2.png 163w,
/static/1d42655e02a6dc3354987d971b682933/ff46a/image-2.png 325w,
/static/1d42655e02a6dc3354987d971b682933/a6d36/image-2.png 650w,
/static/1d42655e02a6dc3354987d971b682933/e548f/image-2.png 975w,
/static/1d42655e02a6dc3354987d971b682933/3c492/image-2.png 1300w,
/static/1d42655e02a6dc3354987d971b682933/e53e8/image-2.png 1982w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;페이크 객체는 DOC 을 흉내내는 아주 간단히 구현된 객체로, 더미 객체와 달리 실제로 동작하고 사용된다. 하지만, 매우 단순한 방식으로 간단하게 구현하기 때문에 실제 프로덕션 코드에선 사용하기에 적합하지 않다.&lt;/p&gt;
&lt;p&gt;페이크 객체로 DOC 을 흉내내는 가장 대표적인 예가 바로 데이터베이스다. 실제로는 복잡한 RDBMS 와 실제로 데이터베이스 통신을 하는 대신에, 실제 DB 를 흉내내는 인메모리 DB 로 대신하는 것이다. 아래와 Dao 코드를 보자.&lt;/p&gt;
&lt;p&gt;JdbcUserDao 는 JDBC 에 기반하여 실제 데이터베이스에 쿼리를 날려 통신을 한다. 나는 실제 DB 에 직접 쿼리를 날려 유저 데이터를 저장하는 동작이 수행되는 대신에, 임의로 DB 와 유사하게 동작하는 DB 로 간단히 테스트하고 싶다. 이를 위해 페이크 객체를 만들어보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcUserDao&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; userDao&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt; jdbcTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcUserDao&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;INSERT INTO USERS VALUES (?, ?, ?, ?)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        jdbcTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;실제 데이터베이스를 대신하는 간단한 인메모리 DB 방식으로 구현했다. &lt;code class=&quot;language-text&quot;&gt;HashMap&lt;/code&gt; 을 사용했는데, 현재 테스트에 한정하여 문제없이 동작할 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FakeUserDao&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; inMemoryDB &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        inMemoryDB&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 과연 위 페이크 객체는 실제 프로덕션 코드에 적합할까? &lt;code class=&quot;language-text&quot;&gt;HashMap&lt;/code&gt;  이 MySQL 과 같은 체계적인 RDBMS 를 따라하진 못하며, 문제없이 동작하는 코드는 위 테스트 코드에 한정될 것이다. 이 떄문에 페이크 객체는 프로덕션 코드에 접하지 않다는 것이다.&lt;/p&gt;
&lt;h3 id=&quot;stub&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stub&quot; aria-label=&quot;stub permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Stub&lt;/h3&gt;
&lt;p&gt;스텁은 테스트 과정에서 수행된 호출에 대해 &lt;strong&gt;하드 코딩된 정적인 응답만을 제공한다.&lt;/strong&gt; 즉, 반한될 응답값이 미리 정적으로 결정되어 있으며, 항상 동일한 응답값만을 리턴한다.&lt;/p&gt;
&lt;p&gt;로또 숫자 생성기가 있을때, 이를 스텁으로 구현하자면 아래와 같이 구현될 수 있다. 이 스텁을 활용하는 객체에 한정하여 항상 동일한 로또 넘버 리스트를 리턴한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LottoGeneratorStub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LottoGenerator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; lottoNumbers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;37&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;38&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;41&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateLottoNumbers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; lottoNumbers&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;spy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#spy&quot; aria-label=&quot;spy permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Spy&lt;/h3&gt;
&lt;p&gt;스파이는 Stub 의 역할을 가지면서, 호출된 내용에 대해 약간의 동적인 정보를 기록해둔다. 실제 테스트 더블로 구현된 객체에 자기 자신이 호출 되었을 때 확인이 필요한 부분을 기록하도록 구현한다.&lt;/p&gt;
&lt;p&gt;아래 UserService 는 회원기입 메소드를 호출할 때 마다 유저 정보를 저장하고, 총 몇번 회원가입을 시도했는지 체크한다. 이처럼 자기 자신이 호출된 상황을 저장하고 확인할 수 있는 객체가 스파이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; signUpCnt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Collecction&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; users &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;signUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        signUpCnt&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        users&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getSignUpCnt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; signUpCnt&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;바로 아래에서 후술할 Mockito 프레임워크의 &lt;code class=&quot;language-text&quot;&gt;verify()&lt;/code&gt; 가 Spy 와 동일한 역할을 한다.&lt;/p&gt;
&lt;h3 id=&quot;mock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mock&quot; aria-label=&quot;mock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Mock&lt;/h3&gt;
&lt;p&gt;모의 객체는 &lt;strong&gt;호출에 대한 기대를 명세할 수 있고, 그 명세 내용에 따라 동작하도록 프로그래밍된 객체다.&lt;/strong&gt; Stub 에 비해, 특정 응답에 대한 구체적인 기댓값을 설정할 수 있다. 보통 Mock 객체는 개발자가 직접 코드를 작성하여 기댓값을 작성할 수도 있지만, Mocking 프레임워크를 사용하여  편리하게 동작 및 기댓값을 단언할 수 있다. 자바 진영에서 사용되는 대표 프레임워크는 &lt;code class=&quot;language-text&quot;&gt;Mockito&lt;/code&gt; 이다.&lt;/p&gt;
&lt;p&gt;또한 위 테스트 더블 객체 종류 모두에게 호환되는 객체 종류이다. 즉, Mocking 한 객체는 Dummy, Stub, Spy 처럼 동작할 수 있는 호환성이 좋은 훌륭한 객체이다.&lt;/p&gt;
&lt;p&gt;아래 코드를 보면 Mockito 라이브러리에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;when&lt;/code&gt; 절을 활용하어 명세를 하였다. Mocking 된 userRepository 는 when 절을 활용하여 명세한 내용에 따라 동작하게 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@ExtendWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MockitoExtension&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserServiceTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Mock&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRepository&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;anyLong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;thenReturn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Test User&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        
        &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; actual &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;actual&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isEqualTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;actual&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isEqualTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Test User&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;지금까지 테스트를 진행하면서 Mockito 라이브러리를 자주 사용해왔기 떄문에, 더 자세한 내용은 여기서 학습하지 않도록 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Mockito 프레임워크 어노테이션 (@Spy, @SpyBean, @Mock, @MockBean)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Effective Software Testing (Mauricio Aniche)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cobbybb.tistory.com/16#3.2%20%EC%84%B8%20%EB%B2%88%EC%A7%B8%20Refactoring%20%3A%20%40Mock%2C%20%40InjectMocks%EC%9D%98%20%EC%A1%B0%ED%95%A9-1&quot;&gt;https://cobbybb.tistory.com/16#3.2%20%EC%84%B8%20%EB%B2%88%EC%A7%B8%20Refactoring%20%3A%20%40Mock%2C%20%40InjectMocks%EC%9D%98%20%EC%A1%B0%ED%95%A9-1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2020-09-19-what-is-test-double/&quot;&gt;https://tecoble.techcourse.co.kr/post/2020-09-19-what-is-test-double/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/test-double/&quot;&gt;https://hudi.blog/test-double/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[자바의 멤버 클래스는 되도록 static을 붙여서 정적 멤버 클래스로 만들어라]]></title><description><![CDATA[중첩 클래스 중첩 클래스(Nested Class) 란 다른 클래스 안에 정의된 클래스로써 내부 클래스(inner class…]]></description><link>https://haon.site/haon/java/static-member-class/</link><guid isPermaLink="false">https://haon.site/haon/java/static-member-class/</guid><pubDate>Thu, 11 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;중첩-클래스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A4%91%EC%B2%A9-%ED%81%B4%EB%9E%98%EC%8A%A4&quot; aria-label=&quot;중첩 클래스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;중첩 클래스&lt;/h2&gt;
&lt;p&gt;중첩 클래스(Nested Class) 란 다른 클래스 안에 정의된 클래스로써 내부 클래스(inner class) 라고도 불립니다. &lt;strong&gt;중첩 클래스는 자신을 감싼 외부 클래스에서만 쓰여야하며,&lt;/strong&gt; 그 이외에 쓰임세가 있다면 탑레밸 클래스로 만들어야 합니다.&lt;/p&gt;
&lt;p&gt;중첩 클래스의 종류에는 &lt;code class=&quot;language-text&quot;&gt;정적 멤버 클래스&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;(비정적) 맴버 클래스&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;익명 클래스&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;지역 클래스&lt;/code&gt; 이렇게 4가지가 해당합니다. 이 중에서 정적 멤버 클래스를 제외한 모든 클래스는 내부 클래스(inner class) 에 해당합니다. 이들중에 익명 클래스를 제외한 모든 클래스에 대한 특징을 파악하고, 언제 왜 사용해야하는지를 학습하는 것이 이번 포스팅의 핵심 취지가 될 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;정적-멤버-클래스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EC%A0%81-%EB%A9%A4%EB%B2%84-%ED%81%B4%EB%9E%98%EC%8A%A4&quot; aria-label=&quot;정적 멤버 클래스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정적 멤버 클래스&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyClass&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; myNumber&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyInnerClass&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MyClass&lt;/span&gt; myclass&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; myClass&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;myNumber&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 일반 클래스처럼 사용 가능하다.&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;MyInnerClass&lt;/span&gt; instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyClass&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MyInnerClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;클래스으 정적 멤버 필드처럼 정적(static) 으로 정의된 멤버 클래스를 정적 멤버 클래스라고 합니다. 위를보면 MyClass 내부에 MyInnerClass 가 정의되어 있습니다. 이 클래스는 일반적인 클래스와 같이 &lt;code class=&quot;language-text&quot;&gt;new&lt;/code&gt; 키워드를 통해 객체를 생성할 수 있고, 필드를 가지며 메소드를 작성할 수 있습니다. 하지만 자신을 감싼 외부 클래스와 private 필드에 접근 가능하다는 것이 일반 클래스와 다릅니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Calculator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Operator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;PLUS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MINUS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DIVIDE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MULTIPLY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Calculator 클래스 내부의 Operator 열겨형은 Calculator.Operator.PLUS 와 같이 계산 유형을 선택하는 역학을 합니다. 여기서 Operatro 는 오로지 Calculator 클래스 내부에서만 사용되기 때문에 새로운 탑레벨 클래스를 만들지 않고 내부의 중첩 클래스로 정의했습니다.&lt;/p&gt;
&lt;h3 id=&quot;비정적non-static-멤버-클래스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EC%A0%95%EC%A0%81non-static-%EB%A9%A4%EB%B2%84-%ED%81%B4%EB%9E%98%EC%8A%A4&quot; aria-label=&quot;비정적non static 멤버 클래스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비정적(non-static) 멤버 클래스&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Machine&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; status&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; status &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Machine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(비정적) 멤버 클래스는 정적 멤버 클래스와 달리 static 키워드를 사용하지 않고 정의합니다. 정의할때 키워드 하나 차이지만 큰 차이가 있습니다. 먼저 non-static (비정적) 클래스이기 때문에, &lt;strong&gt;객체를 만들기 위해서는 먼저 감싸는 외부 클래스의 인스턴스가 존재해야합니다.&lt;/strong&gt; 이 말은 즉슨, 생성되는 멤버 클래스의 객체는 외부 클래스의 인스턴스와 연결되어 있다는 뜻입니다. 예를들어 위처럼 Machine.this.status 와 같이 외부 클래스의 인스턴스에 접근해서 멤버 변수를 가져올 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;비정적-클래스의-사용을-지양하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EC%A0%95%EC%A0%81-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%9D%98-%EC%82%AC%EC%9A%A9%EC%9D%84-%EC%A7%80%EC%96%91%ED%95%98%EC%9E%90&quot; aria-label=&quot;비정적 클래스의 사용을 지양하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비정적 클래스의 사용을 지양하자.&lt;/h2&gt;
&lt;h3 id=&quot;1-외부-클래스-인스턴스가-있어야지-사용-가능하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EC%99%B8%EB%B6%80-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4%EA%B0%80-%EC%9E%88%EC%96%B4%EC%95%BC%EC%A7%80-%EC%82%AC%EC%9A%A9-%EA%B0%80%EB%8A%A5%ED%95%98%EB%8B%A4&quot; aria-label=&quot;1 외부 클래스 인스턴스가 있어야지 사용 가능하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 외부 클래스 인스턴스가 있어야지 사용 가능하다&lt;/h3&gt;
&lt;p&gt;(비정적) 멤버 클래스는 외부 클래스와 인스턴스로 연결된다는 특성 때문에 사용이 지양됩니다. &lt;strong&gt;멤버 클래스는 먼저 외부 클래스와 인스턴스가 있어야 클래스를 사용할 수 있습니다.&lt;/strong&gt; 또한 이는 일반적인 클래스와 사용이 다르기 떄문에 사용이 불편합니다.&lt;/p&gt;
&lt;h3 id=&quot;2-메모리-사용량이-많아진다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%82%AC%EC%9A%A9%EB%9F%89%EC%9D%B4-%EB%A7%8E%EC%95%84%EC%A7%84%EB%8B%A4&quot; aria-label=&quot;2 메모리 사용량이 많아진다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 메모리 사용량이 많아진다&lt;/h3&gt;
&lt;p&gt;또 멤버 클래스와 외부 클래스와 인스턴스의 연결 정보를 가지고 있어야하기 때문에 &lt;strong&gt;사용되는 메모리가 많습니다.&lt;/strong&gt; 따라서 상위 클래스의 인스턴스에 연결이 필요한 특수한 경우가 아니라면, 멤버클래스에는 static 키워드를 사용하여 정적으로 만드는것이 권장됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;지역-클래스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%80%EC%97%AD-%ED%81%B4%EB%9E%98%EC%8A%A4&quot; aria-label=&quot;지역 클래스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;지역 클래스&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MainClass&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 지역클래스 선언&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyLocalClass&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 지역 클래스 인스턴스 생성&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;MyLocalClass&lt;/span&gt; instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyLocalClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  	&lt;span class=&quot;token comment&quot;&gt;// 컴파일 에러 발생&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;MyLocalClass&lt;/span&gt; instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyLocalClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;정적 및 비정적 멤버 클래스에 이어서, 추가적으로 지역 클래스에 대해서도 알아봅시다. 지역 클래스는 일반 클래스와 유사하게 사용할 수 있지만, &lt;strong&gt;클래스의 타입을 사용할 수 있는 범위가 클래스가 정의된 지역을 벗어날 수 없습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;위 예제를 보면, method() 에서 MyLocalClass 가 정의 되었고 new 키워드를 통해 인스턴스를 만들었습니다. 이처럼 일반 클래스와 유사하게 정의하고 사용 가능합니다. 하지만 main 메소드에서 MyLocalClass 타입을 사용하려고 하면 MyLocalClass 는 method() 에서 정의된 지역 클래스이기 때문에 method() 를 벗어나서는 사용 불가능하므로 컴파일 에러가 발생합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;중첩 클래스에는 4가지가 있으며, 각각은 쓰임이 다릅니다. 메소드 밖에서도 사용해야 하거나 메소드 안에 정의하기엔 너무 길다면 멤버 클래스로 만듭시다. 멤버 클래스의 인스턴스 각강이 바깥 인스턴스를 참조한다면 비정적으로, 그렇지않다면 정적으로 만듭시다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[자바의 toString 메소드를 올바르게 활용하는 방법에 대하여]]></title><description><![CDATA[학습동기 왜 toString 을 활용해서 코드를 작성하는지 잘 이해하지 못해서 사용하지 않고 있었는데, 이번 기회에 왜 toString 을 활용하는 것인지에 대해 알고자 이렇게 학습을 하게 되었습니다. toString toStrng…]]></description><link>https://haon.site/haon/java/tostring-guide/</link><guid isPermaLink="false">https://haon.site/haon/java/tostring-guide/</guid><pubDate>Thu, 11 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습동기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%8F%99%EA%B8%B0&quot; aria-label=&quot;학습동기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습동기&lt;/h2&gt;
&lt;p&gt;왜 toString 을 활용해서 코드를 작성하는지 잘 이해하지 못해서 사용하지 않고 있었는데, 이번 기회에 왜 toString 을 활용하는 것인지에 대해 알고자 이렇게 학습을 하게 되었습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;tostring&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tostring&quot; aria-label=&quot;tostring permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;toString&lt;/h2&gt;
&lt;p&gt;toStrng 일반 규약에 따르면, &lt;strong&gt;&quot;간결하면서 사람이 읽기 쉬운 형태의 유익한 정보를 반환해야 한다&quot;&lt;/strong&gt; 라고 정의하고 있습니다. 또 규약에 따르면 &quot;toString 을 잘 구현한 클래스는 사용하기가 매우 편해지며, 그 클래스 활용했을 때 &lt;strong&gt;&quot;디버깅&quot;&lt;/strong&gt; 하기 쉽다는 특징을 지니고 있다고합니다.&lt;/p&gt;
&lt;h3 id=&quot;objecttostring&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#objecttostring&quot; aria-label=&quot;objecttostring permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Object.toString&lt;/h3&gt;
&lt;p&gt;대부분의 클래스는 기본적으로 &lt;code class=&quot;language-text&quot;&gt;Object&lt;/code&gt; 클래스의 메소드를 재정의하여, 원하는 형식대로 사용하는 구조를 가집니다. 개발자가 별도의 재정의를 하지 않았다면 기본적으로 &lt;code class=&quot;language-text&quot;&gt;클래스명@16진수로_표시한_해시코드&lt;/code&gt; 형식을 반환하게 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;book1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Book@adbbd&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;book1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 Object 에서 반환하는 포맷은 읽기도 힘들며, 의미있는 정보가 아닙니다. 때문에 추후 디버깅 용도로 활용하지도 못하게 될겁니다. 이 때문에 서적에서는 &lt;strong&gt;&quot;toString 을 항상 재정의하라&quot;&lt;/strong&gt; 는 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;tostring-을-항상-재정의하라&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tostring-%EC%9D%84-%ED%95%AD%EC%83%81-%EC%9E%AC%EC%A0%95%EC%9D%98%ED%95%98%EB%9D%BC&quot; aria-label=&quot;tostring 을 항상 재정의하라 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;toString 을 항상 재정의하라&lt;/h2&gt;
&lt;p&gt;위와 같은 &lt;code class=&quot;language-text&quot;&gt;Object&lt;/code&gt; 클래스의 toString 의 단점을 이해했다면, 왜 toString 을 재정의하라는 것인지 충분히 납득됐을겁니다. 앞서 언급했듯이, &lt;strong&gt;toString 을 잘 구현한 클래스는 사용하기 편하며 디버깅하도 쉽습니다.&lt;/strong&gt; toString 메소드는 객체를 print, 문자열 연결 연산자인 &quot;+&quot;, assert 구문에 넘길때, 디버깅을 객체를 출력할 때 등의 상황에서 자동으로 호출됩니다.&lt;/p&gt;
&lt;p&gt;toString 은 그 객체가 가진 모든 주요정보를 반환하는게 좋습니다. 예를들어 아래와 같이 name, price 라는 주요 필드가 있고 그 외에 상대적으로 중요도가 떨어지는 기타 필드들이 있다면, toString 출력 형식을 주요 필드들로 구성하여 출력해줄 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; price&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ... (기타 컬럼)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// (생성자 정의부 생략)&lt;/span&gt;
  &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;책 (이름 = %s, 가격 = %s&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; price&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;단, &lt;code class=&quot;language-text&quot;&gt;정적 유틸리티 클래스&lt;/code&gt; 와 대부분의 &lt;code class=&quot;language-text&quot;&gt;열거타입&lt;/code&gt; 은 toString 을 제공할 필요가 없습니다. 해당 타입들은 이미 자바에서 완벽한 toString 형식을 제공하기 때문입니다.&lt;/p&gt;
&lt;h3 id=&quot;활용방안&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%99%9C%EC%9A%A9%EB%B0%A9%EC%95%88&quot; aria-label=&quot;활용방안 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;활용방안&lt;/h3&gt;
&lt;p&gt;toString 은 계속 언급했던 맥락이지만, &lt;strong&gt;디버깅을 위해 설계된 메소드&lt;/strong&gt;라고 합니다. 어떤 문제가 발생한 클래스에 대해 toString 이 잘 정의되었다면, 스스로 본인에게 무슨 문제가 발생했는지를 명쾌하게 설명해줌으로써 유익한 정보를 제공받을 수 있게됩니다. 때문에 개발자는 무엇이 원인인지를 더 쉽게 찾을 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;toString 은 디버깅이 주목적이 되므로, 보통 클라이언트에게 보여줄 출력을 위한 메소드는 별도로 정의하는 방안이 좋을 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Effective Java (Joshua Bloch 지음)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/java-correct-purpose-of-tostring/&quot;&gt;https://hudi.blog/java-correct-purpose-of-tostring/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[혼란스러운 단위, 통합, 인수, 시스템 테스트. 명확한 테스트 격리 수준 정리 😵‍💫]]></title><description><![CDATA[학습배경 이번 카카오테크 교육과정에서 맥(mac) 코치님의 수업을 들으면서, 애자일스러운 개발 프로세스에 대해 알게 되었다. 수업 내용중 E2E…]]></description><link>https://haon.site/test/type/</link><guid isPermaLink="false">https://haon.site/test/type/</guid><pubDate>Wed, 10 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;이번 카카오테크 교육과정에서 맥(mac) 코치님의 수업을 들으면서, 애자일스러운 개발 프로세스에 대해 알게 되었다. 수업 내용중 E2E 테스트, 테스트 커버리지, ... 등 실제 현업에서의 테스트 진행방식에 관한 내용도 자세히 들을 수 있었다.&lt;/p&gt;
&lt;p&gt;이전부터 혼자 무작정 테스트를 작성하면서 무작정 단위 테스트, 통합 테스트에 대한 개념을 익혔는데, 항상 명확한 기준없이 작성했었던 것 같다. 사실 테스트 더블(Test Double), 모의 객체에 관한 학습을 진행해볼까 하다 근본 기초부터 제대로 학습하기위해 테스트 수준에 대해 먼저 학습하기로 생갃했다. 이번 기회에 테스트에 관한 지식을 매꾸고자 글을 작성한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;단위-통합-인수-테스트--햇갈리는-기본-개념-정리-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%9C%84-%ED%86%B5%ED%95%A9-%EC%9D%B8%EC%88%98-%ED%85%8C%EC%8A%A4%ED%8A%B8--%ED%96%87%EA%B0%88%EB%A6%AC%EB%8A%94-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC-&quot; aria-label=&quot;단위 통합 인수 테스트  햇갈리는 기본 개념 정리  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단위, 통합, 인수 테스트, .. 햇갈리는 기본 개념 정리 😫&lt;/h2&gt;
&lt;p&gt;단위, 통합, 인수, 시스템 테스트등은 기본적인 용어, 개념임에도 사실 지금껏 햇갈려했던 단어이다. &quot;이걸 단위 테스트라고 하는거겠지?&quot; 라는 추측성에 가까운 정의를 해왔는데, 이번 기회에 확실히 개념을 정교화한 후 향후 포스트에서 테스트 더블, 모의 객체에 관하여 학습하도록 한다.&lt;/p&gt;
&lt;h2 id=&quot;테스트-피라미드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%94%BC%EB%9D%BC%EB%AF%B8%EB%93%9C&quot; aria-label=&quot;테스트 피라미드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;테스트 피라미드&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b9c942ab3fcf2ed5705519f04db20d86/ad00e/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 80.3680981595092%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAACVklEQVR42p1SDVMSURTd/1yTo85USA6JyTRaflSwLp8uApofyWjKrvIlKm5kFqg56SILCCYs4oRyfLuCg4Oh9WbO3Lv3nnfeuW8fhVarWlVDMZ9HWZbxkEXdR/hDREW3B1meR7XhkH8XJBuVrflYDJtdWsT0vZBF8ZbzhwvWNsjZLOL9A5h90oaFtnaIFivOy+X/c1ghoj9ZJ9afa+Dt6ITr0WMIxKnEcS1Hp/7mLhWK4FdPH2I6PYRuHda13fih78OBoR/5nb0atdpasE7InR5jgWPAcaPgeAaLPhqfl0zgls3wLRmx5Gchl8/uFG1yeHlxCf67F6xggvubDZ5tuxon4w64t6xwEbCCEZH4St3G3YL1k7YPv8AZNcJg00Iz1I4ugmdv2tDLaDAybcCL4U4MuF7CI5ixn9xtckk1Fo5PJcxsWTCXsMGbsGMuTpwJNByBIYwFh9Vvpe6N2/EpQXixMcilwi3RG4cXlUvMrHGwhSfgjMyAVbB2DVd0DuMbs3CEp0h9Wu2Nr83CEnTDJ0Ruj1x3G91NY9iXhMmfgXElrULJR4M5GJclkmfBhE+a+u+4Q+wdndy4VB1KuSLoZRF0IEOgECWYV3N4PRVDx9sJ6Ox+aEzz6Bz8iMH5HTChY9CEM0q4H4ioIySS0a8fPFWpVOCJJDFCTlTFGvDKvQEt44OeXUUPwdP3XhgmBdDBzA2HDqYxxEtY/JpS/zi1SUY18iKsoRS5kwYEjmDxJ9XcTKK1VjfXao2whsiV8AfYT+VBFeQznBZLLVG4p6/gd6GE0tk5rgCAg3uKd3ca4gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/b9c942ab3fcf2ed5705519f04db20d86/a6d36/image.png&quot;
        srcset=&quot;/static/b9c942ab3fcf2ed5705519f04db20d86/222b7/image.png 163w,
/static/b9c942ab3fcf2ed5705519f04db20d86/ff46a/image.png 325w,
/static/b9c942ab3fcf2ed5705519f04db20d86/a6d36/image.png 650w,
/static/b9c942ab3fcf2ed5705519f04db20d86/e548f/image.png 975w,
/static/b9c942ab3fcf2ed5705519f04db20d86/3c492/image.png 1300w,
/static/b9c942ab3fcf2ed5705519f04db20d86/ad00e/image.png 1366w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;테스트 피라미드는 테스트 수준별로 단위 테스트, 통합 테스트, 시스템 테스트, 수동 테스트로 구분을 지어놓았다. 서로 다른 테스트 수준과 그 이점을 명확히 이해하고 나면, 우리는 단위 테스트와 이 외의 테스트 수준 중 어떤것에 더 노력을 들여야할지, 그리고 서비스에서 어떤 부분을 테스트 기법으로 테스트할지 결정할 수 있다. 이를위해 각 계층별 테스트 특징을 학습해본다.&lt;/p&gt;
&lt;h3 id=&quot;단위-테스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;단위 테스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단위 테스트&lt;/h3&gt;
&lt;p&gt;단위(Unit) 를 격리래서 테스트하는 것을 단위 테스트라고 한다. 단위 테스트를 구성할 때 가장 어려우면서도 재밌는 점은, 한 단위를 구성하는 요소를 정의하는 명확한 기준이다. 즉, 어떤 크기를 가진 컴포넌트를 단위(Unit) 로 바라봐야 하는가인데, &lt;strong&gt;한 단위는 단 1개의 메소드일 수도 있고, 또는 여러개의 클래스로 구성될수도 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;단위 테스트는 시스템에서 작업 단위를 호출하는 자동화된 코드 조각이다. 그러고 작업 단위는 한 메소드, 한 클래스 또는 함께 동작하는 여러 클래스에 이를 수 있고, &lt;strong&gt;검증 가능한 단 하나의 논리적 목표를 달성한다.&lt;/strong&gt;&quot; -  로이 오쉐로브(2009)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;로이 오쉐로브의 인용문 중 살펴볼 부분은 &quot;검증 가능한 단 하나의 논리적 목표를 달성한다&quot; 이다. 하나의 논리적 단위를 구성하는 것이 단위(Unit) 으로 취급되며, 이 떄문에 한 논리적 단위를 구성하는 요소들은 단 1개의 메소드로 구성될 수 있지만, 여러개의 클래스로도 구성될 수 있는 것이다.&lt;/p&gt;
&lt;h4&gt;모의 객체와 단위 테스트&lt;/h4&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/86c218037931d900e62fadd6694fcd93/37048/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.16564417177914%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAABkElEQVR42p1Ty07CUBDld9y58Z+MP+VWFyYkbkiUxKDBgEB88Cjl0XdLW6BvHi48zr2kijYVcHEy6dx7z5w5My0kvoY8LAIdvjOCIjZhyW/w7CHPJV7+m0LeQeypWEYmHK2H6k0RzfsSJ2W5RbApdhDhtsr3ZIIVIwoNRDMZ/lRDMFV40YMJU7Xp42iuQXq9hibc0bdBUP9PuI5N6LII9fYYZv0UcRiQn/LfhKmSbY+QWEjmCoadGmTxGaZQxFR9oDvWboWMiJEGrrSJMwXldgOdTh2u1qWhGFglc/LTzpBlCBmZawgYd5/Qf6nCoHWpNio4urjCZa0CrGxErIs5K6bsN+WIWmPgvlH03DEehRbEfgsSFQqmElaLPRX+9i7FB1ubUOd7OO61oL2ewxmVkIQTGsqeU45TldxHmStj+2gqA2jlE1j1M8SBv3vKHETi2SPysw9H78Gntr92MTAwbpehD2pYhmZW4Tq2kEFkYUmt81+MLi2pXbaDmzMWbcLkO7eFgsNU7IBNk/+ZE3LvfgJt8RHPQP5EPgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/86c218037931d900e62fadd6694fcd93/a6d36/image-2.png&quot;
        srcset=&quot;/static/86c218037931d900e62fadd6694fcd93/222b7/image-2.png 163w,
/static/86c218037931d900e62fadd6694fcd93/ff46a/image-2.png 325w,
/static/86c218037931d900e62fadd6694fcd93/a6d36/image-2.png 650w,
/static/86c218037931d900e62fadd6694fcd93/e548f/image-2.png 975w,
/static/86c218037931d900e62fadd6694fcd93/3c492/image-2.png 1300w,
/static/86c218037931d900e62fadd6694fcd93/37048/image-2.png 1352w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;관례적으로 단위 테스트는 (데이터베이스나 웹 서비스 등과 같은) 외부 시스템에 의존하지 않는 작은 클래스 집합, 또는 내가(개발자 나 자신) 이 제어하지 않는 무언가를 테스트하는 것으로 작성된다. 이 떄문에 단위 테스트 작성시 모의 객체를 자주 활용하게 된다.&lt;/p&gt;
&lt;p&gt;가령 위와같이 클래스A 에 대해 테스트를 진행한다고 했을때, 이 클래스 A는 단위로 바라볼 수 있다. 이 단위를 테스트할 때, 우리의 목표는 클래스 A 에 대한 테스트에 집중할 수 있도록 최대한 나머지 (A 와 의존관계가 있는 요소들) 로 부터 A 를 격리하는 것이다. 만약 A 가 다른 나머지 클래스들에 의존하게 된다면 그것들을 모두 고려하여 테스트를 진행해야 하므로 A 라는 단위가 아닌 나머지를 모두 고려한 테스트가 되버리며, 단위의 기준이 모호해진다.&lt;/p&gt;
&lt;p&gt;모의 객체, 테스트 더블에 관한 내용은 향후 포스트에서 다루겠다.&lt;/p&gt;
&lt;h3 id=&quot;통합-테스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%86%B5%ED%95%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;통합 테스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;통합 테스트&lt;/h3&gt;
&lt;p&gt;단위 테스트가 오로지 한 단위에 최대한 집중하여 테스트를 진행하는데에 목적을 둔다. 즉, 시스템의 가장 작은 부분에 집중한다. 반면 통합 테스트는 하나의 서비스를 구성하는 여러 구성요소 중 특정 요소들에 대해 외부 요소(DB, 외부 라이브러리) 들과 함께 묶어 테스트 하는 방식이다. 통합 테스트의 목적은 시스템의 여러 구성요소들을 함께 묶어 테스트하는 것이며, &lt;strong&gt;시스템을 구성하는 여러 구성요소들간의 상호작용에 초점을 맞춘다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이때, 구성요소란 우리 작성한 코드의 컴포넌트 여럿을 비롯하여 외부 요소 또한 함께 포함한다. 외부 요소란 개발자가 변경할 수 없는 부분(데이터베이스, JWT 제공처, 외부 라이브러리, 인프라 등) 을 뜻한다. 즉, 통합 테스트는 &lt;strong&gt;우리가 작성한 컴포넌트 코드들과 외부 요소를 모두 하나로 묶었을 때 원활히 상호작용하는 것을 테스트해야 할 때 사용하는 테스트 수준이다.&lt;/strong&gt;  이는 DB에 접근하거나 전체 코드와 다양한 환경이 제대로 작동하는지 확인하는데 필요한 모든 작업을 수행할 수 있다. 그러나, 통합 테스트가 응용 프로그램이 완전하게 작동하는 걸 무조건 증명하지는 않는다.&lt;/p&gt;
&lt;p&gt;또한 유의할 점은, 통합테스트는 시스템내에 여러 컴포넌트, 외부 요소들의 상호작용 관계를 기반으로 테스트하는 것이므로 아래에서 기술할 &lt;code class=&quot;language-text&quot;&gt;인수 테스트(Acceptance Test)&lt;/code&gt; 에서도 활용될 수 있다. 쉽게말해, 통합 테스트를 구성하는 컴포넌트 범위는 작을 수도 있고, 클 수도 있다. 프레젠테이션 계층부터 레포지토리 계층까지 전 범위에 구성된 컴포넌트 여럿을 함께 묶어 테스트하는 것도 통합 테스트가 될 수도 있고, 이것이 곧 상황에 따라 인수 테스트가 되기도 한다. 프레젠테이션 계층에선 JWT 토큰을 인증하고 받아오는 것이 수행될 수 있고, 서비스 및 레포지토리 계층에선 DB 와의 원활한 상호작용 로직 검증이 포함될 수 있다. 반대로 자그마한 컴포넌트 여럿을 묶어 외부 요소와 테스트할 수도 있기에, 더 작은 범위의 계층별 테스트에서도 활용될 수 있다. 그저 통합 테스트는 논리적인 단위를 가진 컴포넌트와 외부 요소 여럿을 함께 묶는다는 의미가 내포되기 때문에, 크기와 범위가 명확하지 않고 다소 모호한 면이 있다.&lt;/p&gt;
&lt;p&gt;스프링부트에서는 클래스 상단에 &lt;code class=&quot;language-text&quot;&gt;@SpringBootTest&lt;/code&gt; 어노테이션을 붙여 통합 테스트를 수행할 수 있다. &lt;code class=&quot;language-text&quot;&gt;@SpringBootTest&lt;/code&gt; 는 스프링의 전체 빈을 컨텍스트에 모두 띄워서 테스트 환경을 구동한다. 즉, 어플리케이션이 동작하는 실제 환경과 동일한 환경에서 테스트를 진행하게 된다.&lt;/p&gt;
&lt;p&gt;통합 테스트를 이해하기 위해, DAO 를 생각하면쉽다. DAO 는 외부 구성요소인 DB 와의 상호작용만을 담당하는 클래스(데이터 접근 객체) 이다. DAO 는 우리가 작성한 코드로, 외부 요소인 데이터베이스와 상호작용한다.  우리는 아래와 같은 DAO 코드를 작성했다고 했을 떄, DAO 를 통한 SQL 쿼리가 제대로 작동하는지 확인하고 싶어 테스트 하고 싶은 상황이라고 생각해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt; userDao&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt; jdbcTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JdbcTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDao&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;INSERT INTO USERS VALUES (?, ?, ?, ?)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        jdbcTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;과연 위와 같은 Dao 의 insert 쿼리문 하나가 원활히 동작하는지 테스트하기 위해 서비스 전체를 모두 통틀어서 테스트 할 필요가 있을까? 당연히 아니다. 우리는 Dao 와 데이터베이스 이 둘의 통합에만 집중하여 테스트하길 바랄 것이다.&lt;/p&gt;
&lt;p&gt;이처럼 통합 테스트는 시스템(서비스) 내의 특정 요소들을 여럿 추출하여, 이들에 대해 테스트하는 방식이다. 이러한 방식은 앞서 말한 &quot;시스템 전체를 테스트하는 대신 구성요소들의 상호작용에 초점을 맞춘다.&quot; 라는 말과 부합한다.&lt;/p&gt;
&lt;h3 id=&quot;시스템-테스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;시스템 테스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시스템 테스트&lt;/h3&gt;
&lt;p&gt;시스템(서비스) 을 더 실질적인 관점에서 바라보자면, 시스템이 가진 모든 구성요소(데이터베이스, FE, BE 전체 구성요소) 를 포함한 테스트를 수행해야 할 것이다. 이것이 바로 시스템 테스트다. 이 테스트 수준은 시스템이 내부에서 어떻게 동작하는지 관심이 없다. 자바로 개발되었는지, MySQL 로 데이터베이스를 구축했는지 여부도 관심이 없다. &lt;strong&gt;오로지 어떤 입력(Input)으로 넣었을 떄 어떤 출력(Output)이 나오는지에 대해서만 관심이 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;이점&lt;/h4&gt;
&lt;p&gt;시스템 테스트의 명백한 이점은 &lt;strong&gt;현실적&lt;/strong&gt;이라는 것이다. 즉, 실제 서비스 사용자가 진행하는 작업과 유사한 행위에 대해 검증하기 떄문에, 우리는 서비스 전체 로직에 대해 더 믿음을 가질 수 있다.&lt;/p&gt;
&lt;h4&gt;취약점&lt;/h4&gt;
&lt;p&gt;하지만 &lt;strong&gt;느리다.&lt;/strong&gt; 전체 시스템을 구동하는 작업은 쉽지 않은 일이다. 또한 &lt;strong&gt;다소 불안정하다.&lt;/strong&gt; 같은 테스트 코드를 실행했을 때, 어떨떄는 통과하고, 어떨떄는 실패하기도 한다. 외부 오픈 API 를 사용하는 활용하는 경우를 생각하면 이해하기 쉬운데, 우리 서비스에서 HTTP POST 요청을 보낸 후 응답이 오는데까지 항상 동일한 시간이 걸린다는 보장이 없다. 어떨떄는 3초가 걸리고, 또 어떨떄는 5초가 걸릴 수 있다. 이 떄문에 시스템 테스트는 예기치 못한 동작을 일으킬 수 있는 불확실성이 너무 많기 떄문에, 테스트를 전적으로 믿기 힘들다.&lt;/p&gt;
&lt;h2 id=&quot;각-테스트-수준을-언제-사용하고-무엇을-테스트해야할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%81-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%88%98%EC%A4%80%EC%9D%84-%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B3%A0-%EB%AC%B4%EC%97%87%EC%9D%84-%ED%85%8C%EC%8A%A4%ED%8A%B8%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;각 테스트 수준을 언제 사용하고 무엇을 테스트해야할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;각 테스트 수준을 언제 사용하고, 무엇을 테스트해야할까?&lt;/h2&gt;
&lt;p&gt;각 테스트 수준을 언제 사용할지에 대한 대답은, &quot;상황에 따라 다르다&quot; 이다. 아래 각 수준별 특징에 따라 알맞은 테스트 수준을 선정하여 작성하는 것이 올바를 것이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 테스트 수준별 활용 상황&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;시스템 테스트 : 애플리케이션의 주요하고, 위험한 실행 흐름에 대해 테스트시 작성&lt;/li&gt;
&lt;li&gt;통합 테스트 : 외부 서비스(DB 등)와의 복잡한 상호작용를 테스트시 작성&lt;/li&gt;
&lt;li&gt;단위 테스트 : 모든 비즈니스 규칙은 여기서 테스트되어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;테스트 피라미드에서, 위로 올라갈수록 테스트는 더 현실적이고 복잡해진다. 또한 위로 올라갈수록 작성 비율이 낮아진다. 즉, 관례적으로 단위 테스트가 작성 비율이 가장 높음을 알 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;단위-테스트를-지향할-것&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%A7%80%ED%96%A5%ED%95%A0-%EA%B2%83&quot; aria-label=&quot;단위 테스트를 지향할 것 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단위 테스트를 지향할 것&lt;/h3&gt;
&lt;p&gt;사람마다 가치관과 기준은 다르겠지만, 단위 테스트를 선호하는 경우가 꽤나 많다. 단위 테스트는 작성하기 쉽고, 빠르다. 작은 단위에 대해 효율적이고 엄격하게 테스트하는 일은, 이보다 더 큰 기능에 대한 굳은 믿음을 가질 수 있게 해준다. 결국 현재 작성한 여러 단위들이 서로 함꼐 동작하여 더 큰 기능을 구성하기 때문이다. 큰 기능은 자신의 작은 단위들에 대해 올바르게 동작할 것이라는 믿음을 가지고 있다.&lt;/p&gt;
&lt;h3 id=&quot;각-수준에서-정확히-무엇을-테스트하는데&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%81-%EC%88%98%EC%A4%80%EC%97%90%EC%84%9C-%EC%A0%95%ED%99%95%ED%9E%88-%EB%AC%B4%EC%97%87%EC%9D%84-%ED%85%8C%EC%8A%A4%ED%8A%B8%ED%95%98%EB%8A%94%EB%8D%B0&quot; aria-label=&quot;각 수준에서 정확히 무엇을 테스트하는데 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;각 수준에서 정확히 무엇을 테스트하는데?&lt;/h3&gt;
&lt;h4&gt;단위 테스트&lt;/h4&gt;
&lt;p&gt;소프트웨어 시스템의 알고리즘, 단일 비즈니스 로직과 관련된 단위에 대해선 단위 테스트를 사용한다. 이때, 대부분의 비즈니스 로직은 메시지를 주고받는 엔티티 클래스에 담겨있다. 이 떄문에 엔티티를 단위 테스트하는 것이 매우 중요하다.&lt;/p&gt;
&lt;p&gt;또한 비즈니스 로직은 대개 외부 서비스에 의존하지 않기 떄문에, 단위 테스트로 쉽게 테스트하고 완전히 제어할 수 있다. 단위 테스트를 이요하면 입력 데이터를 완전히 통제할 수 있을 뿐 아니라, 기대한 대로 동작했는지에 대한 믿음을 확보할 수 있다.&lt;/p&gt;
&lt;h4&gt;통합 테스트&lt;/h4&gt;
&lt;p&gt;앞서 계속 설명했듯이, 테스트 대상 구성요소가 DB, 외부 서비스의 공공 API 와 같은 외부 구성요소와 상호작용 할떄마다 통합 테스트를 사용하는 것이 좋다.&lt;/p&gt;
&lt;p&gt;단, 통합 테스트는 단위 테스트보다 비용이 많이 들고 설정하기 어려우므로, 시스템의 일부를 테스트할 수 있는 유일한 방법일 떄만 사용하도록 하자.&lt;/p&gt;
&lt;h3 id=&quot;인수-테스트-vs-시스템-테스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EC%88%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-vs-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;인수 테스트 vs 시스템 테스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인수 테스트 vs 시스템 테스트&lt;/h3&gt;
&lt;p&gt;필자가 이번 테스트 수준을 학습하면서 가장 햇갈렸던 부분이 인수 테스트와 시스템 테스트의 차이다. 생각의 정교화를 위해, 인수 테스트에 대해 아래와 같이 정리해보겠다.&lt;/p&gt;
&lt;h3 id=&quot;인수-테스트-acceptance-test&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EC%88%98-%ED%85%8C%EC%8A%A4%ED%8A%B8-acceptance-test&quot; aria-label=&quot;인수 테스트 acceptance test permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인수 테스트 (Acceptance Test)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;인수 테스트는 개발이 완료된 소프트웨어에 대해 사용자 요구사항을 충족하는지 테스트하는 것이다.&lt;/strong&gt; 이 떄문에 사용자 입장을 고려하여, 사용자 시나리오(스토리) 를 작성하게 된다. 프로젝트에 참여하는 사람들 (기획자, 클라이언트 대표, 개발자 등) 이 토의를 진행하여, 시나리오를 만들고, 개발자는 이에 의거하여 테스트 코드를 작성한다. 정리하자면, 인수 테스트란 사용자 시나리오에 의거하여 테스트를 작성하는 기법이다. 인수테스트 단계에서 소프트웨어에 문제가 없으면 사용자는 소프트웨어를 인수하게 되고, 프로젝트는 종료된다.&lt;/p&gt;
&lt;p&gt;시나리오에서 요구하는 것은 어떤 사용자가 (= 누가), 어떤 목적으로, 무엇을 하는가이다. 개발을 하다보면 이런 기능은 API 를 통해 드러난다.&lt;/p&gt;
&lt;p&gt;결국 인수 테스트는 소프트웨어 인수를 목적으로 하는 테스트다. 소프트웨어를 인수하기 전에 명세한 요구사항(인수 조건) 대로 잘 동작하는지 검증이 필요하다. 또한 소프트웨어를 인수할 떄, &lt;strong&gt;소프트웨어 내부 구조나 구현 방법을 고려하기 보단, 실제 사용자 관점에서 테스트하는 경우가 많다.&lt;/strong&gt; 따라서, 인수 테스트는 소프트웨어 내부 코드에 관심이 없는 블랙박스 테스팅 기법이다. 실제 사용자 관점에서 테스트할 주로 &lt;code class=&quot;language-text&quot;&gt;E2E(End to End)&lt;/code&gt; 형식을 이용해서 확인한다.&lt;/p&gt;
&lt;h4&gt;E2E (End To End) Test&lt;/h4&gt;
&lt;p&gt;e2e test는 End To End test의 약자이다. 말 그대로 Endpoint, 즉 사용자가 실제 프로그램을 사용하는 상황을 테스트하는 것이다. 그래서 소프트웨어의 내부 구조 보다는 비즈니스 쪽에 초점을 두어 실제 시나리오대로 잘 동작하는지 테스트 하는 것이다. 그래서 Acceptance test(인수테스트)와 같은 의미로 사용되며, 이 또한 소프트웨어 인수를 위해 사용자 시나리오대로 테스트를 해보는 의미로 직결된다.&lt;/p&gt;
&lt;p&gt;이러한 특징을 지닌 인수 테스트는 보통 자바 진영에선 RestAssured, MockMVC 와 같은 테스트 툴을 활용하여 테스트를 작성한다.&lt;/p&gt;
&lt;h3 id=&quot;차이점은-무엇인가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot; aria-label=&quot;차이점은 무엇인가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;차이점은 무엇인가&lt;/h3&gt;
&lt;p&gt;시스템 테스트는 그저 해당 시스템(서비스) 가 완벽하게, 문제없이 동작하는지에 대해 초점이 맞추어져있다. 특정 인풋(Input) 에 대해 정확하게 원하는 아웃풋(Output) 이 도출되는지에 관심이 쏠려있다. 즉, 소프트웨어 장애 리스크에 대해 절대 문제가 발생하지 않도록 작성하는데에 목적을 둔다.&lt;/p&gt;
&lt;p&gt;인수 테스트 또한 문제가 발생하지 않도록 테스트를 진행하는 면에선 비슷하지만, 조금 목적이 다르다. 인수 테스트는 실제 사용자 환경에서, 사용자 입장에서 테스트를 수행하는 것에 초점을 둔다. 다시말해, 사용자가 만족할만한 서비스인지에 대해서 만족도를 높이기 위해 테스트한다. 장애없는, 버그없는 시스템이 구축되는것이 시스템 테스트라면, 인수 테스트는 사용자의 입장에서 불편함이 없고, 만족할 수 있도록 여러 사용자 시나리오에 기반하여 테스트를 작성한다.&lt;/p&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;테스트 더블 (Test Double)&lt;/li&gt;
&lt;li&gt;모의 객체&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Effective Software Testing (Mauricio Aniche)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://engineering.linecorp.com/ko/blog/server-side-test-automation-journey-2&quot;&gt;https://engineering.linecorp.com/ko/blog/server-side-test-automation-journey-2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://star7sss.tistory.com/846#3._%EC%8B%9C%EC%8A%A4%ED%85%9C_%ED%85%8C%EC%8A%A4%ED%8A%B8_(System_Testing)&quot;&gt;https://star7sss.tistory.com/846#3._%EC%8B%9C%EC%8A%A4%ED%85%9C_%ED%85%8C%EC%8A%A4%ED%8A%B8_(System_Testing)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-05-25-unit-test-vs-integration-test-vs-acceptance-test/&quot;&gt;https://tecoble.techcourse.co.kr/post/2021-05-25-unit-test-vs-integration-test-vs-acceptance-test/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dudurian.tistory.com/107&quot;&gt;https://dudurian.tistory.com/107&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (feat. 싱글톤)]]></title><description><![CDATA[학습배경 순수 POJO 로 돌아가서, 싱글톤의 원초적인 개념부터 다시 살펴보며 학습해보고자 합니다. 싱글톤(Singleton) 싱글톤(Singleton…]]></description><link>https://haon.site/haon/java/object-injection/</link><guid isPermaLink="false">https://haon.site/haon/java/object-injection/</guid><pubDate>Wed, 10 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;순수 POJO 로 돌아가서, 싱글톤의 원초적인 개념부터 다시 살펴보며 학습해보고자 합니다.&lt;/p&gt;
&lt;h2 id=&quot;싱글톤singleton&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%B1%EA%B8%80%ED%86%A4singleton&quot; aria-label=&quot;싱글톤singleton permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;싱글톤(Singleton)&lt;/h2&gt;
&lt;p&gt;싱글톤(Singleton) 이란 인스턴스를 오직 하나만 생성할 수 있는 클래스를 말합니다. &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 프로그램 내에서 하나의 객체만 존재하며, &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 프로그램 내의 여러 부분에서 해당 객체를 공유해서 사용해야할 때 활용하는 패턴입니다. 그런데 &lt;strong&gt;클래스를 싱글톤으로 만들면, 이를 사용하는 클라이언트를 테스트하기 어려워질 수 있습니다.&lt;/strong&gt; 타입을 인터페이스로 정의한 다음 그 인터페이스를 구현해서 만든 싱글톤이 아니라면 싱글톤 인스턴스를 가짜(Mock) 구현으로 대체할 수 없기 떄문입니다.&lt;/p&gt;
&lt;p&gt;싱글톤을 만드는 방식은 아래와 같은 2가지 방식이 존재합니다.&lt;/p&gt;
&lt;h3 id=&quot;생성방법1-public-static-final-필드와-private-생성자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%9D%EC%84%B1%EB%B0%A9%EB%B2%951-public-static-final-%ED%95%84%EB%93%9C%EC%99%80-private-%EC%83%9D%EC%84%B1%EC%9E%90&quot; aria-label=&quot;생성방법1 public static final 필드와 private 생성자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;생성방법1. public static final 필드와 private 생성자&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;private 생성자&lt;/code&gt;는 &lt;code class=&quot;language-text&quot;&gt;public static final 필드&lt;/code&gt;인 instance 를 초기화할 때 딱 한번만 호출됩니다. public 이나 proteted 생성자가 없으므로 User 클래스가 초기화될 때 만들어진 인스턴스가 전체 시스템에서 하나뿐임이 보장됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printBookInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;생성방법2-정적-팩토리-메소드로-public-static-멤버로-제공하는-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%9D%EC%84%B1%EB%B0%A9%EB%B2%952-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%86%8C%EB%93%9C%EB%A1%9C-public-static-%EB%A9%A4%EB%B2%84%EB%A1%9C-%EC%A0%9C%EA%B3%B5%ED%95%98%EB%8A%94-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;생성방법2 정적 팩토리 메소드로 public static 멤버로 제공하는 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;생성방법2. 정적 팩토리 메소드로 public static 멤버로 제공하는 방식&lt;/h3&gt;
&lt;p&gt;정적 팩토리 메소드를 통해 public static 멤버를 제공하는 방식도 싱글톤을 보장합니다. 아래의 &lt;code class=&quot;language-text&quot;&gt;instance&lt;/code&gt; 의 경우, &lt;code class=&quot;language-text&quot;&gt;getInstance()&lt;/code&gt; 로 항상 같은 객체의 참조를 반환하므로 싱글톤이 보장됩니다. 여기서도 마찬가지로 &lt;code class=&quot;language-text&quot;&gt;private 생성자&lt;/code&gt; 가 호출되므로 외부에서 신규 객체가 생성되는 상황을 방지할 수 있게됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

   &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printBookInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   	 &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;private-생성자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#private-%EC%83%9D%EC%84%B1%EC%9E%90&quot; aria-label=&quot;private 생성자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;private 생성자&lt;/h3&gt;
&lt;p&gt;여기서 둘의 공통점이자 핵심은 바로 &quot;private 생성자&quot; 입니다. 생성자를 private 로 생성함으로써 외부에서 새로운 객체의 생성을 방지할 수 있게됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;book1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;// book1, book2 는 같은 인스턴스이므로 같은 주소값 반환&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;book2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;지금껏 싱글톤(Singleton) 이 무엇인지를 알아봤으며, 어떤 전략으로 패턴을 생성하는지에 대해 알아봤습니다. 이제부턴 위와같은 싱글톤 및 정적 유틸리티 클래스 방식보다 더 나은 방식을 알아봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;싱글톤이-잘못-사용된-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%B1%EA%B8%80%ED%86%A4%EC%9D%B4-%EC%9E%98%EB%AA%BB-%EC%82%AC%EC%9A%A9%EB%90%9C-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;싱글톤이 잘못 사용된 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;싱글톤이 잘못 사용된 경우&lt;/h2&gt;
&lt;p&gt;많은 클래스들은 상위-하위 관계로 많은 관계에 의존합니다. 예를들어 아래와 같은 번역기(Translator) 클래스는 백과사전(dictionary) 에 의존하는데, 이런 클래스르 싱글톤으로 구현할 수 있을겁니다. 번역기는 &quot;한국어&quot; 백과사전이라는 특정 백과사전 구현체에만 의존 가능한 형태입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Translator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dictionary&lt;/span&gt; dictionary &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Korean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Translator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... (추가구현)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이런 싱글톤 방식은 백과사전을 단 하나만 사용한다고 가정한다는 점에서 썩 좋지 못합니다. 만약 애플리케이션 상황에 따라 Korean 이 아니라 America 백과사전로 갈아끼워야 하는 상황에서는 난처해질 것입니다. 결국 사전 하나로 모든 케이스에 대해 대응할 수 있다는 사고방식은 확장성에 있어 좋지 못합니다.&lt;/p&gt;
&lt;p&gt;필드에서 final 키워드를 제거하고 다른 사전으로 교체하는 신규 메소드를 추가한다면 쉽겠지만, 이 방식은 어색하며 멀티쓰레드 환경에서 사용 불가능합니다. 결론적으로, &lt;strong&gt;사용하는 자원에 따라 동작이 달라지는 클래스에 싱글톤 패턴을 적용하는 것은 적합하지 않습니다.&lt;/strong&gt; 이는 &lt;code class=&quot;language-text&quot;&gt;SOLID&lt;/code&gt; 를 위반하는 행위이기도 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;생성자에-필요한-자원을-파라미터로-넘겨주자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%9D%EC%84%B1%EC%9E%90%EC%97%90-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9E%90%EC%9B%90%EC%9D%84-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EB%A1%9C-%EB%84%98%EA%B2%A8%EC%A3%BC%EC%9E%90&quot; aria-label=&quot;생성자에 필요한 자원을 파라미터로 넘겨주자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;생성자에 필요한 자원을 파라미터로 넘겨주자&lt;/h2&gt;
&lt;p&gt;클래스가 여러 자원 타입에 대한 인스턴스를 유동적으로 지원해야하기 위해선, &lt;strong&gt;객체를 생성할 때 생성자에 필요한 자원을 파라미터로 넘겨주는 방식&lt;/strong&gt; 으로 해결 가능합니다. &lt;code class=&quot;language-text&quot;&gt;의존 객체 주입의&lt;/code&gt; 한 형태로, 예시의 경우는 생성자에다 백과사전 객체를 파라미터로 주입해주면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Translator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dictionary&lt;/span&gt; dictionary&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Translator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Dictionary&lt;/span&gt; dictionary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dictionary &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requireNonNull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dictionary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... (추가구현)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 예시는 dictionary 라는 딱 하나의 자원만 사용하지만, 자원이 몇개든 의존관계가 어떻든간에 상관없이 잘 작동합니다. 또한 불변을 보장하므로, 같은 자원을 사용하려는 여러 클라이언트가 의존 객체들을 안심하고 공유할 수도 있습니다.&lt;/p&gt;
&lt;p&gt;여기서 중요한건 Translator 생성자의 매개변수인 dictionary의 타입이 구체 클래스가 아닌 인터페이스여야 의미가 있다는 것입니다. 만약 의존관계가 구체타입으로 주입이 된다면, 우리는 사전 종류를 바꾸기 위해 결국 클라이언트 코드를 변경해야 합니다. 인터페이스로 들어왔을때 클라이언트는 dictionary의 종류에 상관없이 내부 로직을 진행 할 수 있을 것이겠죠. 이는 객체지향의 원칙 중 다형성을 구현한 것이라 할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;장점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90&quot; aria-label=&quot;장점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h3&gt;
&lt;p&gt;생성자의 파라미터를 통한 의존성 주입을 활용시 다음과 같은 이점을 얻을 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;같은 자원을 사용하려는 여러 클라이언트가 의존 객체들을 안심하고 공유할 수 있다.&lt;/li&gt;
&lt;li&gt;정적 팩토리, 빌더 모두에 동일하게 적용 가능하다.&lt;/li&gt;
&lt;li&gt;클래스의 테스트, 유연성, 재사용성등을 개선해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Effective Java (Joshua Bloch 지음)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nankisu.tistory.com/91&quot;&gt;https://nankisu.tistory.com/91&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@seongwon97/%EC%8B%B1%EA%B8%80%ED%86%A4Singleton-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80&quot;&gt;https://velog.io/@seongwon97/싱글톤Singleton-패턴이란&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devjem.tistory.com/62&quot;&gt;https://devjem.tistory.com/62&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rutgo.tistory.com/517&quot;&gt;https://rutgo.tistory.com/517&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[간략히 적어보는 카카오테크 1주차 생활 회고]]></title><description><![CDATA[회고를 시작하며 쉽지 않은 선발 과정을 거쳐 들어온만큼 뿌듯함, 애정이 있는 과정이다. 무엇보다 내가 지금껏 절실히 바랐던 몰입 환경, 멋진 카카오 현직자분들, 열정 넘치는 동료, 수많은 혜택이 제공되니 너무 기쁘다. 사실…]]></description><link>https://haon.site/회고/first-week/</link><guid isPermaLink="false">https://haon.site/회고/first-week/</guid><pubDate>Tue, 09 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;회고를-시작하며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%9A%8C%EA%B3%A0%EB%A5%BC-%EC%8B%9C%EC%9E%91%ED%95%98%EB%A9%B0&quot; aria-label=&quot;회고를 시작하며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;회고를 시작하며&lt;/h2&gt;
&lt;p&gt;쉽지 않은 선발 과정을 거쳐 들어온만큼 뿌듯함, 애정이 있는 과정이다. 무엇보다 내가 지금껏 절실히 바랐던 몰입 환경, 멋진 카카오 현직자분들, 열정 넘치는 동료, 수많은 혜택이 제공되니 너무 기쁘다.&lt;/p&gt;
&lt;p&gt;사실 1주차 회고를 적을까말까 고민중에 있다가 간략하게 기록을 남기고자 한다. 내가 특별히 하는게 없었다면 마음 편하게 글을 작성했겠지만, 우선순위에서 밀려났기 떄문이다. 최근 내 하루 일과는 &lt;code class=&quot;language-text&quot;&gt;9 to 6&lt;/code&gt; 으로 항상 쉴틈없이 작업을 해야하고, 6시가 넘어가면 제빨리 저녁 식사를 마친후 근처 카페로 달려가 새벽 1시까지 공부한다. 본 과정에서 학습한 내용을 복습하는 것은 몰론, 이 외에도 공부하고, 준비하고, 작업해야 할 것들이 많이 넘쳐나기 때문이다.&lt;/p&gt;
&lt;h3 id=&quot;멋진-회고법은-아직-거절-️&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%8B%EC%A7%84-%ED%9A%8C%EA%B3%A0%EB%B2%95%EC%9D%80-%EC%95%84%EC%A7%81-%EA%B1%B0%EC%A0%88-%EF%B8%8F&quot; aria-label=&quot;멋진 회고법은 아직 거절 ️ permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;멋진 회고법은 아직 거절 🙅‍♂️&lt;/h3&gt;
&lt;p&gt;난 블로깅을 프로그래밍 학습 수단 이외에 마음, 생각의 정리를 위해 작성한다. 글을 작성하면 마음이 진정되고, 생각이 한 곳으로 모여 &lt;code class=&quot;language-text&quot;&gt;정교화(elaboration)&lt;/code&gt; 된다. 사실 이 글을 작성하기전에 여러 글들을 찾아보니 직장 생활을 하며, 훌륭한 훈련기관 내에서 회고법을 실천하는 것을 봤다. 하지만, 아직은 떠다니는 회고 작성법은 내키지 않아 따라하지 않고자 한다. 내가 회고를 작성하는 이유는 생각을 정교화하고, 마음을 평온하게 만들기 위함인데, 굳이 복잡하게 회고법을 도입하고 싶지 않다. 🙂&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;처음-가보는-판교-입과식-진행&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B2%98%EC%9D%8C-%EA%B0%80%EB%B3%B4%EB%8A%94-%ED%8C%90%EA%B5%90-%EC%9E%85%EA%B3%BC%EC%8B%9D-%EC%A7%84%ED%96%89&quot; aria-label=&quot;처음 가보는 판교 입과식 진행 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;처음 가보는 판교, 입과식 진행&lt;/h2&gt;
&lt;p&gt;정확히는 (살면서) 처음 판교를 간건 아니고, 2번째가 되겠다. 그런데 20살때 잠깐 1번간게 전부인지라, 사실상 이번이 처음이라고 해야지. 🤣&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4b953e1af5b8862c6dec75838bd742ac/a5120/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.16564417177914%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAD9klEQVR42g2SfVDTBRzGfxugImkYiBudsDFkKONtg7Exh0wcm2y8bApSEkwmimiKGMSl40zkyEu97kLE6xD1MEh8wXgZsc2plJZ1Z3VdmedVXl2d1dmVvZx/ffr98dzzveeP557vPY8weXyQby4G+WJ4gm9Hp3h4NcCtt89xqeMwg407GWpsErmeY64KjlTaeX19MQfsJlqLdbSYs3kxL50SZSJFyQnoEp9D+GfsNo/OB3kwPMNXI35+9n/I/ctTtJbZ6HZX0u0o57BjPT3OUg7aLXSVmfFVFFGvW0W5OoUihZxcWRyZCUtx5qQi/NU3CUPXeTpyg3vvBXkcvsuBugaSFkZSplbizc9hZ2EeHVYTnXYzHl0mbk2amCqJPLmcQlUSzQ4dfW1r6azVIDzw3+HJ2TBc/JT/rn3NzFunsaWnYkyWo46NwapKpDY3jar0FMpFmJc/j06WSJV2FW0Vek5sMdJTnYFBtZjSAiXCD12DPDo1ze9XPuHf0D3qLVba7Pm02UyYUxPIiItGu2wxGUsWkbNMxqYCDa+6tRxvNtDsSidpx0oknbkIhgQaK3MQ/nZ08dh3lj8vfMSZ9m5k8xdizVHSW19Cu6MATUIMqmcXUJ6Xyv7qbHq3mdnlcaJcIkPYp6H4lwFKfjyF8KuXRR7x5acbemHLCX7bM4BFuYIieRwbk+JosaRzsqmUbeYsPOtW8tpmEzU791Bsr8XibERjtBB1upTEP3po+O4KkvtNCFa5WMq172HkLh1rq3Frs7m9oZA7LjUBezyztjgC6+O54IznvH4eJ7x1OLbuZ/WaChT5epJNOoT65Sj6nZQectH6yg6EJ4GHhIZmcWm1nCszclQvo087n4BJQlDEu3kShg2RjBZGMmVcgNtuY0VWLvuam9jb4uFwt48zI8OMBm/i//wOwuPwT+x1NzBmTeOkJhJfagTjOilBvZSQMYJhXSQdorZfJWXCGMVphwybUYEqRqDBZWF0bo7B8E3Ozt1k+HoQYejgAG+sNTBniWcgM4p+tYQ5ncDVHIFgrsCseLcrpUwXRDCVLyFQFMObuz3U1LjQi5Nq81YzOH6Z8+Eg4XtfIlQbixhfo8JvURJYHctn5SrO2cX25LFM2ZWEjfOYLVzAlC6CiVwpV7MlvFNTyLGedsrXmVkjbrNzax1jM9P09B1FaDIZCJQk41+nJlSaxsd7tzC7+yXGGisItVQTdqZwy7aMmbxIJrKlTIrJT1pS2GjV49u3nV6fjy6vl5d3bUehUiAcsRmYMTzD+8YlfGCOF80TCdmSuVGWQqAqi4AhmpBOgj9LyvgqCZfUAv0GBZvcG9FmZvCCx8vmxgaWJsQRHSXlf7fnUxyAK1RXAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/4b953e1af5b8862c6dec75838bd742ac/a6d36/image.png&quot;
        srcset=&quot;/static/4b953e1af5b8862c6dec75838bd742ac/222b7/image.png 163w,
/static/4b953e1af5b8862c6dec75838bd742ac/ff46a/image.png 325w,
/static/4b953e1af5b8862c6dec75838bd742ac/a6d36/image.png 650w,
/static/4b953e1af5b8862c6dec75838bd742ac/e548f/image.png 975w,
/static/4b953e1af5b8862c6dec75838bd742ac/3c492/image.png 1300w,
/static/4b953e1af5b8862c6dec75838bd742ac/a5120/image.png 2380w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;입과식을 들으며 다시 한번 현직자들은 지금껏 내가 가졌던 뷰(view) 보다 훨씬 높고 훌륭한 뷰, 철학, 가치관을 지녔음을 알게되었다. 어찌어찌 한마디가 모두 감명깊던지. 6개월동안 많은 것을 배워가며 성장할 것이다.&lt;/p&gt;
&lt;h3 id=&quot;지금까지-속했던-열약한-환경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%80%EA%B8%88%EA%B9%8C%EC%A7%80-%EC%86%8D%ED%96%88%EB%8D%98-%EC%97%B4%EC%95%BD%ED%95%9C-%ED%99%98%EA%B2%BD&quot; aria-label=&quot;지금까지 속했던 열약한 환경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;지금까지 속했던 열약한 환경&lt;/h3&gt;
&lt;p&gt;사실 예전부터 정말 절실하게 필요성을 느꼈던것 중 하나가 바로 열정 넘치는 동료다. 지금까지 내가 속한 어떤 그룹에서도 가장 잘하고, 열정 넘치는 사람이었다. 이와 관련해서도 24년도 상반기 회고 내용에서도 기록했었다. 내가 지금껏 속해있던 열악한 환경은 어떠했을까? 그를 나열해보자면 아래와 같다. &lt;strong&gt;부정적인 말들이 많이 섞여있음을 인지하고 있지만, 정말 솔직하게 나 자신을 위해, 나를 속이지않고 회고하고 싶은만큼 내가 솔직하게 경험한것들을 나열해본다.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&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;code class=&quot;language-text&quot;&gt;&quot;맹목적인&quot;&lt;/code&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;/ul&gt;
&lt;p&gt;위 말들을 보면 어떤 키워드가 가장 많이 등장했는가? 바로 &lt;code class=&quot;language-text&quot;&gt;동료&lt;/code&gt; 다. 위 말들에 잘 공감되지 않는다면, 본인이 대학교 내에서 진행했던 팀플 경험을 떠올리면 좋을 것이다. 대학교에서 팀프로젝트를 진행하면서 단 한번도 협업에 있어 불편한 요소가 없었다면, 거짓말이라 생각한다.&lt;/p&gt;
&lt;p&gt;지금까지 여러번 협업을 진행하면서 항상 함꼐 성장할 동료가 부족했다. 가령 기본적인 요구사항, 기능명세서, Tech Spec 조차도 제대로 문서화시키지 못하니 내가 알려줘야하는 입장이 되버리고. 그렇다고해서 부족한점, 협업 방식을 개선할 여지가 있지도 않아보였다. 소통없이 맹목적으로 기술만을 공부하는 것에 고집하니, 난 어떻게 해야할지 난감한 상황이 많았다. 게다가 소리없이 도메인 하나를 개발을 끝내고 PR 을 올리는 상황도 있었기도 하다.&lt;/p&gt;
&lt;p&gt;확실한것은, 특히 요즘 개발자 채용 시장은 상향평준화 되었기 떄문에 소프트 스킬이 단언컨에 매우 큰 비중을 차지한다. 단순 기술만을 고집하는 사람은 살아남기 정말 힘든 시대가 찾아왔다. &quot;그냥 남들하니깐 나도 뭐 블로깅해야지&quot;, &quot;이 기술 사용하면 좋겠지 뭐&quot;, &quot;이 기술 고급 기술이라던데, 써보고 싶으니깐 써야지&quot; 와 같은 근거, 논리없는 사고방식 또한 개발자로써 성장을 가로막는 사고방식이다.&lt;/p&gt;
&lt;p&gt;아무리 내 역량이 매우 뛰어나고, 재능이 있어봤자 주변에 훌륭한 동료들이 존재하지 않는다면 내 역량은 절대 무의미하다. 동료들이 없으니 충분히 금방 현업에 뛰어들만한 역량을 보유했음에도, 몇년간 현업에 참여하지 못하고 지연됐다. &quot;괜찮아. 그래도 힘내보자!&quot; 라는 긍정적인 마인드를 가지려니 했지만, 사실상 주변에 훌륭한 동료들이 없으니 많이 지쳐버렸다. 그래서 지난 회고에도 기록했듯이, 난 컴포트 존에서 벗어나기 위해 모든 하루마다 쉬지않고 절박함을, 필사적인 마음을 갖고 살아왔다. 힘든 여정을 지내며 얻어낸 성과인만큼, 소중한 경험을 쌓아나갈 것이다.&lt;/p&gt;
&lt;h3 id=&quot;현재-내가-직접-얻어낸-훌륭한-환경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%98%84%EC%9E%AC-%EB%82%B4%EA%B0%80-%EC%A7%81%EC%A0%91-%EC%96%BB%EC%96%B4%EB%82%B8-%ED%9B%8C%EB%A5%AD%ED%95%9C-%ED%99%98%EA%B2%BD&quot; aria-label=&quot;현재 내가 직접 얻어낸 훌륭한 환경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;현재 내가 직접 얻어낸 훌륭한 환경&lt;/h3&gt;
&lt;p&gt;정말 많은 고생끝에 내가 얻어낸 것들을 무엇이 있을까? 객관화를 위해, 또한 성취감을 위해 적어본다.&lt;/p&gt;
&lt;ul&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;AWS, Kakao Cloud, ChatGPT Plus, Claude 등 정말 수많은 지원 혜택으로 지속적으로 자기계발 할 수 있는 환경. (특히 백엔드 개발자로써, 서버 인스턴스 비용 지원이 아주 막강하다 👍)&lt;/li&gt;
&lt;li&gt;외부인들은 시중에서 구할 수 없는 카카오 전용 기술 전문서적들 (너무 좋다 😆), 인프런 강의, 맥북 M3 Pro (약 260만원 가량) 등을 지원&lt;/li&gt;
&lt;li&gt;이 외에도 본인의 자기계발을 위한 응시료, 성장 장려.격려금을 지원&lt;/li&gt;
&lt;li&gt;1:1 면담 및 커피쳇&lt;/li&gt;
&lt;li&gt;매달 존재하는 카카오테크내의 다양한 개발자들과의 네트워킹 파티&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 모든것들은 내가 직접 노력하여 얻어낸 것임을 반드시 기억하자. 또한 이런 환경속에 속하게 해준, 날 뽑아준 카카오에게 진심으로 감사를 표한다. 😆&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;향후-방향성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%96%A5%ED%9B%84-%EB%B0%A9%ED%96%A5%EC%84%B1&quot; aria-label=&quot;향후 방향성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;향후 방향성&lt;/h2&gt;
&lt;p&gt;1주차는 입과식을 가고, 이것저것 적응할게 꽤나 많았다. 또한 6시가 되고 작업을 마치면 집에와서 다른 자기계발과 공부를 새벽까지 해야하기 때문에, 힘들었고 어수선했다. 슬슬 적응시기는 끝났고, 앞으로는 어떤 방향성을 가지고 나아가야할까?&lt;/p&gt;
&lt;h3 id=&quot;시간에-따른-방향성-나열&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EA%B0%84%EC%97%90-%EB%94%B0%EB%A5%B8-%EB%B0%A9%ED%96%A5%EC%84%B1-%EB%82%98%EC%97%B4&quot; aria-label=&quot;시간에 따른 방향성 나열 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시간에 따른 방향성 나열&lt;/h3&gt;
&lt;p&gt;우선 다음주차 까지는 저녁에 내가 따로 진행하는 자기계발에 몰입해볼까 한다. 이 과정의 계획상 다음주까지는 내게있어 중요도가 많이 높지 않기 떄문에, &lt;code class=&quot;language-text&quot;&gt;선택과 집중&lt;/code&gt; 을 위해 개인 자기계발에 더 시간을 투자하고자 한다.&lt;/p&gt;
&lt;p&gt;이후에는 점차 시간이 지나갈수록 내게 주어진 업무외 시간에서 개인 자기계발 시간이 줄어들것이 분명하다. 게다가 아직까진 평가 요소에도 포함되지 않는다. 이때부터 블로깅을 통해 카카오테크 과정내에서 얻어낸 지식들을 내것으로 더 체화시키는데 시간을 투자할 계획이다.&lt;/p&gt;
&lt;h3 id=&quot;목적에-따른-방향성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A9%EC%A0%81%EC%97%90-%EB%94%B0%EB%A5%B8-%EB%B0%A9%ED%96%A5%EC%84%B1&quot; aria-label=&quot;목적에 따른 방향성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;목적에 따른 방향성&lt;/h3&gt;
&lt;h4&gt;소프트 스킬&lt;/h4&gt;
&lt;p&gt;난 여기서 기술도 중요하지만, 소프트 스킬을 기르고싶다. 애자일, 스크럼, 글쓰기, 문서화등에 대한 소프트 스킬을 어떻게 쌓아갈 수 있을지 고민해야겠다.&lt;/p&gt;
&lt;h4&gt;야상에서 살아남기, 더 얻어가야한다.&lt;/h4&gt;
&lt;p&gt;본 과정은 정말 야생이다. 열심히 살지 않아도 아무도 터치하지 않는다. 반대로 열심히 살아남기를 도전한다면 지속적인 성장 방법을 알 수 있고, 소프트스킬, 카카오내의 실무 기술등 모든것을 얻어낼 수 있고, 내가 작성한 코드들이 높은 평가를 받고 카카오 계열사 채용 전환 및 현장 경험을 조금이나마 기대해볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9 to 6 내에서 진행되는 모든 일과에 능동적으로 참여하자는 것을 명심하자.&lt;/strong&gt; 능동적으로 참여한만큼 많은 것을 얻어갈 수 있으며, 좋은 혜택과 평가가 부여되는 것이 객관화된 사실인만큼, 더 열심히 살자.&lt;/p&gt;
&lt;h4&gt;No! 나 못해. 라는 두려움에 휘말리지 말것&lt;/h4&gt;
&lt;p&gt;이전부터 항상 회고했듯이, 컴포트존에만 있다면 정체된다. 내가 한번도 해본 적이 없는 새로운 것을 할 때, 두려움을 느끼고 (Fear Zone), 더 나아가면 배운다고 느끼고 (Learning Zone), 그리고 마지막으로 성장하게 된다 (Growth Zone). 내가 아직 잘 떨쳐내지 못하는 점이므로 극복해야할 취약점이라 생각한다. 두려움에 휩싸여 성장할 수 있는 소중한 기회를 잃지말고, 한발짝 더 나아갈 수 있도록 용기를 내보자. 하루 아침에 마인드 셋이 바뀌기란 쉽진 않겠지만.&lt;/p&gt;
&lt;h3 id=&quot;블로그과-더-효과적인-학습전략을-위해&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%94%EB%A1%9C%EA%B7%B8%EA%B3%BC-%EB%8D%94-%ED%9A%A8%EA%B3%BC%EC%A0%81%EC%9D%B8-%ED%95%99%EC%8A%B5%EC%A0%84%EB%9E%B5%EC%9D%84-%EC%9C%84%ED%95%B4&quot; aria-label=&quot;블로그과 더 효과적인 학습전략을 위해 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;블로그과 더 효과적인 학습전략을 위해&lt;/h3&gt;
&lt;p&gt;마지막으로 블로그에 관한 전략을 회고해볼까한다. 앞으로 마주칠 학습 키워드에는 분명히 이전에 학습할 키워드들이 존재할 것이다. 그래서 난, 본 과정에서 이전에 이미 블로깅했던 키워드들에 대해 다음과 같은 4가지의 카테고리로 분류하고자 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(1) 이해도가 높고, 부수적인 지식&lt;/li&gt;
&lt;li&gt;(2) 이해도가 적당한 편이며, 부수적인 지식&lt;/li&gt;
&lt;li&gt;(3) 이해도가 높은 편이며, 중요도가 높은 지식&lt;/li&gt;
&lt;li&gt;(4) 이해도가 적당한 편이며, 중요도가 높은 지식&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(1) 에서 (4) 로 갈수록 블로깅의 타겟에 있어 우선순위가 높은 지식이 된다. 어떠한 지식을 만났을 때, 4가지의 분류중 어떤것에 해당할지 잘 선택하여 노션에 카테고리별로 정리해두고자 한다. (4) 이 끝났다면 (3) 에 대해 블로깅을 진행하는 방식으로 진행해보겠다.&lt;/p&gt;
&lt;p&gt;또한 아직 세컨드브레인 활용 및 효과적인 학습법이 원활하지 않다. 이에대한 학습법도 더 실험을 진행해보고자 한다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[자바 String, StringBuilder, StringBuffer 개념]]></title><description><![CDATA[String 클래스 불변의 특징 : "+" 연산 덧셈연산자 "+" 를 사용해서 문자열을 결합하는 것은 매 연산마다 새로운 문자열을 가진 String…]]></description><link>https://haon.site/haon/java/string-builder/</link><guid isPermaLink="false">https://haon.site/haon/java/string-builder/</guid><pubDate>Sun, 07 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;string-클래스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#string-%ED%81%B4%EB%9E%98%EC%8A%A4&quot; aria-label=&quot;string 클래스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;String 클래스&lt;/h2&gt;
&lt;h3 id=&quot;불변의-특징---연산&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%88%EB%B3%80%EC%9D%98-%ED%8A%B9%EC%A7%95---%EC%97%B0%EC%82%B0&quot; aria-label=&quot;불변의 특징   연산 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;불변의 특징 : &quot;+&quot; 연산&lt;/h3&gt;
&lt;p&gt;덧셈연산자 &quot;+&quot; 를 사용해서 문자열을 결합하는 것은 &lt;strong&gt;매 연산마다 새로운 문자열을 가진 String 인스턴스를 생성하는 행위&lt;/strong&gt;입니다. 때문에 더하기 연산이 많아질경우 메모리공간 낭비가 심해지게 되고, 가능한 문자열 결합횟수를 최소화하는 것이 좋습니다.&lt;/p&gt;
&lt;h3 id=&quot;문자열-비교--equals-와-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%B9%84%EA%B5%90--equals-%EC%99%80-&quot; aria-label=&quot;문자열 비교  equals 와  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문자열 비교 : equals() 와 &quot;==&quot;&lt;/h3&gt;
&lt;p&gt;String 클래스의 생성자를 이용한 경우, new 연산자에 의해서 메모리할당이 이루어지기 때문에 항상 새로운 String 인스턴스가 생성됩니다. 그러나 문자열 리터럴은 &lt;strong&gt;이미 존재하는 것을 재사용&lt;/strong&gt;하는 것입니다. 반면 String 클래스를 이용한 경우는 new 연산자에 의해서 메모리할당이 이루어지기 때문에 항상 &lt;strong&gt;새로운 String 인스턴스가 생성&lt;/strong&gt;됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; str1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// str1, str2 는 동일한 &quot;abc&quot; 주소값을 보유&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; str2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; str3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// str3, str4 는 각자 다른 메모리할당이 이루어짐&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Stirng&lt;/span&gt; str4 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (다른 문자열 인스턴스)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// str1 == str2 -&gt; true&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// str1.equals(str2) -&gt; true&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// str3 == str4 -&gt; false&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// str3.equals(str4) -&gt; true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또 문자열 비교시 주의할점은 &quot;==&quot; 와 equals() 를 활용한 비교 방식입니다.&lt;br&gt;
&lt;code class=&quot;language-text&quot;&gt;&quot;==&quot;&lt;/code&gt; 은 각 String 인스턴스의 주소값을 비교하는 것이며, &lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 는 두 문자열 안의 내용자체를 비교하는 것입니다. 때문에 위에서 적어놓은 것처럼 연산의 결과가 나오게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;string-클래스의-메소드들&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#string-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%9D%98-%EB%A9%94%EC%86%8C%EB%93%9C%EB%93%A4&quot; aria-label=&quot;string 클래스의 메소드들 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;String 클래스의 메소드들&lt;/h3&gt;
&lt;p&gt;이어서 String 클래스에서 제공하는 메소드들중에 유용하게 활용 가능한 기능들을 일부 알아봅시다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;메소드&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;charAt(), contains(), length()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;concat(str)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;주어진 문자열 str 을 뒤에 덧붙인다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;indexOf()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;주어진 문자열(또는 문자) 가 위치해있는 index 위치값을 반환. 못찾으면 -1 을 반환한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;compareTo(str)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;주어진 문자열과 사전순으로 비교한다. 같으면 0, 사전순으로 이전이면 음수를, 이후면 양수를 반환한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;split(regex)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;문자열을 지정된 분리자(regex) 로 나누어 문자열 배열에 담아서 반환한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;toLowerCase(), toUpperCase()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;모든 문자를 소문자/대문자로 변환하여 반환한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;replace(old, new)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;문자열 중의 문자(old) 새로운 문자(new) 로 바꾼다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;valueOf()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;지정된 값(int, long, float, .. 타입) 을 문자열로 변환하여 반환한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;join-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#join-&quot; aria-label=&quot;join  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;join( )&lt;/h3&gt;
&lt;p&gt;join() 은 여러 문자열 사이에 구분자를 넣어서 결합합니다. 구분자로 문자열을 자르는 split() 과 반대 역할을 한다고 생각하면 이해하기 쉽습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; animals &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dog,cat,bear&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; arr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; animals&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; str &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&quot;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;string-을-기본형-값으로-변환--parseint&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#string-%EC%9D%84-%EA%B8%B0%EB%B3%B8%ED%98%95-%EA%B0%92%EC%9C%BC%EB%A1%9C-%EB%B3%80%ED%99%98--parseint&quot; aria-label=&quot;string 을 기본형 값으로 변환  parseint permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;String 을 기본형 값으로 변환 : parseInt()&lt;/h3&gt;
&lt;p&gt;위에서 간략히 설명했기를 &lt;code class=&quot;language-text&quot;&gt;valueOf()&lt;/code&gt; 를 활용하여 기본타입을 문자열로 변환할 수 있었습니다. 반대로 String 을 기본타입으로 변환하려면 &lt;code class=&quot;language-text&quot;&gt;parseInt()&lt;/code&gt; 를 사용하면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;100&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; f &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseFloat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;stringbuffer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stringbuffer&quot; aria-label=&quot;stringbuffer permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;StringBuffer&lt;/h2&gt;
&lt;p&gt;String 클래스는 인스턴스를 생성할 때 지정된 문자열을 변경할 수 없지만, &lt;strong&gt;StringBuffer 클래스는 문자열 변경작업이 가능&lt;/strong&gt;합니다. 내부적으로 문자열 편집을 위한 버퍼(buffer) 를 가지고 있으며, StringBuffer 인스턴스를 생성할 때 그 크기를 지정할 수 있습니다.&lt;/p&gt;
&lt;p&gt;이때, 편집할 문자열의 길이를 고려하여 버퍼의 길이를 충분히 잡아주는 것이 좋습니다. 편집중인 문자열이 버퍼의 길이를 넘어서게되면 버퍼의 길이를 늘려주는 작업이 추가로 수행되기 때문에 작업효율이 떨어지기 때문이죠.&lt;/p&gt;
&lt;h3 id=&quot;stringbuffer-의-변경작업&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stringbuffer-%EC%9D%98-%EB%B3%80%EA%B2%BD%EC%9E%91%EC%97%85&quot; aria-label=&quot;stringbuffer 의 변경작업 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;StringBuffer 의 변경작업&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/f66a5f11-e59f-46d0-a387-31d09a8ce750/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;StringBuffer&lt;/span&gt; sb &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringBuffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
sb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;StringBuffer&lt;/span&gt; sb2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ZZ&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;String 과 달리 StringBuffer 는 변경작업이 가능하다고 했었습니다. 예를들어 &quot;abc&quot; 를 생성했다고 가정해봅시다. 이후 sb 에 문자열을 &quot;123&quot; 을 추가한 후, 이어서 &quot;ZZ&quot; 를 append() 로 추가할때, append() 를 반환타입이 StringBuffer 인데 자신의 주소를 반환합니다. 따라서 sb 에 새로운 문자열이 추가되고 sb 자신의 주소값이 sb2 에게 반환되므로, sb 와 sb2 는 모두 같은 StringBuffer 인스턴스를 가리키게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;stringbuffer-의-비교작업&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stringbuffer-%EC%9D%98-%EB%B9%84%EA%B5%90%EC%9E%91%EC%97%85&quot; aria-label=&quot;stringbuffer 의 비교작업 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;StringBuffer 의 비교작업&lt;/h3&gt;
&lt;h4&gt;(1) == 와 equals() 는 동일한 작업을 수행한다.&lt;/h4&gt;
&lt;p&gt;String 클래스에서는 &lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 메소드를 오버라이딩해서 문자열의 내용을 비교하도록 구현되어 있지만, StringBuffer 클래스는 &lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 메소드를 오버라이딩하지 않아서 &lt;strong&gt;StringBuffer 클래스의 equals 메소드를 사용해도 &quot;==&quot; 연산자로 비교한 것과 같은 결과를 얻습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;StringBuffer&lt;/span&gt; sb &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringBuffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;StringBuffer&lt;/span&gt; sb2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringBuffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sb &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; sb2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sb2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;(2) String 의 toString() 을 사용해서 내부 문자열을 비교하자.&lt;/h4&gt;
&lt;p&gt;StringBuffer 인스턴스 내부에 담긴 문자열을 비교하기 위해선 &lt;code class=&quot;language-text&quot;&gt;toString()&lt;/code&gt; 를 호출해서 String 인스턴스를 얻은 다음, 여기에 &lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 메소드를 사용해서 비교합시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; s1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; s2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sb2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;stringbuffer-의-기능-및-메소드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stringbuffer-%EC%9D%98-%EA%B8%B0%EB%8A%A5-%EB%B0%8F-%EB%A9%94%EC%86%8C%EB%93%9C&quot; aria-label=&quot;stringbuffer 의 기능 및 메소드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;StringBuffer 의 기능 및 메소드&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;메소드&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;append(), capacity(), charAt(), length(), reverse(), toString()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;insert(pos, element)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;지정된 위치(pos) 에 원소(element) 를 삽입한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;delete(start, end), deleteCharAt(index)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;지정된 범위(start ~ end) 또는 특정 위치(index) 데이터를 삭제한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;substring(start), substring(start, end)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;지정된 범위의 부분 문자열을 추출한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;reverse()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;문자열을 거꾸로 뒤집는다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;replace(start, end, str)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;주어진 범위의 부분문자열을 새로운 문자열로 대체한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&quot;stringbuilder&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stringbuilder&quot; aria-label=&quot;stringbuilder permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;StringBuilder&lt;/h2&gt;
&lt;h3 id=&quot;stringbuffer-의-성능이슈&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stringbuffer-%EC%9D%98-%EC%84%B1%EB%8A%A5%EC%9D%B4%EC%8A%88&quot; aria-label=&quot;stringbuffer 의 성능이슈 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;StringBuffer 의 성능이슈&lt;/h3&gt;
&lt;p&gt;StringBuffer 는 멀티쓰레드에 &lt;code class=&quot;language-text&quot;&gt;안전(Thread Safe)&lt;/code&gt; 하도록 동기화되어 있습니다. 이는 StringBuffer 의 성능을 떨어뜨린다는 특징이 있으며, 멀티쓰레드를 고려하지 않아도 되는 상황인 경우는 StringBuffer 의 동기화 기능은 불필요하게 성능만 떨어뜨리게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;stringbuffer-의-진화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stringbuffer-%EC%9D%98-%EC%A7%84%ED%99%94&quot; aria-label=&quot;stringbuffer 의 진화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;StringBuffer 의 진화&lt;/h3&gt;
&lt;p&gt;때문에 StringBuffer 에서 쓰레드의 &lt;strong&gt;동기화 기능만 뺀&lt;/strong&gt; StringBuilder 가 새롭게 추가되었습니다. &lt;strong&gt;StringBuilder 는 StringBuffer 와 완전히 똑같은 기능으로 작성되어 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// (1) StringBuilder 버전&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;StringBuffer&lt;/span&gt; sb1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
sb1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringBuffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
sb1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// (2) StringBuffer 버전&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt; sb2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
sb2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
sb2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드에서 (1) 과 (2) 는 StringBuilder 로 구현되어 있는 코드인가, StringBuffer 로 구현되어 있는가의 차이입니다. 생성부분을 제외하면 모든 부분이 동일합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;자바의 정석 (3rd Edition)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://onlyfor-me-blog.tistory.com/317&quot;&gt;https://onlyfor-me-blog.tistory.com/317&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[자바의 Comparator 과 Comparable 인터페이스]]></title><description><![CDATA[학습배경  ,  에 대한 사용법을 잘 몰라서, 이번 기회에 제대로 학습해보고자 합니다. Comparator 와 Comparable…]]></description><link>https://haon.site/haon/java/comparable-comparator/</link><guid isPermaLink="false">https://haon.site/haon/java/comparable-comparator/</guid><pubDate>Sat, 06 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Comparable&lt;/code&gt; , &lt;code class=&quot;language-text&quot;&gt;Comparator&lt;/code&gt; 에 대한 사용법을 잘 몰라서, 이번 기회에 제대로 학습해보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;comparator-와-comparable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#comparator-%EC%99%80-comparable&quot; aria-label=&quot;comparator 와 comparable permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Comparator 와 Comparable&lt;/h2&gt;
&lt;h3 id=&quot;공통점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%B5%ED%86%B5%EC%A0%90&quot; aria-label=&quot;공통점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;공통점&lt;/h3&gt;
&lt;p&gt;결론부터 말하자면, 둘의 공통점은 &lt;strong&gt;사용자 정의 클래스의 객체들을 비교할 수 있게 해주며&lt;/strong&gt;, 위 인터페이스들을 구현한 사용자 정의 클래스의 객체들을 정렬하고 싶을때 임의의 정렬기준을 쉽게 정의할 수 있도록 도와주는 역할을 수행합니다.&lt;/p&gt;
&lt;p&gt;자바 차원에서 제공해주는 일반 클래스(ex. Integer) 들과 달리, 사용자가 직접 정의한 클래스들의 객체들을 정렬하고 싶어도 명확한 정렬기준이 없습니다. 이때 Comparator 와 Comparable 를 활용한다면 해당 클래스들에 대한 정렬기준을 손쉽게 정의 할 수 있으며, &lt;code class=&quot;language-text&quot;&gt;Collections.sort()&lt;/code&gt; 와 같은 메소드를 호출했을 때 정의한 &quot;정렬기준&quot; 에 따라서 정렬된 결과가 나오게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;차이점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; aria-label=&quot;차이점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;차이점&lt;/h3&gt;
&lt;p&gt;둘의 차이점은 &quot;비교대상&quot; 에 있습니다. &lt;code class=&quot;language-text&quot;&gt;Comparator&lt;/code&gt; 의 경우 두 매개변수 객체를 비교하는 것이고, &lt;code class=&quot;language-text&quot;&gt;Comparable&lt;/code&gt; 는 자기 자신과 매개변수 객체를 비교하는 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Comparator, Comparable 실제 구현코드&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Comparable&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;compareTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt; o&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Comparator&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;compare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt; o1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;To2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 점들을 핵심 포인트로 생각하면서, 지금부터 두 인터페이스들의 개념에 대해 자세히 알아봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;comparable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#comparable&quot; aria-label=&quot;comparable permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Comparable&lt;/h2&gt;
&lt;p&gt;Comparable 은 사용자 정의 클래스의 객체들이 임의의 기준으로 정렬될 수 있도록 만들고 싶을때 활용합니다. &lt;strong&gt;자기 자신과 매개변수 객체를 비교하는 방식&lt;/strong&gt; 으로 정렬기준이 생성됩니다. 또 &quot;정렬기준&quot; 은 &lt;code class=&quot;language-text&quot;&gt;compareTo()&lt;/code&gt; 를 직접 오버라이딩 하여 개발자가 직접 정의해주면 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;compareto&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#compareto&quot; aria-label=&quot;compareto permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;compareTo&lt;/h3&gt;
&lt;p&gt;Comparable 인터페이스를 구현하는 클래스는 &lt;code class=&quot;language-text&quot;&gt;compareTo(T o)&lt;/code&gt; 메소드를 오버라이딩 해야합니다. &lt;code class=&quot;language-text&quot;&gt;compareTo(T o)&lt;/code&gt; 는 파라미터로 객체를 전달받으며, 정렬 기준은 오름차순입니다. 정렬기준은 반환값에 따라 달라집니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;현재 객체가 파라미터로 받아온 객체보다 우선순위가 높다면 : 양수 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;현재 객체가 파라미터로 받아온 객체보다 우선순위가 높다면 : 음수 반환&lt;/li&gt;
&lt;li&gt;둘의 우선순위가 같다면 : 0 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예를 들어봅시다. 우선 사용자 정의 클래스 Student 가 아래와 같이 정의되었다고 해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; score&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Stinrg&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; score&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;score &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; score&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러고 ArrayList 를 활용하여 더미데이터를 넣어주고, 아래처럼 &lt;code class=&quot;language-text&quot;&gt;Collections.sort()&lt;/code&gt; 를 통해 정렬을 시도해보면 에러가 발생합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; students &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
students&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;홍길동&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
students&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;이하온&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
students&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;김철수&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

students&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		 &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	     &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이는 왜 에러가 발생할까요? 그 이유는 &quot;정렬기준&quot; 을 알 수 없기 때문입니다. 때문에 객체를 비교할 수 있도록 Comparator 과 Comparable 를 인터페이스로 사용하여 저희가 직접 &quot;사용자 정의 클래스 객체들의 정렬기준&quot; 을 &lt;code class=&quot;language-text&quot;&gt;compareTo()&lt;/code&gt; 를 오버라이딩하여 구현해줘야 합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Comparable&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; score&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; score&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;score &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; score&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;compareTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt; ohter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;score &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;other&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// return this.other - this.score;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위에서 보듯이 compareTo( ) 를 오버라이딩하여 정수형 반환값을 리턴하도록 했습니다. 이때 compareTo 의 경우는 개발자가 원하는 다양한 방식대로 구현이 가능할겁니다. 예를들어 위 정렬기준 코드를 아래처럼도 정의 가능합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;compareTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;score &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;score&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;score &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;score&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 정의도 가능하지만, 위 Student 클래스의 경우는 if문으로 굳이 분기처리를 복잡하게 구성하지 않고도 정렬이 가능하기 떄문에, 첫번째 방식이 더 좋을겁니다. 이런 구현 방법은 상황에 따라 적절히 구현해주면 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;comparator&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#comparator&quot; aria-label=&quot;comparator permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Comparator&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Comparable&lt;/code&gt; 은 객체 자신이 정렬 가능하도록 구현하는데 목적이 있다면, &lt;code class=&quot;language-text&quot;&gt;Comparator&lt;/code&gt; 는 타입이 같은 객체 2개를 매개변수로 전달받아 우선순위를 비교할 수 있는 정렬기준을 만드는데에 차이가 있습니다. 마치 제 3자가 두 객체를 비교하고 정렬해주는 느낌이라고 보면 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;compare&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#compare&quot; aria-label=&quot;compare permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;compare&lt;/h3&gt;
&lt;p&gt;앞서 살펴본 &lt;code class=&quot;language-text&quot;&gt;Comparable&lt;/code&gt; 는 &lt;code class=&quot;language-text&quot;&gt;compareTo(T o)&lt;/code&gt; 가 인자를 1개만 받고 자기자신과 비교하면 됐다면, &lt;code class=&quot;language-text&quot;&gt;Comparator&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;compare(T o1, T o2)&lt;/code&gt; 는 비교할 2개의 객체를 인자로 받고 정렬기준을 정의해줘야 합니다. 반환값은 아래 기준을 따릅니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;o1 이 o2 보다 우선순위가 높다면 : 양수 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;o1 이 o2 보다 우선순위가 낮다면 : 음수 반환&lt;/li&gt;
&lt;li&gt;둘의 우선순위가 같다면 : 0 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;아래처럼 Comparator 를 구현하는 클래스를 별도로 생성해주면 됩니다. Comparable 과 마찬가지로 비교 대상의 타입을 제네릭으로 전달하면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StudentComparator&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Comparator&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;compare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt; s1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt; s2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; s1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; s2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Collections.sort()&lt;/code&gt; 로 정렬시, 두 객체를 정렬해줄 제 3자로 Comparator 구현 클래스 객체를 함께 인자로 넘겨주면 정렬이 수행됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; students &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
students&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;김철수&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
students&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;나영희&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
students&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;다람쥐&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;Collections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;students&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StudentComparator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;comparator-주요-메소드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#comparator-%EC%A3%BC%EC%9A%94-%EB%A9%94%EC%86%8C%EB%93%9C&quot; aria-label=&quot;comparator 주요 메소드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Comparator 주요 메소드&lt;/h2&gt;
&lt;p&gt;Comparator 는 Comparable 과 달리 다양한 메소드 기능들을 여럿 제공합니다. 그 중 자주 쓰이는 주요 메소드들을 추가적으로 살펴봅시다.&lt;/p&gt;
&lt;h3 id=&quot;reversed&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reversed&quot; aria-label=&quot;reversed permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;reversed&lt;/h3&gt;
&lt;p&gt;정렬 규칙의 반대로 정렬합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Collections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;students&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StudentComparator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reversed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;collectionsreverseorder-collectionsnaturalorder&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#collectionsreverseorder-collectionsnaturalorder&quot; aria-label=&quot;collectionsreverseorder collectionsnaturalorder permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Collections.reverseOrder, Collections.naturalOrder&lt;/h3&gt;
&lt;p&gt;내림차순 정렬 / 오름차순 정렬을 수행합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Collections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;students&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Collections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reverseOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StudentComparator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
students&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StudentComparator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;nullsfirst-nullslast&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nullsfirst-nullslast&quot; aria-label=&quot;nullsfirst nullslast permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;nullsFirst, nullslast&lt;/h3&gt;
&lt;p&gt;Null 을 맨 앞에 정렬 / 맨 뒤에 정렬 합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Collections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;students&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Comparator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;nullsFirst&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StudentComparator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Collections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;students&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Comparator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;nullsLast&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StudentComparator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;comparing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#comparing&quot; aria-label=&quot;comparing permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;comparing&lt;/h3&gt;
&lt;p&gt;JAVA8 에 추가된 Comparator 의 comparing() 을 사용하면 비교 함수를 간단하게 구현할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;students&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Comparator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;comparing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
students&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Comparator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;comparing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reversed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;thencomparing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#thencomparing&quot; aria-label=&quot;thencomparing permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;thenComparing&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Comparator&lt;/code&gt; 를 한번 수행하고 다른 &lt;code class=&quot;language-text&quot;&gt;Compataotr&lt;/code&gt; 를 이어서 수행할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Comparator&lt;/span&gt; compareScore &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Comparator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;comparing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Comparator&lt;/span&gt; compareName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Comparator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;comparing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

students&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;compareScore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;thenComparing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;compareName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;comparable-이-있는데-comparator-는-왜-필요할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#comparable-%EC%9D%B4-%EC%9E%88%EB%8A%94%EB%8D%B0-comparator-%EB%8A%94-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;comparable 이 있는데 comparator 는 왜 필요할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Comparable 이 있는데, Comparator 는 왜 필요할까?&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Comparable&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;Comparator&lt;/code&gt; 는 결국 인스턴스의 정렬기준을 정의하고 정렬을 도와주는데에 공통점이 있습니다. 그렇다면 왜 제 3자가 두 객체의 우선순위를 비교하는 기능인 &lt;code class=&quot;language-text&quot;&gt;Comparator&lt;/code&gt; 가 필요할까요?&lt;/p&gt;
&lt;p&gt;첫번째로는 정렬 대상 클래스의 코드를 수정할 수 없을경우, 두번쨰로는 정렬 대상 클래스에 이미 정의된 &lt;code class=&quot;language-text&quot;&gt;Comparable&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;compareTo&lt;/code&gt; 로 이미 정의한 정렬기준이 있을때 다른 기준으로 정렬하고 싶은 경우, 마지막으로는 여러 정렬 기준을 적용하고 싶을 경우에 사용하면 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;활용방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%99%9C%EC%9A%A9%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;활용방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;활용방법&lt;/h2&gt;
&lt;p&gt;추가적으로 몇가지 유의사항 및 활용방법에 대해 알아봅시다.&lt;/p&gt;
&lt;h3 id=&quot;순서를-고려하는-경우-comparable-를-꼭-구현하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%9C%EC%84%9C%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%8A%94-%EA%B2%BD%EC%9A%B0-comparable-%EB%A5%BC-%EA%BC%AD-%EA%B5%AC%ED%98%84%ED%95%98%EC%9E%90&quot; aria-label=&quot;순서를 고려하는 경우 comparable 를 꼭 구현하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;순서를 고려하는 경우 Comparable 를 꼭 구현하자.&lt;/h3&gt;
&lt;p&gt;순서를 고려해야 하는 값 클래스를 작성한다면 꼭 Comparable 인터페이스를 구현합시다. Comparable 을 구현하면 compareTo 를 오버라이딩하여 손쉽게 컬렉션을 정렬할 수 있기 때문입니다. 예를들어 알파벳, 숫자, 연대와 같이 순서가 명확한 클래스들이 Comparable 의 구현 대상이 될것입니다.&lt;/p&gt;
&lt;h3 id=&quot;값을-차를-기준으로-비교하지-말자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%92%EC%9D%84-%EC%B0%A8%EB%A5%BC-%EA%B8%B0%EC%A4%80%EC%9C%BC%EB%A1%9C-%EB%B9%84%EA%B5%90%ED%95%98%EC%A7%80-%EB%A7%90%EC%9E%90&quot; aria-label=&quot;값을 차를 기준으로 비교하지 말자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;값을 차를 기준으로 비교하지 말자&lt;/h3&gt;
&lt;p&gt;compareTo 메소드에서 필드의 값을 비교할때 &quot;&amp;#x3C;&quot; 와 &quot;&gt;&quot; 연산자는 사용하지 말아야합니다. 가령 아래처럼 구현한다면 정수 오버플로우와 부동 소수점 계산방식에 따른 오류가 발생할 수 있습니다. 그 대신 박싱된 기본 타입 클래스가 제공하는 정적 메소드 &lt;code class=&quot;language-text&quot;&gt;compare()&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;Comparator&lt;/code&gt; 생성 메소드를 이용하여 비교해주도록 합시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Comparator&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; hashCodeOrder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Comparator&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;compare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; o1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; o2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; o1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hashCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; o2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hashCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Effective Java (Joshua Bloch 지음)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://veneas.tistory.com/entry/Java-%EC%9E%90%EB%B0%94-8-Comparator-API-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%95%EB%A0%AC&quot;&gt;https://veneas.tistory.com/entry/Java-자바-8-Comparator-API-데이터-정렬&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.hongo.app/comparing/&quot;&gt;https://blog.hongo.app/comparing/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dding9code.tistory.com/68&quot;&gt;https://dding9code.tistory.com/68&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gmlwjd9405.github.io/2018/09/06/java-comparable-and-comparator.html&quot;&gt;https://gmlwjd9405.github.io/2018/09/06/java-comparable-and-comparator.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/java-comparable-and-comparator/&quot;&gt;https://hudi.blog/java-comparable-and-comparator/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@neity16/Java-%EA%B8%B0%EB%B3%B83-Comparable-Comparator-%EC%B0%A8%EC%9D%B4%EC%A0%90&quot;&gt;https://velog.io/@neity16/Java-%EA%B8%B0%EB%B3%B83-Comparable-Comparator-%EC%B0%A8%EC%9D%B4%EC%A0%90&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[VO(Value Object) 란 무엇이고, 캡슐화를 통해 얻는 이점은 무엇인가?]]></title><description><![CDATA[VO(Value Object) VO 는 도메인에서 1개 또는 그 이상의 속성들을 묶어서 특정 값을 나타내는 객체입니다. 도메인 객체의 일종이고 보통 PK 로 식별값을 가지는 엔티티(Entity) 와 구분해서 사용합니다. 그렇다면 VO…]]></description><link>https://haon.site/haon/java/vo/</link><guid isPermaLink="false">https://haon.site/haon/java/vo/</guid><pubDate>Fri, 05 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;vovalue-object&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#vovalue-object&quot; aria-label=&quot;vovalue object permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;VO(Value Object)&lt;/h2&gt;
&lt;p&gt;VO 는 도메인에서 1개 또는 그 이상의 속성들을 묶어서 특정 값을 나타내는 객체입니다. 도메인 객체의 일종이고 보통 PK 로 식별값을 가지는 엔티티(Entity) 와 구분해서 사용합니다. 그렇다면 VO 는 어떤 조건들에 의해 엔티티와 구분되는지 알아봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;vo-의-규약사항&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#vo-%EC%9D%98-%EA%B7%9C%EC%95%BD%EC%82%AC%ED%95%AD&quot; aria-label=&quot;vo 의 규약사항 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;VO 의 규약사항&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MYCLASS&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bridge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; bridgePath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bridgePath&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;MYCLASS&lt;/span&gt; otherPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MYCLASS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;otherPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hashCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;1-불변성immutable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EB%B6%88%EB%B3%80%EC%84%B1immutable&quot; aria-label=&quot;1 불변성immutable permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 불변성(immutable)&lt;/h3&gt;
&lt;p&gt;VO 는 수정자(setter) 를 가지지 않습니다. 즉, VO 는 불변하다는 특징이 있기 떄문에 내부의 모든 필드는 &lt;code class=&quot;language-text&quot;&gt;final&lt;/code&gt; 로써 변경되지 않음을 보장하게 됩니다.&lt;/p&gt;
&lt;p&gt;불변성으로 인해, VO 는 언제 어디서 호출되던간에 값이 변경되지 않음이 보장됩니다. 예를들어 DB 에서 가져온 데이터를 VO 에다 담는다면 항상 VO 의 값의 무결성을 보장하게 되고, 데이터에 대한 신뢰도가 높아지게 됩니다.&lt;/p&gt;
&lt;p&gt;보듯이 Bridge 는 &lt;code class=&quot;language-text&quot;&gt;final&lt;/code&gt; 로써 모든 필드의 불변성을 보장했습니다. 만약 필드의 변경을 원할시엔, 별도의 setter 가 없고 대신에 새로운 인스턴스를 생성하고 반환하는 방식으로 구성했습니다.&lt;/p&gt;
&lt;h3 id=&quot;2-동등성equality&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EB%8F%99%EB%93%B1%EC%84%B1equality&quot; aria-label=&quot;2 동등성equality permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 동등성(equality)&lt;/h3&gt;
&lt;p&gt;두 객체가 실제로 다른 객체이더라도, 즉 &lt;code class=&quot;language-text&quot;&gt;동일성(identity)&lt;/code&gt; 를 갖지 않더라도 논리적으로 표현하는 값이 같다면 &lt;code class=&quot;language-text&quot;&gt;동등성(equality)&lt;/code&gt; 를 갖습니다. 예를들어 스타벅스 서울점과 인천점에서 각각 만든 아메리카노가 있다고 해봅시다. 이들은 서로 엄연히 다른 객체(물체)이지만, 동등한 가치를 지니므로 논리적으로 &quot;동등하다&quot; 라고 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;떄문에 &lt;strong&gt;&quot;&lt;code class=&quot;language-text&quot;&gt;equals()&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;hashCode()&lt;/code&gt; 를 재정의하라&quot;&lt;/strong&gt; 라는것도 이에 딱 들어맞는 사항입니다. 타입도 같고, 내부의 속성값도 같은 두 VO 객체가 있다면 실제로도 같은 객체로 취급하고 싶을텐데, 이러한 동등성 비교를 Equals 메소드를 재정의함으로써 가능해집니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;equals 의 자세한 재정의 규약은 &lt;a href=&quot;https://velog.io/@msung99/JAVA-Object.equals-%EC%9D%98-%EC%9D%BC%EB%B0%98-%EA%B7%9C%EC%95%BD%EC%9D%84-%EC%A7%80%EC%BC%9C%EC%84%9C-%EC%9E%AC%EC%A0%95%EC%9D%98%ED%95%98%EB%9D%BC&quot;&gt;[JAVA] Object.equals 의 일반 규약을 지켜서 재정의하라&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;3-자가-유효성-검사self-vadlidation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%EC%9E%90%EA%B0%80-%EC%9C%A0%ED%9A%A8%EC%84%B1-%EA%B2%80%EC%82%ACself-vadlidation&quot; aria-label=&quot;3 자가 유효성 검사self vadlidation permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 자가 유효성 검사(Self-Vadlidation)&lt;/h3&gt;
&lt;p&gt;VO 는 자기 유효성 검사라는 특징을 갖습니다. 만약 &lt;code class=&quot;language-text&quot;&gt;int&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;double&lt;/code&gt; 과 같은 원시타입을 사용하면 사용하는 측에서 일일이 직접 검사해줘야합니다. 하지만 원사타입을 VO 로 &lt;code class=&quot;language-text&quot;&gt;래핑(Wrapping)&lt;/code&gt; 하면서 객체를 생성한다면, 유효성 체크가 용이해집니다.&lt;/p&gt;
&lt;p&gt;모든 유효성 검사는 VO 객체 생성 시간에 이루어져야 하며, 떄문에 유효하지 않은 값으로 VO 를 생성할 수 없습니다. 따라서 VO 를 사용하는 클라이언트 입장에선 도메인 규칙이 꺠진다는 걱정없이 마음껏 VO 과 그 내부의 값을 사용할 수 있게됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;원시타입primitive-obsession-을-vo-로-래핑함으로써-얻는-이점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%8B%9C%ED%83%80%EC%9E%85primitive-obsession-%EC%9D%84-vo-%EB%A1%9C-%EB%9E%98%ED%95%91%ED%95%A8%EC%9C%BC%EB%A1%9C%EC%8D%A8-%EC%96%BB%EB%8A%94-%EC%9D%B4%EC%A0%90&quot; aria-label=&quot;원시타입primitive obsession 을 vo 로 래핑함으로써 얻는 이점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원시타입(Primitive Obsession) 을 VO 로 래핑함으로써 얻는 이점&lt;/h2&gt;
&lt;h3 id=&quot;원시타입에-비해-값에-대한-신뢰성이-올라간다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%8B%9C%ED%83%80%EC%9E%85%EC%97%90-%EB%B9%84%ED%95%B4-%EA%B0%92%EC%97%90-%EB%8C%80%ED%95%9C-%EC%8B%A0%EB%A2%B0%EC%84%B1%EC%9D%B4-%EC%98%AC%EB%9D%BC%EA%B0%84%EB%8B%A4&quot; aria-label=&quot;원시타입에 비해 값에 대한 신뢰성이 올라간다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원시타입에 비해 값에 대한 신뢰성이 올라간다.&lt;/h3&gt;
&lt;p&gt;원시타입, 즉 &lt;code class=&quot;language-text&quot;&gt;int&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;float&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;double&lt;/code&gt; 과 같은 원시타입을 VO 로 얻는 이점에는 무엇이 있을까요? 우선 앞서 간접적으로 언급했기를, &lt;code class=&quot;language-text&quot;&gt;유효성 검사&lt;/code&gt; 를 거친 데이터를 활용하므로, 사용하는 입장에서 값에 대한 신뢰성을 보장할 수 있을겁니다.&lt;/p&gt;
&lt;p&gt;또 &lt;code class=&quot;language-text&quot;&gt;불변성&lt;/code&gt; 으로 인해 실수로 로직을 잘못 설계하여 값을 함부로 변경하는 행위를 방지할 수 있으며, 동일한 타입에 대해서만 &lt;code class=&quot;language-text&quot;&gt;동일성&lt;/code&gt; 을 비교할 수 있게됩니다. 즉, 실수로 다른 타입끼리의 값을 변경하는 상황을 방지할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;비즈니스-규칙을-포함할-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EC%A6%88%EB%8B%88%EC%8A%A4-%EA%B7%9C%EC%B9%99%EC%9D%84-%ED%8F%AC%ED%95%A8%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;비즈니스 규칙을 포함할 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비즈니스 규칙을 포함할 수 있다.&lt;/h3&gt;
&lt;p&gt;이어지는 맥락으로, &lt;code class=&quot;language-text&quot;&gt;DTO&lt;/code&gt; 와 달리 VO 는 &lt;code class=&quot;language-text&quot;&gt;비즈니스 로직&lt;/code&gt;을 포함하게 되므로 주어진 상황에서 검증 과정을 거칠 수 있게됩니다. 예를들어 사람의 나이를 표현하는 VO 인 Age 가 있고, 이 나이값에 할당되는 값이 최대로 150살이 넘어 가는 상황은 비정상적일 겁니다. 떄문에 150살을 제한사항으로 비즈니스 정책을 세웠을떄, VO 객체를 생성시 이에 대한 검증을 수행할 수 있게됩니다. 이는 앞서 언급한 &quot;유효성 검사&quot; 와도 비슷한 맥락입니다&lt;/p&gt;
&lt;h3 id=&quot;더-의미있는-값을-지닐-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%EC%9D%98%EB%AF%B8%EC%9E%88%EB%8A%94-%EA%B0%92%EC%9D%84-%EC%A7%80%EB%8B%90-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;더 의미있는 값을 지닐 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 의미있는 값을 지닐 수 있다.&lt;/h3&gt;
&lt;p&gt;DTO 가 단순히 계층간 데이터 전송을 위한 것이라면, VO 는 도메인 계층에 위치하여 더 &quot;유의미한&quot; 값을 표현할 수 있습니다. 예를들어 &quot;계좌 잔액&quot; 과 같은 값을 원시타입 대신에 VO 로 래핑하여 위와 같은 규악사항들을 준수한다면, 분명 더욱이 도메인 객체로써 충분히 중요한 역할을 할 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;요약하자면, &lt;strong&gt;VO 를 통해 도메인을 설게하면 객체가 생성될 때 해당 객체안에 제약사항 및 중요한 정책사항을 추가할 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;동일성(identity) 과 동등성(eqality)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/value-object/&quot;&gt;https://hudi.blog/value-object/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2020-06-11-value-object/&quot;&gt;https://tecoble.techcourse.co.kr/post/2020-06-11-value-object/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[자바의 스트림(Stream) 이란 무엇이고, 어떻게 사용하는걸까?]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/java/stream/</link><guid isPermaLink="false">https://haon.site/haon/java/stream/</guid><pubDate>Wed, 03 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;스트림에 대한 이해도가 부족하여 원활히 활용하지 못하고 있습니다. 이번 기회에 스트림에 대해 개념을 제대로 학습하고, 추후 코드 작성에도 매끄럽게 활용해보고자 합니다. &lt;strong&gt;때문에 이번 포스팅은 개념정리본이 될 것이고, 추후 스트림을 활용한 내용은 추후에 별도로 다루도록 하겠습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;왜-스트림-vs-컬렉션-배열&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-%EC%8A%A4%ED%8A%B8%EB%A6%BC-vs-%EC%BB%AC%EB%A0%89%EC%85%98-%EB%B0%B0%EC%97%B4&quot; aria-label=&quot;왜 스트림 vs 컬렉션 배열 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 스트림? (vs 컬렉션, 배열)&lt;/h2&gt;
&lt;p&gt;스트림은 기존에 컬렉션과 배열을 활용하는 방식에 비해 가독성과 재사용성을 높이고, 모든 데이터 소스를 &lt;code class=&quot;language-text&quot;&gt;추상화&lt;/code&gt;하여 호환성을 높인 기능입니다. 여기서 추상화란, 데이터 소스가 무엇이던간에 같은 코드 및 방식으로 다룰 수 있게 만들어놓았다는 것을 의미합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;스트림의-특징&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9D%98-%ED%8A%B9%EC%A7%95&quot; aria-label=&quot;스트림의 특징 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스트림의 특징&lt;/h2&gt;
&lt;p&gt;스트림의 가장 큰 특징 및 사용법에 대해 알아봅시다.&lt;/p&gt;
&lt;h3 id=&quot;스트림은-데이터-소스-자체를-변경하지-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9D%80-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%86%8C%EC%8A%A4-%EC%9E%90%EC%B2%B4%EB%A5%BC-%EB%B3%80%EA%B2%BD%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;스트림은 데이터 소스 자체를 변경하지 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스트림은 데이터 소스 자체를 변경하지 않는다.&lt;/h3&gt;
&lt;p&gt;우선 스트림은 데이터 소스(컬렉션, 배열) 로 부터 데이터를 읽기만 할뿐, 데이터 소스 자체를 변경하지 않습니다. 정렬된 결과를 컬렉션이나 배열에 담아서 반환할 수도 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; strStream1` &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; strList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 정렬된 결과를 새로운 리스트에 담아서 반환한다.&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; sortedList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; strStream1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;스트림은-일회용이다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9D%80-%EC%9D%BC%ED%9A%8C%EC%9A%A9%EC%9D%B4%EB%8B%A4&quot; aria-label=&quot;스트림은 일회용이다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스트림은 일회용이다.&lt;/h3&gt;
&lt;p&gt;스트림은 Iterator 처럼 일회용으로, 스트림은 한번 사용하면 닫혀서 다시 사용할 수 없습니다. &lt;strong&gt;즉, 스트림은 재사용이 불가능하므로 필요하다면 스트림을 다시 생성해야합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;strStream1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; numOfStr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; strStream1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// -&gt; 에러발생 (스트림이 이미 닫혀있는데 재사용 시도함)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;스트림의-중간연산-최종연산&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9D%98-%EC%A4%91%EA%B0%84%EC%97%B0%EC%82%B0-%EC%B5%9C%EC%A2%85%EC%97%B0%EC%82%B0&quot; aria-label=&quot;스트림의 중간연산 최종연산 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스트림의 중간연산, 최종연산&lt;/h3&gt;
&lt;p&gt;스트림에서 제공하는 다양한 연산을 이용해서 복잡한 작업들을 간단히 처리할 수 있습니다. 연산의 종류에는 &lt;code class=&quot;language-text&quot;&gt;중간 연산&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;최종 연산&lt;/code&gt; 으로 분류할 수 있는데, 각 특징은 다음과 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;중간연산 : 연산결과를 스트림으로 반환하기 때문에 &lt;strong&gt;중간 연산을 연속해서 연결할 수 있다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;최종연산 : 연산 결과가 스트림이 아니다. 때문에 스트림의 요소를 소모하므로 &lt;strong&gt;단 한번만 가능하다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;stream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;distinct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// distinct, limit, sorted : 중간연산  / forEach : 최종연산&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;stream-연산종류&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stream-%EC%97%B0%EC%82%B0%EC%A2%85%EB%A5%98&quot; aria-label=&quot;stream 연산종류 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Stream 연산종류&lt;/h3&gt;
&lt;p&gt;Stream 에 정의된 연산을 나열해보자면 아래와 같습니다. 이 중 몇가지 연산만 사용해 볼 것이고, 필요한 연산들은 그때마다 찾아보고 보충해봅시다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;중간연산&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;distinct()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;중복을 제거&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;filter()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;조건에 안 맞는 요소제외&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;limit()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;스트림의 일부를 잘라낸다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;skip()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;스트림의 일부를 건너뛴다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;peek()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;스트림의 요소에 작업수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;sorted()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;스트림의 요소를 정렬한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;map()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;스트림의 요소를 반환한다. (반환타입 : String&amp;#x3C; R &gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;mapToDouble(), mapToInt(), mapToLong()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;스트림의 요소를 반환한다. (반환타입 : IntStream, DoubleStream, LongStream)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;flatMap()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;스트림의 요소를 반환한다. (반환타입 : Stream&amp;#x3C; R &gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;flatMapToDouble(), flatMapToInt(), flatMapToLong()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;스트림의 요소를 반환한다. (반환타입 : IntStream, DoubleStream, LongStream)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;forEach()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;각 요소에 지정된 작업 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;count()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;스트림의 요소 개수 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;findAny(), findFirst()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;스트림의 최댓값, 최솟값을 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;allMatch(), anyMatch(), noneMatch()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(주어진 조건을) 모두 만족하는지 / 하나라도 만족하는지 / 모두 만족하지 않는지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;reduce()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;스트림의 요소를 하나씩 줄여가면서 계산한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;collect()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;스트림의 요소를 수집한다. 주로 요소를 그룹화하거나, 분할한 결과를 컬렉션에 담아 반환하는데 사용한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;stream-intstream--와-intstream&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stream-intstream--%EC%99%80-intstream&quot; aria-label=&quot;stream intstream  와 intstream permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Stream&amp;#x3C; IntStream &gt; 와 IntStream&lt;/h3&gt;
&lt;p&gt;요소의 타입이 T 인 스트림은 기본적으로 Stream&amp;#x3C; T &gt; 이지만, 오토방식 및 언방식으로 인한 비효율성을 줄이기위해 데이터 소스의 요소를 기본형으로 다루는 스트림, &lt;code class=&quot;language-text&quot;&gt;IntStream&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;LongStream&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;DoubleStream&lt;/code&gt; 이 제공됩니다.
일반적으로 Stream&amp;#x3C; Integer &gt; 대신 IntStream 을 사용하는 것이 더 효율적이고, IntStream 에는 int 타입의 값으로 작업하는데 유용한 메소드들이 포함되어 있으므로, 이를 사용하는 것이 일반적으로 더 좋습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;스트림-생성하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%8A%B8%EB%A6%BC-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0&quot; aria-label=&quot;스트림 생성하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스트림 생성하기&lt;/h2&gt;
&lt;p&gt;지금부터 스트림의 기본 연산 및 생성방법에 대해 간단히 알아보겠습니다. 스트림의 소스가 될 수 있는 대상은 컬렉션, 배열, 임의의 수등 다양하며, 이 다양한 소스들로부터 스트림을 생성하는 방법에 대해 알아봅시다.&lt;/p&gt;
&lt;h3 id=&quot;컬렉션&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%AC%EB%A0%89%EC%85%98&quot; aria-label=&quot;컬렉션 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컬렉션&lt;/h3&gt;
&lt;p&gt;컬렉션의 최상위 조상인 &lt;code class=&quot;language-text&quot;&gt;Collection&lt;/code&gt; 에 &lt;code class=&quot;language-text&quot;&gt;stream()&lt;/code&gt; 이 정의되어 있으므로, Collection 의 자손인 List, Set 을 구현한 &lt;strong&gt;컬렉션 클래스들은 모두 이 stream() 으로 스트림을 생성할 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; list &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Arrays&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;asList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; intStream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

intStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 정상 수행됨&lt;/span&gt;
intStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 에러 발생&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이때 주의할점은, 위처럼 &lt;code class=&quot;language-text&quot;&gt;forEach()&lt;/code&gt; 가 &lt;strong&gt;스트림의 요소를 소모하면서 작업을 수행&lt;/strong&gt;하므로, 같은 스트림에 forEach() 를 2번 호출할 수 없습니다. 따라서 스트림의 요소를 한번 더 출력하려면 스트림을 새롭게 생성해야합니다. 또한 forEach() 에 의해 스트림의 요소가 소모되는 것이지, 원본 소스의 요소들이 소모되는 것은 아니기때문에, 같은 소스로부터 다시 스트림을 생성할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;배열&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%EC%97%B4&quot; aria-label=&quot;배열 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배열&lt;/h3&gt;
&lt;p&gt;배열에 대해 스트림 변환과정은 아래처럼 문자열 스트림으로 변형이 가능하며, 기본형(int, long, double) 과 같은 기본형 배열을 소스로 하는 스트림을 생성하느 메소드도 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 문자열 스트림 생성&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; strStream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; strStream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 기본형(int, long, double) 배열에 대한 스트림 생성&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;IntStream&lt;/span&gt; intStream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IntStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;IntStream&lt;/span&gt; intStream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Arrays&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 외에도 long, double 타입의 배열로 부터 LongStream 과 DoubleStream 을 반환하는 메소드들이 있지만, 여기서 일일이 나열하진 않겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;스트림의-중간연산&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9D%98-%EC%A4%91%EA%B0%84%EC%97%B0%EC%82%B0&quot; aria-label=&quot;스트림의 중간연산 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스트림의 중간연산&lt;/h2&gt;
&lt;p&gt;앞서 컬렉션, 배열등의 데이터 소스를 스트림으로 생성하는 방법에 대해 알아봤습니다. 지금부터는 스트림의 수많은 연산들중에, 가장 기초 연산들에 대해서만 일부분을 알아보겠습니다. 앞서 말했듯이, 자세한 연산 기능들은 필요할때마다 보충하는 방식으로 학습할 예정입니다.&lt;/p&gt;
&lt;h3 id=&quot;빈-스트림-생성--empty&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%88-%EC%8A%A4%ED%8A%B8%EB%A6%BC-%EC%83%9D%EC%84%B1--empty&quot; aria-label=&quot;빈 스트림 생성  empty permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;빈 스트림 생성 : empty()&lt;/h3&gt;
&lt;p&gt;요소가 없는 빈 스트림 생성입니다. 스트림에 연산을 수행한 결과가 하나도 없을때, null 보다 빈 스트림을 반환하는것이 낫습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt; emptyStream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 빈 스트림을 생성해서 반환함&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; emptyStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 0 이 담김&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;두-스트림간의-연결--concat&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%91%90-%EC%8A%A4%ED%8A%B8%EB%A6%BC%EA%B0%84%EC%9D%98-%EC%97%B0%EA%B2%B0--concat&quot; aria-label=&quot;두 스트림간의 연결  concat permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;두 스트림간의 연결 : concat()&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;concat()&lt;/code&gt; 을 사용하면 두 스트림을 하나로 연결할 수 있습니다. 몰론 연결하려는 두 스트림의 요소는 같은 타입이여야 합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; strArr1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;str1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; strArr2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;str2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; strArr3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;str1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; str2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 두 스트림을 하나로 병합&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;스트림의-요소-필터링하기--filter-distinct&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9D%98-%EC%9A%94%EC%86%8C-%ED%95%84%ED%84%B0%EB%A7%81%ED%95%98%EA%B8%B0--filter-distinct&quot; aria-label=&quot;스트림의 요소 필터링하기  filter distinct permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스트림의 요소 필터링하기 : filter(), distinct()&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;distinct()&lt;/code&gt; 에서 중복된 요소를 제거하고, &lt;code class=&quot;language-text&quot;&gt;filter()&lt;/code&gt; 는 주어진 조건에 부합하는 요소만을 추출해냅니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;IntStream&lt;/span&gt; intStream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IntStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;rangeClosed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
intStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 2 4 6 8 10&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;filter() 의 경우 아래처럼 여러개의 조건을 추가 가능합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;intStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
intStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;정렬--sorted&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A0%AC--sorted&quot; aria-label=&quot;정렬  sorted permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정렬 : sorted()&lt;/h3&gt;
&lt;p&gt;다음으로 정렬을 위해선 sorted() 를 사용할 수 있습니다. sorted( ) 안의 인자로 Comparator, 람다식등의 특별한 정렬기준을 넣을 수 있으니 참고합시다. 또 별도의 인자가 없다면 기본정렬로 사전순 정렬을 수행합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; strStream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aaa&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CC&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bb&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
strStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 결과 : CCaaabbccdd&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;변환--map&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B3%80%ED%99%98--map&quot; aria-label=&quot;변환  map permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;변환 : map()&lt;/h3&gt;
&lt;p&gt;다음으로 map() 을 사용하면 스트림의 요소에 저장된 값 중에서 원하는 필드만 뽑아내거나, 특정 형태로 변환할 수 있습니다. 예를들어 아래처럼 스트램에서 name 필드로만 구성된 String 타입의 스트림으로 변환할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; userNameStream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또한 아래에서 설명할 &lt;code class=&quot;language-text&quot;&gt;Collectors&lt;/code&gt; 를 활용해서도 스트림이 아닌 List, Set 와 같은 일반 컬렉션들로도 변환이 가능해집니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; nameList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;maptoint-maptolong-maptodouble&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#maptoint-maptolong-maptodouble&quot; aria-label=&quot;maptoint maptolong maptodouble permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;mapToInt(), mapToLong(), mapToDouble()&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;map()&lt;/code&gt; 은 연산의 결과로 &lt;code class=&quot;language-text&quot;&gt;Stream&amp;lt;T&gt;&lt;/code&gt; 타입의 스트림을 변환하는데, 스트림의 요소를 숫자로 변환하는 경우 IntStream 과 같은 기본형 스트림으로 변환하는 것이 더 유용할 수 있습니다. &lt;code class=&quot;language-text&quot;&gt;Stream&amp;lt;T&gt;&lt;/code&gt; &lt;strong&gt;타입의 스트림을 IntStream 과 같은 기본형 스트림으로 변환할 때 사용하는 것이 &lt;code class=&quot;language-text&quot;&gt;mapToInt()&lt;/code&gt; 와 같은 메소드들입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; studentScoreStream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; studentStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTotalScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;IntStream&lt;/span&gt; studentScoreStream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; studentStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mapToInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTotalScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; allTotalScore &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; studentScoreStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;참고로 &lt;code class=&quot;language-text&quot;&gt;count()&lt;/code&gt; 만 지원하는 &lt;code class=&quot;language-text&quot;&gt;Stream&amp;lt;T&gt;&lt;/code&gt; 와 달리, Instream 과 같은 기본형 스트림은 &lt;code class=&quot;language-text&quot;&gt;sum()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;average()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;max()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;min()&lt;/code&gt; 과 같이 숫자를 다루는데 편리한 메소드들을 제공해줍니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;스트림의-최종연산&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9D%98-%EC%B5%9C%EC%A2%85%EC%97%B0%EC%82%B0&quot; aria-label=&quot;스트림의 최종연산 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스트림의 최종연산&lt;/h2&gt;
&lt;p&gt;최종 연산은 스트림의 요소를 소모해서 결과를 만들어냅니다. 그래서 최종 연산후에는 스트림에 닫히게 되고 더 이상 사용할 수 없습니다. 최종 연산의 결과는 스트림 요소의 합과 같은 단일 값이거나, 스트림의 요소가 담긴 배열 또는 컬렉션일 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;foreach&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#foreach&quot; aria-label=&quot;foreach permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;forEach()&lt;/h3&gt;
&lt;p&gt;계속 봤던 forEach() 로, 반환타입이 void 이므로 스트림의 요소를 출력하는 용도로 많이 사용됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Consumer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;조건-검사&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A1%B0%EA%B1%B4-%EA%B2%80%EC%82%AC&quot; aria-label=&quot;조건 검사 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;조건 검사&lt;/h3&gt;
&lt;p&gt;스트림의 요소에 의해 지정된 조건에 모든 요소가 일치하는지(또는 아닌지) 등의 조건을 확인하는데 활용되는 메소드들입니다. 반환타입으로는 모두 boolean 을 리턴합니다. &lt;code class=&quot;language-text&quot;&gt;allMatch()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;anyMatch()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;noneMatch()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;findFirst()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;findAny()&lt;/code&gt; 등이 이에 해당됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; isSuccess &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stuStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;anyMatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTotalScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; stu1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stuStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTotalScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findFirst&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; sut2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stuStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTotalScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findAny&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이때 스트림의 요소 중에서 조건에 일칠하는 첫번째 절을 반환하는 findFirst() 가 있는데, 주로 &lt;code class=&quot;language-text&quot;&gt;filter()&lt;/code&gt; 와 함께 사용되어 조건에 맞는 스트림의 요소가 있는지 확인하는데 사용됩니다. 병렬 스트림의 경우는 findAny() 를 사용해야 합니다.&lt;/p&gt;
&lt;h3 id=&quot;통계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%86%B5%EA%B3%84&quot; aria-label=&quot;통계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;통계&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;count()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;sum()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;average()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;max()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;min()&lt;/code&gt; 등이 해당되며, IntStream 과 같은 기본형 스트림 요소들에 대해 통계 정보를 내릴 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;리듀싱--reduce-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%EB%93%80%EC%8B%B1--reduce-&quot; aria-label=&quot;리듀싱  reduce  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리듀싱 : reduce( )&lt;/h3&gt;
&lt;p&gt;reduce() 는 &lt;strong&gt;스트림의 요소를 줄여나가면서 연산을 수행하고 최종 결과를 반환합니다.&lt;/strong&gt; 처음 두 요소를 가지고 연산한 결과를 가지고 그 다음 요소와 연산하며, 이 과정에서 스트림의 요소를 하나씩 소모하게 되며 스트림의 모든 요소를 소모하게 되면 그 결과를 반환합니다.&lt;/p&gt;
&lt;p&gt;첫번째 파라미터는 초기값이며, 두번째 파라미터는 연산식입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; intStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; a&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; sum &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; intStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; a&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; max &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; intStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MIN_VALUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; a&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; min &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; intStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MAX_VALUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; a&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;OptionalInt&lt;/span&gt; max &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; intStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;OptionalInt&lt;/span&gt; min &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; intStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;collect&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#collect&quot; aria-label=&quot;collect permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Collect&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;collect()&lt;/code&gt; 는 스트림의 요소를 수집하는 최종 연산으로, reducing 과 비슷합니다. &lt;code class=&quot;language-text&quot;&gt;collect()&lt;/code&gt; 가 스트림의 요소를 수집하려면, 어떻게 수집할 것인가에 대한 방법이 정의되어 있어야 하는데, 이 방법을 정의한 것이 바로 &lt;code class=&quot;language-text&quot;&gt;컬렉터(collector)&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;p&gt;컬렉터는 Collectors 인터페이스를 구현한 것으로, 직접 구현할 수도있고 미리 작성된 것을 사용할수도 있습니다. Collectors 클래스는 미리 작성된 다양한 종류의 컬렉터를 반환하는 static 메소드를 가지고 있으며, 이 클래스를 통해 제공되는 컬렉터만으로도 많은 일들을 할 수 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;collect() : 스트림의 최종연산이다. 매개변수로 컬렉터를 필요로 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Collector : 인터페이스다. 컬렉터는 이 인터페이스를 구현해야한다.&lt;/li&gt;
&lt;li&gt;Collectors : 클래스다. static 메소드로 미리 작성된 컬렉터를 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;스트림을-컬렉션-배열로-변환&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9D%84-%EC%BB%AC%EB%A0%89%EC%85%98-%EB%B0%B0%EC%97%B4%EB%A1%9C-%EB%B3%80%ED%99%98&quot; aria-label=&quot;스트림을 컬렉션 배열로 변환 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스트림을 컬렉션, 배열로 변환&lt;/h3&gt;
&lt;h4&gt;toList()&lt;/h4&gt;
&lt;p&gt;스트림의 모든 요소를 컬렉션에 수집하려면, Collectors 클래스의 &lt;code class=&quot;language-text&quot;&gt;toList()&lt;/code&gt; 메소드를 사용하면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; names &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stuStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			         &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;toCollection()&lt;/h4&gt;
&lt;p&gt;List 와 Set 이 아닌 특정 컬렉션을 지정하려면, &lt;code class=&quot;language-text&quot;&gt;toCollection()&lt;/code&gt; 에 해당 컬렉션 생성자 참조를 매개변수로 넣어주면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; list &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; names&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;toMap()&lt;/h4&gt;
&lt;p&gt;Map 은 key-value 의 쌍으로 지정해야하므로, 객체의 어떤 필드를 각각 key 와 value 로 사용할지를 지정해줘야 합니다. 아래 예시의 경우는, 요소의 타입이 Person 인 스트림에서 사람의 id 를 key 로 하고, value 로 Person 객체를 그대로 저장합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; map &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; personStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; person &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; person&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;toArray()&lt;/h4&gt;
&lt;p&gt;스트림에 저장된 요소들을 &quot;T[]&quot; 타입의 배열로 변환하려면 &lt;code class=&quot;language-text&quot;&gt;toArray()&lt;/code&gt; 를 사용하면 됩니다. 단, 해당 타입의 생성자 참조롤 매개변수로 지정해줘야 합니다. 만약 매개변수를 지정해주지 않으면 반환되는 배열의 타입은 &quot;Object[ ]&quot; 입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; stuNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stdStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; stuNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stdStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; stuNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stdStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// -&gt; 에러발생!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;문자열-결합--joining&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%9E%90%EC%97%B4-%EA%B2%B0%ED%95%A9--joining&quot; aria-label=&quot;문자열 결합  joining permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문자열 결합 : joining()&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;문자열 스트림의 모든 요소를 하나의 문자열로 연결&lt;/strong&gt;해서 반환합니다. 구분자나 접두사와 접미사를 지정가능하며, &lt;strong&gt;스트림의 요소가 String, StringBuffer 처럼 CharSeqence 의 자손인 경우에만 결합이 가능합니다.&lt;/strong&gt; 스트림의 요소가 문자열이 아닌 경우네는 먼저 map() 을 이용해서 스트림의 요소를 문자열로 변환해야 합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; studentNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stdStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;joining&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; studentNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stdStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;joining&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; studentNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stuStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;joining&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;][&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;리듀싱--reducing-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%EB%93%80%EC%8B%B1--reducing-&quot; aria-label=&quot;리듀싱  reducing  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리듀싱 : reducing( )&lt;/h3&gt;
&lt;p&gt;리듀싱 역시 collect() 로 가능합니다. IntStream 에는 매개변수 3개짜리 collect() 만 정의되어 있으므로, &lt;code class=&quot;language-text&quot;&gt;boxed()&lt;/code&gt; 를 통해 IntStream 을 Stream&amp;#x3C; Integer &gt; 로 변환해야지 매개변수 1개짜리 collect() 를 쓸 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; sum &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; intStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; a&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; sum &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; intStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;boxed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reducing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;통계-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%86%B5%EA%B3%84-1&quot; aria-label=&quot;통계 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;통계&lt;/h3&gt;
&lt;p&gt;마찬가지로 통계기능도 보유하고 있습니다. &lt;code class=&quot;language-text&quot;&gt;counting()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;summingInt()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;averagingInt()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;maxBy()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;minBy()&lt;/code&gt; 가 해당되죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stuStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stuStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;counting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Collectors.counting();&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; totalScore &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stuStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mapToInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTotalScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; totalScore &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; studStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;summingInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTotalScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;OptionalInt&lt;/span&gt; topScore &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; studentStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mapToInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTotalScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;그룹화와-분할&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%A3%B9%ED%99%94%EC%99%80-%EB%B6%84%ED%95%A0&quot; aria-label=&quot;그룹화와 분할 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그룹화와 분할&lt;/h3&gt;
&lt;p&gt;이에 관련한 내용까지 다룬다면 내용이 너무 깊어지므로, 추후 별도의 포스팅에서 다루고자 합니다. &lt;code class=&quot;language-text&quot;&gt;groupingBy()&lt;/code&gt; , &lt;code class=&quot;language-text&quot;&gt;partitioningBy()&lt;/code&gt; 등이 해당합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;스트림 기반 그룹화, 분할&lt;/li&gt;
&lt;li&gt;람다와 메소드 참조&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;자바의 정석 (3rd Edition)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jaehyun8719.github.io/2019/07/06/java/java8inaction/chapter6/&quot;&gt;https://jaehyun8719.github.io/2019/07/06/java/java8inaction/chapter6/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://isntyet.github.io/java/java-stream-%EC%A0%95%EB%A6%AC(map)/&quot;&gt;https://isntyet.github.io/java/java-stream-%EC%A0%95%EB%A6%AC(map)/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[자바 컬렉션(Collection) 의 개념과 종류]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/java/collection/</link><guid isPermaLink="false">https://haon.site/haon/java/collection/</guid><pubDate>Tue, 02 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;이번 포스팅은 각 컬렉션의 세부적인 기능 및 메소드 사용법을 살펴보는 것이 아닙니다. 각 컬렉션의 특징과 핵심 기능만을 다룹니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;컬렉션의-핵심-인터페이스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%AC%EB%A0%89%EC%85%98%EC%9D%98-%ED%95%B5%EC%8B%AC-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4&quot; aria-label=&quot;컬렉션의 핵심 인터페이스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컬렉션의 핵심 인터페이스&lt;/h2&gt;
&lt;p&gt;컬렉션을 사용해봤다면 아래의 인터페이스들을 살펴봤을겁니다. 각 특징을 분석해보면 아래와 같습니다. 가장 큰 구분특징은 &quot;순서유지&quot;, &quot;중복허용&quot; 으로 구분지었습니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;인터페이스&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;설명&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;구현 클래스&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;List&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;순서를 유지하며, 중복을 허용한다.&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;ArrayList, LinkedList, Stack, Vector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Set&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;순서를 유지하지 않으며, 중복을 허용하지 않음&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;HashSet, TreeSet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Map&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;key-value 쌍으로 구성. 순서를 유지하지 않고, key 는 중복을 허용하지 않고, value 는 허용한다.&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;HashMap, TreeMap, HashTable, Properties&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이 중에 과거 컬렉션 프레임워크가 개발되기 전부터 존재한 Vector, HashTable 과 같은 컬렉션 클래스들은 가능하면 사용하지 않는것이 좋습니다. 그 대신 새로 추가된 ArrayList 와 HashMap 을 사용합시다.&lt;/p&gt;
&lt;h3 id=&quot;collection-인터페이스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#collection-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4&quot; aria-label=&quot;collection 인터페이스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Collection 인터페이스&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;List 와 Set 의 조상인 Collection 인터페이스&lt;/strong&gt;는 아래와 같은 메소드들이 정의되어 있습니다. 충분히 유추할 수 있는것들은 설명을 생략했습니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;인터페이스&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;add(), addAll()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;contains(), containsAll()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;해당값(또는 값들)이 리스트에 포함되어있는지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;equals()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;동일한 Collection 인지 비교&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;hashCode()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;컬렉션의 해시코드를 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;isEmpty()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;iterator()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;컬렉션의 iterator 를 얻어서 반환한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;remove(), removeAll()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;retainAll()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;파라미터로 넘겨진 컬렉션에 포함된 객체들만 남기고 다른 객체들은 모두 컬렉션에서 삭제한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;size()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;toArray()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;sort()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&quot;list&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#list&quot; aria-label=&quot;list permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;List&lt;/h2&gt;
&lt;p&gt;그럼 지금부터 List 의 각 구현체의 특징에 대해 깊게 살펴봅시다. List 의 구현체로 ArrayList, Vector, LinkedList 를 살펴볼겁니다. 또 앞서 언급했듯이, 각 구현체의 자세한 메소드 기능설명은 하지않고 핵심 기능만 살펴보면서 각 구현체의 특징을 비교해볼겁니다.&lt;/p&gt;
&lt;h3 id=&quot;arraylist-w-vector&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#arraylist-w-vector&quot; aria-label=&quot;arraylist w vector permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ArrayList (w. Vector)&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt; list1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;ArrayList 는 List 인터페이스를 구현하기 때문에 &lt;strong&gt;데이터의 저장순서가 유지&lt;/strong&gt;되고 &lt;strong&gt;중복을 허용&lt;/strong&gt;한다는 특징을 지닙니다. 기존 Vector 를 개선한 컬렉션으로, Vector 와 구현원리와 기능적인 측면에서 동일합니다. Vector 는 단지 기존에 작성된 소스와의 호환성을 위해서 계속 남겨두고 있을 뿐이기 때문에, &lt;strong&gt;Vector 보다는 ArrayList 의 사용을 지향합시다.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;저장할 데이터의 개수를 고려하자.&lt;/h4&gt;
&lt;p&gt;ArrayList 나 Vector 는 Object[] 배열을 이용해서 데이터를 순차적으로 저장합니다. 그러다 &lt;strong&gt;배열에 더 이상 저장할 공간이 없으면, 큰 배열을 생성해서 기존의 배열에 저장된 내용을 새로운 배열로 복사한 다음에 저장합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;때문에, ArrayList 와 Vector 와 같이 배열을 이용한 자료구조는 처음에 인스턴스를 생성시 저장할 데이터의 개수를 잘 고려하여 충분한 용량의 인스턴스를 생성하는 것이 좋습니다. 더 많은 객체를 저장하면 자동적으로 크기가 늘어나기는 하지만, 이 과정에서 처리시간이 많이 포함되기 때문입니다.&lt;/p&gt;
&lt;h4&gt;배열의 한계(단점)&lt;/h4&gt;
&lt;p&gt;배열은 구조가 간단하며 사용하기 쉽고 데이터를 조회시간이 가장 빠르다는 장점을 가지고 있습니다. 하지만 아래와 같은 단점도 존재합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;1.크기 변경이 불가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;2.비순차적인 데이터의 추가 또는 삭제에 시간이 많이 걸린다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;크기를 변경할 수 없으므로, 새로운 배열을 생성해서 복사해야한다는 얘기는 앞서 설명했습니다. 또 실행속도를 향상시키기 위해서는 충분히 큰 크키의 배열을 생성해야 하므로, 메모리가 낭비됩니다. 또한 차례대로 추가하고 마지막에서붙 데이터를 삭제하는 연산은 정말 빠릅니다. 하지만 배열의 중간에 데이터를 추가하라면, 빈자리르 만들기위해 다른 데이터를 복사해서 이동해야합니다.&lt;/p&gt;
&lt;h4&gt;ArrayList 메소드 기능명세&lt;/h4&gt;
&lt;p&gt;ArrayList 에서 제공하는 메소드 및 기능을 살펴보면 아래와 같습니다. 계속 언급했듯이, 자세한 코드 예시는 생략합니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;기능&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;add(), addAll()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;contains()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;해당값(또는 값들)이 리스트에 포함되어있는지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;clear()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;동일한 Collection 인지 비교&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;clone()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;컬렉션의 해시코드를 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;ensureCapacity()&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;ArrayList 의 용량이 최소한 minCapacity 가 되도록 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;get()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;지정된 위치(index) 에 저장된 객체를 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;remove(), removeAll()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;retainAll()&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;파라미터로 넘겨진 컬렉션에 포함된 객체들만 남기고 다른 객체들은 모두 컬렉션에서 삭제한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;size()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;toArray()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;sort()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;trimToSize()&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;용량을 크기에 맞게 줄인다. (빈 공간을 없앤다)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;indexOf()&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;지정된 객체가 저장된 위치를 찾아 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;lastIndexOf()&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;지정된 객체가 저장된 위치를 끝부터 역방향으로 검색해서 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;set()&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;주어진 객체를 지정된 위치(index) 에 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;linkedlist&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#linkedlist&quot; aria-label=&quot;linkedlist permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LinkedList&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a8782f70-a50d-4fed-9fbb-bc08c4d8f57e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;앞서 언급한 배열의 한계를 보완하도록, &lt;strong&gt;더블리 링크드리스트&lt;/strong&gt; 구조의 LinkedList 컬렉션을 도입했습니다. ArrayList 와 비교해봤을때, 어떤 상황에서 무엇이 더 유리할지를 정리해보면 아래와 같습니다.&lt;/p&gt;
&lt;h4&gt;순차적으로 추가/삭제하는 경우에는 ArrayList 가 LinkedList 보다 빠르다.&lt;/h4&gt;
&lt;p&gt;만약 ArrayList 가 새로운 배열에 데이터를 복사하는 비용이 자주 발생하지 않는다면 LinkedList 보다 빠른것이 일반적입니다. 하지만, 반대로 복사비용이 자주 발생한다면 LinkedList 가 더 빠를 수 있습니다.&lt;/p&gt;
&lt;h4&gt;중간 데이터를 추가/삭제하는 경우네는 LinkedList 가 ArrayList 보다 빠르다.&lt;/h4&gt;
&lt;p&gt;중간 요소를 추가 또는 삭제하는 경우, LinkedList 는 각 요소간의 연결만 변경해주면 끝이므로 처리속도가 매우 빠릅니다. 반면 ArrayList 는 각 요소들을 재배치하여 추가할 공간을 확보하거나 빈 공간을 채워야하기 때문에 처리속도가 늦습니다.&lt;/p&gt;
&lt;h4&gt;조회는 ArrayList 가 더 빠르다.&lt;/h4&gt;
&lt;p&gt;배열은 각 요소들이 연속적으로 메모리상에 존재하므로, 간단한 계산만으로 원하는 요소의 주소를 얻어서 곧바로 데이터를 조회할 수 있습니다. 반면 LinkedList 는 불연속적으로 메모리사상에 서로 연결된 구조입니다. 떄문에 저장해야하는 데이터 개수가 많아질수록 데이터 조회 시간이 길이집니다.&lt;/p&gt;
&lt;h3 id=&quot;arraylist-vs-linkedlist--결론이-뭔데-️&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#arraylist-vs-linkedlist--%EA%B2%B0%EB%A1%A0%EC%9D%B4-%EB%AD%94%EB%8D%B0-%EF%B8%8F&quot; aria-label=&quot;arraylist vs linkedlist  결론이 뭔데 ️ permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ArrayList vs LinkedList : 결론이 뭔데? 🤷‍♂️&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;컬렉션&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;읽기&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;추가/삭제&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;특징&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;ArrayList&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;빠르다&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;느리다&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;- 순차적인 추가삭제는 더 빠르다. / - 비효율적인 메모리사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;LinkedList&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;느리다&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;빠르다&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;데이터가 많을수록 접근성이 떨어진다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;다루고자 하는 데이터의 개수가 잘 변하지 않는 경우라면, ArrayList 가 최고의 선택이 될겁니다. 반면 데이터 개수의 변동이 잦다면 LinkedList 를 사용하는것이 더 나은선택이 될 수도 있습니다.&lt;/p&gt;
&lt;h4&gt;LinkedList 메소드 기능명세&lt;/h4&gt;
&lt;p&gt;LinkedList 에서 제공하는 메소드의 기능들을 나열해보면서, List 에 대한 구현체 설명을 마무리짓겠습니다. 앞서 봤던 기능들과 겹치는것이 매우 많아서(ex. add(), indexOf(), retainAll() 등) , LinkedList 에서만 독특하게 제공하는 기능만을 명세했습니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;기능&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;element()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;첫번째 요소를 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;offer()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;지정된 객체를 끝에추가. 성공하면 true, 실패하면 false 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;peek()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;첫번째 요소를 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;pool()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;첫번째 요소를 반환하고 제거한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;remove(), removeFirst(), removeLast()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;addFirst(), addLast()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;getFirst(), getLast()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&quot;set&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#set&quot; aria-label=&quot;set permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Set&lt;/h2&gt;
&lt;p&gt;다음으론 Set 에 대한 구현체 HashSet, TreeSet 입니다.&lt;/p&gt;
&lt;h3 id=&quot;hashset-linkedhashset&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hashset-linkedhashset&quot; aria-label=&quot;hashset linkedhashset permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HashSet, LinkedHashSet&lt;/h3&gt;
&lt;p&gt;HashSet 은 Set 인터페이스를 구현한 가장 대표적인 인터페이스이며, &lt;strong&gt;중복된 요소를 저장하지 않습니다.&lt;/strong&gt; 요소 추가시 add(), addAll() 를 활용하는데, 이미 HashSet 에 저장되어 있는 동일한 요소가 존재한다면 false 를 반환합니다. List 구현체와 달리 저장순서를 유지하지 않으므로, &lt;strong&gt;저장순서 유지를 원한다면 LinkedHashSet 을 활용하면 됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;기능명세&lt;/h4&gt;
&lt;p&gt;기능 및 메소드들은 앞서 살펴봤던 모든 기능들을 제공합니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;기능&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;add(), addAll()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;clear(), clone(), isEmpty(), iterator(), size()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;contains(), containsAll()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;remove(), removeAll()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;toArray()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;예제&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Sttring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; setA &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
setA&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; setA&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;Iterator&lt;/span&gt; it &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; setA&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;iterator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasNext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; tmp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;setA&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tmp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  	setB&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tmp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;treeset&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#treeset&quot; aria-label=&quot;treeset permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TreeSet&lt;/h3&gt;
&lt;p&gt;TreeSet 은 이진검색트리의 성능을 향상시킨 Red-Black Tree 로 구현되어 있습니다. &lt;strong&gt;중복된 데이터의 저장을 허용하지 않으며 정렬된 위치에 저장하므로, 저장순서도 유지하지 않습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;언제사용할까?&lt;/h4&gt;
&lt;p&gt;HashSet 에 비하면 자주 사용되지는 않는데, 일단 특징을 알아봅시다. 트리는 데이터를 순차적으로 저장하는 것이 아니라 저장위치를 찾아서 저장해야하고, 삭제하는 경우 트리의 일부를 재구성해야하므로 링크드리스트보다 &lt;strong&gt;데이터의 추가/삭제 시간이 더 오래걸립니다.&lt;/strong&gt; 대신 배열이나 링크드리스트에 비해 &lt;strong&gt;검색과 정렬기능이 더욱 뛰어납니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;map&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#map&quot; aria-label=&quot;map permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Map&lt;/h2&gt;
&lt;h3 id=&quot;hashmap&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hashmap&quot; aria-label=&quot;hashmap permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HashMap&lt;/h3&gt;
&lt;p&gt;HashTable 와 HashMap 관계는 Vector 와 ArrayList 의 관계와 같습니다. 따라서 &lt;strong&gt;새로운 버전인 HashMap 을 사용합시다.&lt;/strong&gt; HashMap 은 key-value 를 묶어서 하나의 데이터(엔트리) 로 저장합니다. 또 &lt;code class=&quot;language-text&quot;&gt;해싱(Hashing)&lt;/code&gt; 을 사용하기 때문에 &lt;strong&gt;대량의 데이터를 검색&lt;/strong&gt;하는데 있어 뛰어난 성능을 보입니다.&lt;/p&gt;
&lt;p&gt;키(Key) 는 유니크(Unique) 한 특징(즉, 중복 허용안함) 을 지니며, 값(value) 는 중복을 허용합니다.&lt;/p&gt;
&lt;h3 id=&quot;mapentry&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mapentry&quot; aria-label=&quot;mapentry permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Map.Entry&lt;/h3&gt;
&lt;p&gt;HashMap 이 데이터를 어떻게 저장하는지 확인하기 위해 실제소스의 일부를 발췌해봤습니다. 이렇게 HashMap 은 Entry 라는 내부클래스를 정의하고, 다시 Entry 타입의 배열을 선언하고 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AbstractMap&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cloneable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Serializable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;transient&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Entry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; table&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Entry&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Entry&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Map.Entry 기능명세&lt;/h4&gt;
&lt;p&gt;Map.Entry 인터페이스의 메소드 기능은 아래와 같습니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;기능&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;equals()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;동일한 Entry 인지 비교한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;getKey()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Entry 의 Key 객체를 반환한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;getValue()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Entry 의 Value 객체를 반환한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;hashCode()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Entry 의 헤시코드를 반환한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;setValue()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Entry 의 value 객체를 지정된 객체로 바꾼다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;HashMap 기능명세&lt;/h4&gt;
&lt;p&gt;HashMap 의 기능 및 메소드들은 앞서 살펴봤던 모든 기능들을 제공합니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;기능&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;clear(), clone(), isEmpty(), size()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;(생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;containsKey(), containsValue()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;key (또는 value) 의 포함여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;entrySet()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;HashMap 에 저장된 key, value 를 엔트리(key, value의 결합) 의 형태로 Set 에 저장해서 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;keySet()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;HashMap 에 저장된 모든 key 가 저장된 Set 을 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;values()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;HashMap 에 저장된 모든 값을 컬렉션의 형태로 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;get()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;지정된 key 에 대한 value 를 반환. 못찾으면 Null 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;put()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;지정된 Key 와 value 를 HashMap 에 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;putAll()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;지정된 Map 에 저장된 모든 요소를 HashMap 에 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;replace()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;지정된 key, value 를 객체(value) 로 대체&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;예시코드&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grammerTest3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; map &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;김자바&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;이자바&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;강자바&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;안자바&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;90&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt; set &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entrySet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Iterator&lt;/span&gt; it &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; set&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;iterator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasNext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Entry&lt;/span&gt; e &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Entry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;이름:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;점수:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        set &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keySet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;참가자 명단:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; set&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Collection&lt;/span&gt; values &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        it &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; values&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;iterator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; total &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasNext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            total &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;intValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;treemap&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#treemap&quot; aria-label=&quot;treemap permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TreeMap&lt;/h3&gt;
&lt;p&gt;추가적으로 TreeMap 에 대해서 간단히만 알아봅시다. 이름그대로 이진검색트리의 형태로, key-value 쌍으로 이루어진 엔트리를 저장합니다.&lt;/p&gt;
&lt;h3 id=&quot;hashmap-vs-treemap&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hashmap-vs-treemap&quot; aria-label=&quot;hashmap vs treemap permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HashMap vs TreeMap&lt;/h3&gt;
&lt;p&gt;HashMap 과 TreeMap 를 비교해보면, 조회 성능이 대부분의 경우에서 HashMap 이 TreeMap 보다 뛰어나므로, &lt;strong&gt;대부분의 상황에선 HashMap 을 사용하는 것이 좋습니다.&lt;/strong&gt; 다만, 범위검색이나 정렬이 필요한 경우에는 TreeMap 을 사용합시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Comparator, Comparable&lt;/li&gt;
&lt;li&gt;Iterator, ListIterator, Enumeration&lt;/li&gt;
&lt;li&gt;Arrays&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;자바의 정석 (3rd Edition)&lt;/li&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fruitdev.tistory.com/141&quot;&gt;https://fruitdev.tistory.com/141&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[프로그래밍을 공부하며 능동적인 성장을 위한 메타인지 글쓰기 학습법]]></title><description><![CDATA[2015년 학창시절, 프로그래밍을 처음 접하다 내가 프로그래밍을 처음 접하게된 시절은 16살, 중학생 시절이다. 지금도 한창 유행중인 압도적 1위 게임,  를 즐겨왔다. 철없던 시절, 친구들과 PC…]]></description><link>https://haon.site/회고/growth-learning/</link><guid isPermaLink="false">https://haon.site/회고/growth-learning/</guid><pubDate>Thu, 27 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/09b3f9c53f4f2d24de047121f45c22f2/6b26f/image-10.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.533742331288344%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAACn0lEQVR42jWSSUxTURRA/xYTRZeCEYoYjRpIJMjWhUsW6toYYxSDRuNAJS6MSpUFUeNSYSExERWsnX5LgU6Ult/xf1o6mSgoIQaQ1rhishxff/Qm793kvnfPHSXLRwsOix2X7MIz5iEcDCHbZSYmPCQSKrIsM9DfjxKJoiiKsCXxev2MutxEwhH8Hr/uNyqPItucSBazDadDwMa9OiwpHHx+Pz9XV6lIsVSkUMhTLm+zvb2t20qlkvgTQE1oKGGFSX+QiX9QSbY7RQQvoWCYRCxJLpdnKhTWQRUpl8usra3p57+simCZXEH8+U06nSEaiTE1GcI74UOqXOGpaZJJjUwmx7dvC3p5y8vLuvP6+jq/fpVElp8xGo2YTCZ6enq43HGFO8a7RGMJ8gIeE7rCkcIhAYur5LJ5vnyZY2lphbgoZWVlRQdubW3pOpVKUV1dzc6du9hfV8eOqiqqd+/B65tkYWFRvKfRVA0pHo2TTs3qGczPfxegEhkB39jY0EGbmxuU/2yK0tLU19djMBhobGykre0ELS2teARwcfEH2WyOzGwGKapEmRHkz4UCc1/nRH9KmC0ubty6R4+pD9Pjp5h6n3PtRhc1NXup2VdLw4EGmpqbaG5uxheYolgsirLzpLQZpGAghBqPM2j1YHxpQ8sWcI8HaT9znnMXrnOx4zaXOo20nz1PTa2BJsMhDtYf4uixVg4fOY7NMcr7YQtjYsozqgAGvJNMiz72vXjNg75BonENNZVFm80TU2dRYhqReIq3IzKd17t51GXifvcTrt58yMlTp3k1MMjj3me8e28WK6ciWc1WKqsz7h4jFAjgEVP/MGLlzZCFYQFx2tz47G6sn1zYRxw4h+3Y3n5CdozxYciM0+kWCSn6HjplF38B0tpnu05Q+5MAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/09b3f9c53f4f2d24de047121f45c22f2/a6d36/image-10.png&quot;
        srcset=&quot;/static/09b3f9c53f4f2d24de047121f45c22f2/222b7/image-10.png 163w,
/static/09b3f9c53f4f2d24de047121f45c22f2/ff46a/image-10.png 325w,
/static/09b3f9c53f4f2d24de047121f45c22f2/a6d36/image-10.png 650w,
/static/09b3f9c53f4f2d24de047121f45c22f2/e548f/image-10.png 975w,
/static/09b3f9c53f4f2d24de047121f45c22f2/3c492/image-10.png 1300w,
/static/09b3f9c53f4f2d24de047121f45c22f2/6b26f/image-10.png 1658w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;2015년-학창시절-프로그래밍을-처음-접하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2015%EB%85%84-%ED%95%99%EC%B0%BD%EC%8B%9C%EC%A0%88-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%84-%EC%B2%98%EC%9D%8C-%EC%A0%91%ED%95%98%EB%8B%A4&quot; aria-label=&quot;2015년 학창시절 프로그래밍을 처음 접하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2015년 학창시절, 프로그래밍을 처음 접하다&lt;/h2&gt;
&lt;p&gt;내가 프로그래밍을 처음 접하게된 시절은 16살, 중학생 시절이다. 지금도 한창 유행중인 압도적 1위 게임, &lt;code class=&quot;language-text&quot;&gt;리그오브레전드&lt;/code&gt; 를 즐겨왔다. 철없던 시절, 친구들과 PC방에 들러 랭크 게임전을 돌리고, 티어를 올리는데 열정을 다했다. 하라는 공부는 안하고. 😂&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c6a1e71a962163bc61b37927aed58aba/58213/image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 79.75460122699387%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAAD9UlEQVR42l2Ua2xTZRjHzwBRM0Y0EqOgSGSsXdfLej3n9Kxdu7Zr18vaso3edmGbY7CNXdoNNy8QJaJOBHQsYUT9oH7wi4EgijqEJQghaiIx4iUGjFE/+MHPfvz5tkCifPjlOe855/m//+d9nzySwWDCYjTSaDJhMJgxCgz1Jux2GVl1YzSZqW8Q3wR6oxlLo5XGxkZ0Oh16vV7EOsxWO4r412azIZVOXua5k5/x/NInjB37lPGjZxk/8hGjh08x8vopJo6dZvKNMxQXznBg6TTpTB/NniYURRUoyLILzesj0Z6kpaUF6b1r//DW1b85cfkvli79yeLFGyws32Th/E3eXP6FxQu/cfzCDU6s/M77V/8gMzAmRGRU9Y6gjKZpBIMBmkSU3l6+yIdXf+Tdleuc/PgLPrj0DWe+/plTV75k+bsbnP3qc1Z++J5z365w5ddr9O8ZxObwCEFZCMq3HXoIRGJ4fAEk7/CLpCaO0jX2DJkxG4PTTSRn5omWDlE4+A5dM6OU5oucP7eFn643kOvWsKkxFOFGcYvSVQ1V86L5w1hlD5K+8ApbdsyTeCrG7Ox6ClNGthXm2Jx7lcezL/Bk92GeyB0iW0oxV6whEPMjh/IoLXEUfxzZn0DzxQgGQqLkJqSN7ft4JD7L5vZJoj1edIk+Ho5Ms7FdkJxlU+ppHut4lkdTB7Dlh1B8zShOV+X8VFGyyyXj9zeRTrfi9fqR7m+d4t7QOGuCk1Q1T1HtzPOQPc0DWh/Vob2sixRZL3iwrUh1bD8NWgS37PzPLatCyEki3oy/OYhUq7VR646wVQ1TK9iqRqhVQmyVW8W6jTp3FF1TDL2nTALV40dz3+q5Mna7HYfDJZy6KxtIQ7kUZXYJhnPbGcqmGS50MdKbY0/vDkZ2ZhntzzEiGBXPiWiUgDivcnKl7Nvto6oKTqcDqVgsUiwVmZ4uMTk1xdzsPsZ3DzDYk2F4oMBQf55dO7sFBYb7C8QT7fgCrbicZVf/p+xW0ndOoevYS0PXGPWdE+gzsxw8ssDuvix9+e0MdHfSX7hNvpNIpA1/MFRpaIfDUeFW2Q6sVivSWmucGiUjLmMHa61JVpliWDMlFhcXyKXa6Mkk6ckmK7E3m8KrqSLRVkm+G4vFgrTKlKqIrXN2UmVKssmXRtoWoWPiIEdfe4lk2Ee+I062I0a+M44qO8TAMFWS72A2myvRJN5LG+Qga00h7jGG2CCHuM8cZrUxKkTDzLx8nP1zJWIBD13tYbqSYVwOm5hKDRjFhLobg8EgHOqDrLMEWN/oZ1VdkNW6AGv0AarqAtSYY8wfX2L3YC9RIZqOBrFbLejr6yvJd6gX6zLlcfYvq9dpdTFg3oEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/c6a1e71a962163bc61b37927aed58aba/a6d36/image.png&quot;
        srcset=&quot;/static/c6a1e71a962163bc61b37927aed58aba/222b7/image.png 163w,
/static/c6a1e71a962163bc61b37927aed58aba/ff46a/image.png 325w,
/static/c6a1e71a962163bc61b37927aed58aba/a6d36/image.png 650w,
/static/c6a1e71a962163bc61b37927aed58aba/58213/image.png 902w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;그떄부터 &lt;strong&gt;나도 이런 게임을 한번 직접 만들어보고 싶다!&lt;/strong&gt; 라는 막연한 생각이 들었다. 게임을 직접 개발하기 위해선 프로그래밍을 학습해야 함을 알게 되었다. 그 당시부터 &lt;code class=&quot;language-text&quot;&gt;Unity 3D&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;C#&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;C++&lt;/code&gt; 으로 프로그래밍을 독학하기 시작했다. 유튜브에 공개된 Unity 3D 튜토리얼을 통해 기본 사용법, 프레임워크 툴 기본 사용법을 익히고, 부족한 부분이 생길때마다 그떄그때 이론 서적을 통해 보충하는 방식으로 게임을 만들어냈다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/afdf52a9ac218bd8e82daaa9cba0fd46/35252/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 73.00613496932516%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAACPElEQVR42oVUa2/aQBDk//8JVPVLW/GhEtCKFKRWDVFAAoVXCQZsnnbN04DBgG2mN0sdEdHQk1b23t7N7e7MXazb7aLX68GyLPn2+32Mx2PYto3fpomBYcBS3+FwCMdx8L8RW61W2Gw22G63cF1X/heLBSqVCurlMtzJBDs1t9vt4CmbKD8IApxOp38DXjpcxAxzuRzi8Tgyd3e43Lbf75HP51FWB72V7RVgOp1GIpHAYDBAGIavYhyspFgsvg14PB7h+76UtFwupXfz+fyldP6zLYxPp1PMZjPxOU9QZk2LDoyRiMg6nQ50XZeyR6ORZGkoUjgfxZvNpsyRzHa7DU3TZO0LYDRZrVZRVUQ8P7fEr9frsokApVIJrVZLiGo0GmJPT0+o1WoSpypYpQAyGxqBWwpA73ZgmpYC0yVbgtKY1Whkomd0cZAyD2fmPU/6GvU7RuRQpesulpjeP8Db2vDWqoQwkEWUiO9TJoC7nuD+exLmUPtL1A2Ww72Dld7AYWfD92w8Pj6gUCgIOWorplYHiU/v8C3zBc3KD6zmfYTB8QZgsFf7HNSqZaTSX5FKpZHJZOCp8raug2LhJxq/FCFaCcnkZ3z88B7WSHslKQGkExkHGctms9LTqC8MHVXZhqELIYbRUzdmiuBCp1cZHg4HrNdr0RT1SA1yjoON57Wk9qgA6vDcX/8akCDsExeTflM9BBQ3M6UxTp+PAx8QMsqDyDAPvwLkZafxFtCYHcFpBIrivCFngm6/Nn8A15VrZvZUMiAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/afdf52a9ac218bd8e82daaa9cba0fd46/a6d36/image-2.png&quot;
        srcset=&quot;/static/afdf52a9ac218bd8e82daaa9cba0fd46/222b7/image-2.png 163w,
/static/afdf52a9ac218bd8e82daaa9cba0fd46/ff46a/image-2.png 325w,
/static/afdf52a9ac218bd8e82daaa9cba0fd46/a6d36/image-2.png 650w,
/static/afdf52a9ac218bd8e82daaa9cba0fd46/e548f/image-2.png 975w,
/static/afdf52a9ac218bd8e82daaa9cba0fd46/35252/image-2.png 1204w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;내가 부족한 부분을 매꾸면서 얻어내는 성취감과 뿌듯함이 넘쳐났고, 새롭게 알아낸 사실들이 생겨날때마다 더 빨리 공부하고 싶었다. 특히 학창시절에 가장 기억에 남는 활동 중 하나가 &lt;code class=&quot;language-text&quot;&gt;OBB(Oriented Bounding Box) 분리 초평면 이론&lt;/code&gt; 에 관해 며칠간 밤을 세우며 공부하고, 레포트를 작성했을 때다. 내가 만들고 싶은 서비스, 게임을 직접 개발하면서 그때그때 부족한 부분이 무엇인지 깨닫고, 보완이 필요한 부분을 보충하고, 더 공부하고 싶은 내용을 매꾸면서 성장해왔다. &lt;strong&gt;학창시절떄 프로그래밍을 학습했을 때를 떠올리면, 만들고 싶은 서비스를 떠올리고, 일단 부딪힌 후 부족한 부분을 필요할 때 마다 이론을 보충하는 방식으로 학습해왔다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;하지만-대학교-진학-후&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%98%EC%A7%80%EB%A7%8C-%EB%8C%80%ED%95%99%EA%B5%90-%EC%A7%84%ED%95%99-%ED%9B%84&quot; aria-label=&quot;하지만 대학교 진학 후 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;하지만, 대학교 진학 후&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2c30fe5f4d5dbbc9ef99c0025693c6b2/14747/image-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.34969325153374%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAADK0lEQVR42kWR608cVRiH54sf9EMX3KAfNiTG2loSY9FQ0FotsWDBCkSxUrAtu4iwsCzsTfZeFxAqtdTaUgrW3ZnZndkLi2wVEqNNY6KJl+5drPrfPB7AxA9P5px53/P83jkj+eJ5PEoRa6yIJVrCHC0zKFcwy1UsSpUh5Q8sYj0k7z53MMeqe3uz2F8UnI/t0B/9H2lKzjMpmJCL2JQSVrnMiFJhVK1gjQvEelz/m/HEn9jjVVzpfxiL72BVq6KvyvBu4H+YBZJTfoBHzTOdKOBOFLGrJWxqmQkhswkmkw8ZuXaXidAioU+/YO6Wwrz2417AuKiPCfGowCrkoyJccmgV3FoZl/h0t5A6tCKORAmneDeVKBNer7ImK6zcvsF6WmbQ7mJ2LYc3+9defVKcn0jsh9vEMJLzegrn8l08mrhLIXPpRTzJItOpEh/pJS5nfkVJKNyMxdjc0Bj2BIms5pjJPRT13f4Sbr0shinjiBeRzg2cYXbpc/z67/j0B/iT+wRSeXzJAguZX1A1lVVVJrOu4QqHuZXcIiKE4XSBmfUiH6fzhET/tF5AsnzYx/WYhj9VJJDeJ5gRTdkK/kyZxexvJDMaXwmpnlTwR0LEv97Gq/5EUEgCIjyysSPOlZhL/Yz0yskmFpauEl5JcyXxHUv6D3ymf89c7FuupO5zTdsmldaQdZWkELqDfvSNLRbubLK8VWBlWwzxpcp84hvu3b+H1NHdhDdspWvgGJPeQXwzbm6IH3BpLkxwNkgo7GZ1bZloPEY2q+O/5ONONICeXUSJB4ilPqHj4lMMOY6La7mK9FKniVFPLz0fNNA1YqLd0sDxvud4z9bNei6LrKyRTkbJbcZZvXkZb+AdvJFn8M4f4X23kV5nDT1TRqyeemZmm5Gefr6G1jMN9Ay20PquiRc762gdfJzevnrGAi30eV9n2N5DW/uztHXV8Va/kXHvQQbs9Zw4a+C1c0ZO9j9B/9RhrK4jSC2nDRw6eoDWs3V0XDDS3FXL0Q4DL7xxgMY3H8XU9AgHGx/j1dMmTvXVcuLtWg41G2gbeJKXO2s4fMxAY3sNpywmui0N/AvwI2WQovguxQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/2c30fe5f4d5dbbc9ef99c0025693c6b2/a6d36/image-5.png&quot;
        srcset=&quot;/static/2c30fe5f4d5dbbc9ef99c0025693c6b2/222b7/image-5.png 163w,
/static/2c30fe5f4d5dbbc9ef99c0025693c6b2/ff46a/image-5.png 325w,
/static/2c30fe5f4d5dbbc9ef99c0025693c6b2/a6d36/image-5.png 650w,
/static/2c30fe5f4d5dbbc9ef99c0025693c6b2/e548f/image-5.png 975w,
/static/2c30fe5f4d5dbbc9ef99c0025693c6b2/3c492/image-5.png 1300w,
/static/2c30fe5f4d5dbbc9ef99c0025693c6b2/14747/image-5.png 1914w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;프로그래밍의 매력에 빠져 대학교 전공도 컴퓨터공학과를 선택했다. 고민할 필요도 전혀 없었다. 대학도 남들 못지않게 괜찮은 대학교로 입학하게 되었다. 그런데 대학교의 모든 수업 내용들은 연구와 이론 중심으로 구성되었다. 교육을 이수하던 나는 자연스레 &lt;code class=&quot;language-text&quot;&gt;이론 중심의 학습방향&lt;/code&gt;을 갖게 되었고, 이전과 달리 내가 배우는 이론들이 어디에 어떻게 쓰이는지 알 수 없었다. 주입식 교육방식은 내게 깊게 고민하고, 생각할 시간조차 잊게 만들었다. 어느센가 고등학교 시절에 내가 어떻게 프로그래밍을 학습해 왔는지도 완전히 잊고 생활했다. 무엇보다 &lt;strong&gt;맹목적인 수동적 주입식 학습법은 내게 프로그래밍에 대한 흥미를 잃게 만들었다.&lt;/strong&gt; 특히나 1학년때는 정말 방황을 많이했다. 주변에 조언과 큰 도움을 받을 사람도 딱히 없었으니.&lt;/p&gt;
&lt;p&gt;군전역 후 멋쟁이사자처럼 동아리에서 많은 사람들을 만났다. 특히 나와같이 글쓰기를 좋아하시는 뛰어난 선배가 한 분 계셨다. 당시 우아한테크코스에서 학습하신 내용을 기반으로, 내게 어떻게 글쓰기로 프로그래밍을 학습할 수 있는지 많은 도움과 조언을 주셨다. 이후 지금까지 내가 만들어낸 학습법들에 대해 관련해 후술해 보고자 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;전교-1등은-어떻게-공부할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%EA%B5%90-1%EB%93%B1%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B3%B5%EB%B6%80%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;전교 1등은 어떻게 공부할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전교 1등은 어떻게 공부할까?&lt;/h2&gt;
&lt;p&gt;학창시절을 떠올려보면, 전교에서 상위권을 유지하는 친구들은 항상 상위권을 유지한다. 반면 하위권을 유지하는 친구들은 올바른 학습법을 찾고 시도하지 못하여 계속 하위권을 유지하는 경우가 많다. 전교에서 좀 논다는 친구들은 어떠한 학습 전략을 세울까?&lt;/p&gt;
&lt;h3 id=&quot;떠들고-설명하고-가르치기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%96%A0%EB%93%A4%EA%B3%A0-%EC%84%A4%EB%AA%85%ED%95%98%EA%B3%A0-%EA%B0%80%EB%A5%B4%EC%B9%98%EA%B8%B0&quot; aria-label=&quot;떠들고 설명하고 가르치기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;떠들고, 설명하고, 가르치기&lt;/h3&gt;
&lt;p&gt;상위권 친구들 대부분에겐 공통적인 학습법이 있다. 상위권 친구들은 가만히 읽고, 인강을 듣는것으로 학습을 절대 끝내지 않는다. 같이 스터디를 진행하는 친구들이 있다면 최대한 토론하는 것을 지향하고, 혼자서 공부할 때도 조용히 공부하고 문제집을 푸는것을 원하지 않는다. 혼자서 공부할 땐 앞에 인형을 세워두고 누군가에게 가르치듯이 학습중이 내용을 말로 떠드는 방식으로 공부한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;누군가에게 가르치듯이 설명하고, 시끄럽게 떠드는 학습 방식은 이미 뇌과학적으로 높은 학습효과를 보인다는 것이 이미 증명되었다.&lt;/strong&gt; 반면 조용하게 인강과 책을 읽고, 문제를 대충 풀어보는 수동적인 방식은 매우 낮은 학습효과를 보인다는 것 또한 이미 증명된 사실이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ntl-learning-pyramid&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ntl-learning-pyramid&quot; aria-label=&quot;ntl learning pyramid permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;NTL Learning Pyramid&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3c1f24bf4e3c899d258ab1e93712bb44/c211c/image-7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.25766871165644%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACMElEQVR42o2STU8TURSGJxpiAqL4tXBhAomAqAsX6kZ/gya6NMG1iTH+Ev+Be0UFgSBgZEGABSbSgGK1fLS0EOm0nd5pp9POdGYez0wLIRaVkzw5k3vufec99x6NPyIIgogwdF0nkUiwvZ1hbT0prLOxsRHhOM7+/oOh8Y+o1WoYRoFkKkMyMY9RyFIoGEIBz/MOPdMiaFlW5Cyfz2OqInbVpe4YsHVPiuPNXf5fTWiHuQpbSqfT5HM6JasOv54TxPsJErcInE2iJgP/aIK2baOUkvZ0xByo17B8AtbuwLdzkHywd9tHE/R9n5yepVKVO3LFzWp3w118gODnDYKVTgL9RVPO+79g3XUpW7Z8VcXNXXElW36ch/gZyeLw+0lYbZNWZg5tXQsdVSoVqtVqkwqZdJbPa0PML95k7stDZpceC4PMxgaZiz1iYfE28yvPMF1aRkcLRXZ2dkilUmymtnDKipndGu3DeY6N1Lg4YXN5yorombTonqzQMeaiDSmexsqNazpwnVroLmRbRI18jlzN58anMm0jirNjip4PJXqnSvRNN3K/cGFccWrUpG1Y8SrTGHCvKaqFA6pMk91slsB1eBKror0xIrHTo4reSZNr0yYD02EucVVyWOuSWvtIUVybbFr+vtPoUZy6h23keJkocfxd+PcGHe8VlyZMrn80uTIliFif5LDWKXSJsPa2yP0Fq/WVfa9OXDnEih4rymP5AF/N1rU9wvUlOWM3J+g3Y4Cg95oiEkAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/3c1f24bf4e3c899d258ab1e93712bb44/a6d36/image-7.png&quot;
        srcset=&quot;/static/3c1f24bf4e3c899d258ab1e93712bb44/222b7/image-7.png 163w,
/static/3c1f24bf4e3c899d258ab1e93712bb44/ff46a/image-7.png 325w,
/static/3c1f24bf4e3c899d258ab1e93712bb44/a6d36/image-7.png 650w,
/static/3c1f24bf4e3c899d258ab1e93712bb44/e548f/image-7.png 975w,
/static/3c1f24bf4e3c899d258ab1e93712bb44/3c492/image-7.png 1300w,
/static/3c1f24bf4e3c899d258ab1e93712bb44/c211c/image-7.png 1502w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;학습 피라미드(Learning Pyramid)&lt;/code&gt; 란 이 학습법을 뒷받침해주는 증명된 사실이다. 이는 미국 MIT 대학 사회심리학자 레윈(Lewin) 이 미국행동과학연구소 NTL(National Training Laboratories) 에서 발표한 내용이다. 우리가 외부에서 습득한 지식들이 두뇌에 기억되는 비율을 학습 방식(활동별) 에 따라 정리한 사실이다. 이 학습 파리미드는 다양한 학습법으로 학습을 진행한 후, 24시간 뒤 우리 두뇌에 지식이 체화되어 휘발되지 않고 남아있는 비율 수치를 표현한 것이다.&lt;/p&gt;
&lt;h4&gt;수동적 학습법(Passive Teaching Method)&lt;/h4&gt;
&lt;p&gt;학습법은 크게 2가지로 구분된다. &lt;code class=&quot;language-text&quot;&gt;수동적 학습법(Passive Teaching Method)&lt;/code&gt; 에는 수업 듣기(Lecture), 읽기(Reading), 듣고 보기(Audio Visual), 시연하기(Demonstration) 가 해당한다. 특히 수업 듣기(Lecture) 방식의 학습 기억률은 5% 라는 수치에 그친다. 가장 높은 기억률을 보인 시연하기(Demonstration) 도 30% 에 그치지 못한다.&lt;/p&gt;
&lt;h4&gt;능동적인 학습법(Active Learning Method)&lt;/h4&gt;
&lt;p&gt;반면 &lt;code class=&quot;language-text&quot;&gt;능동적인 학습법(Active Learning Method)&lt;/code&gt; 을 살펴보자. 집단토의(Group Discussion) 은 50%, 연습하기(Practing) 은 75%, 가르치기(Teaching Others) 는 90% 라는 높은 기억률을 보여준다.&lt;/p&gt;
&lt;h3 id=&quot;차이는-무엇인가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%A8%EC%9D%B4%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot; aria-label=&quot;차이는 무엇인가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;차이는 무엇인가&lt;/h3&gt;
&lt;p&gt;이러한 두 학습방식의 집단은 어떤 방식에서 가장 큰 차이가 있을까? 가령 수업 듣기(Lecture) 방식과 가르치기(Teaching) 방식을 비교해보자. 단순히 수업을 듣는것은 누구나 편한 마음으로 참여 가능하며, 자신이 무엇이 부족한지 보충할 시간이 마땅히 주어지지 않는다. 자신이 가지고 있는 생각, 의문이 드는점 모든것을 머리 밖으로 &lt;code class=&quot;language-text&quot;&gt;출력(Output)&lt;/code&gt; 할 환경이 마땅치않다. 그저 가만히 수업을 듣고, 간혹 멍을 때리고 그게 끝이다.&lt;/p&gt;
&lt;p&gt;가르치기(Teaching Others) 방식을 생각해보자. 흔히 다른 사람들을 가르치기 위해선 그 지식을 잘 이해하고 있는것이 선수로 가정되어야 한다. 또한 타인에게 내가 알고있는 지식을 설명하며 머리 밖으로 지식을 &lt;code class=&quot;language-text&quot;&gt;출력(Output)&lt;/code&gt; 하는 과정이 동반되며, 내가 무엇을 잘 인지하고 모르고 있는지 알 수 있게 된다. 즉, &lt;code class=&quot;language-text&quot;&gt;메타인지&lt;/code&gt; 가 활성화되어, 내가 부족한 부분을 매꿀 수 있는 기회가 주어진다. 부족한 부분을 보완하며, 흩어진 모호한 생각들을 정교화시킬 수 있고, 단순 수업 듣기에 이어서 여러번 해당 지식을 반복하여 학습하기 때문에 기억의 장기화에도 큰 도움이 된다.&lt;/p&gt;
&lt;h3 id=&quot;지식을-온전히-체화시키는-것은-고통스럽다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%80%EC%8B%9D%EC%9D%84-%EC%98%A8%EC%A0%84%ED%9E%88-%EC%B2%B4%ED%99%94%EC%8B%9C%ED%82%A4%EB%8A%94-%EA%B2%83%EC%9D%80-%EA%B3%A0%ED%86%B5%EC%8A%A4%EB%9F%BD%EB%8B%A4&quot; aria-label=&quot;지식을 온전히 체화시키는 것은 고통스럽다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;지식을 온전히 체화시키는 것은 고통스럽다.&lt;/h3&gt;
&lt;p&gt;하나의 지식에 대해 높은 이해도를 갖기 위해, 완벽히 내것으로 체화시키기 위한 학습 과정은 고통스러워야 한다. 학습자는 스스로 고민할 기회를 가질수록 높은 학습 효과율을 보인다. 누군가 내게 지식을 수동적으로 주입시키는 학습 방식은 그다지 고통스럽지 않다. &lt;strong&gt;능동적인 학습방식과 환경이야말로 학습자 본인의 많은 고민, 반복과 복습, 정확한 이해도가 요구되기 떄문에 다소 시간이 오래걸리고 고통스럽다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;지식을-머리-밖으로-내보내기output-위한-학습수단&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%80%EC%8B%9D%EC%9D%84-%EB%A8%B8%EB%A6%AC-%EB%B0%96%EC%9C%BC%EB%A1%9C-%EB%82%B4%EB%B3%B4%EB%82%B4%EA%B8%B0output-%EC%9C%84%ED%95%9C-%ED%95%99%EC%8A%B5%EC%88%98%EB%8B%A8&quot; aria-label=&quot;지식을 머리 밖으로 내보내기output 위한 학습수단 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;지식을 머리 밖으로 내보내기(Output) 위한 학습수단&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e421a6bf748d5749fbfd56d68f5bdd28/0a867/image-8.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.03067484662577%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACYUlEQVR42n1TXW/TMBTd/38A8YSEeOUNCXgCTUJiGptgUI22U1m7tE3aNJ9243w0iePYh5u0ZVQCruTYzk3OvT7n+AwUgvkIVzPEmwW47yB25whoL2Kf1gtEawvZNupzPFwjZQH+FWdHQPt+gNX0Fr4zhbecwP45wMPwCqvZDzi0jlwL9uQbvMUd3MUYbVOjlRVao6EaiaZbt6oDNCcVjNnvt6MrfH73DF8/vMBue4vl99f48v45LOcCs+lHfHr7FJeXr2DFFm7OX+LyzRO40+s9YAditEZdFVC6RVtVKByLKHgAc2/Q7DwE/gSWfQ0m1nD4EnPvHoytUJQC3HsA30xRZfFph2s6Vp4LyCDohzECymwQqhjzZg1Ox7PpWJzGfznsOmyIhzB2ES2WUEWBOnUgGYE1LvLagStLOLVH3CkkUkGqjjfAduaw5iPsdvkjoG5b+J26zKU5gUh1n1QxgyYAA0FFMxSmAtNbVLqAbGtANpjPhliSkC1hPAISb55vY0O2KcsUKQGKlKigLjTnB7ESeiag3pDpHE2XowJBtKAmVpBSngK65L2QPFYcWk/TFonQ0HlGm+TAUPfTvltFgCQfRB7Rt3FvmRPAohAIAqcX5RhVZbAVBlmYoYk5TFkRpoQhNzRyh7s7hsEgxHAYYzzuQM1R5b3SeZ4QuVlfTamGCikSq0FWkLJhgdBmCJcxIocToMR4FOP8fImLixUmE/Yn4D7KMu+5TARDFLngPEBEylfkNZEy8lwOkfHOub0z6rqlnKL/FBU3xyObk1vSddfNmox+HL3xjf79/m9xuGD4BYA031Mf9YeLAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e421a6bf748d5749fbfd56d68f5bdd28/a6d36/image-8.png&quot;
        srcset=&quot;/static/e421a6bf748d5749fbfd56d68f5bdd28/222b7/image-8.png 163w,
/static/e421a6bf748d5749fbfd56d68f5bdd28/ff46a/image-8.png 325w,
/static/e421a6bf748d5749fbfd56d68f5bdd28/a6d36/image-8.png 650w,
/static/e421a6bf748d5749fbfd56d68f5bdd28/e548f/image-8.png 975w,
/static/e421a6bf748d5749fbfd56d68f5bdd28/0a867/image-8.png 986w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;지식을 체화시키기 위해선, 그에 알맞은 학습수단이 필요하다. 나는 내 후배들에게 학습방식을 조언할 때 항상 블로깅을 가장 먼저 추천한다. 앞선 NTL Learning Pyramid 에서 선술했듯, 누군가에게 가르치고, 설명하는 과정속에선 &lt;code class=&quot;language-text&quot;&gt;메타인지&lt;/code&gt;가 활성화되며 큰 학습효과를 보인다고 했다.&lt;/p&gt;
&lt;h3 id=&quot;블로깅&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%94%EB%A1%9C%EA%B9%85&quot; aria-label=&quot;블로깅 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;블로깅&lt;/h3&gt;
&lt;p&gt;블로깅은 시간, 장소에 구애받지 않고 지식을 머리밖으로 &lt;code class=&quot;language-text&quot;&gt;인출(Output)&lt;/code&gt; 하기 위한 최고의 학습수단이다. 누군가와 집단 토론을 하고, 서로에게 가르치는 학습을 원할떄 항상 아쉬울점은, 사람들과 시간과 장소를 협의하고 만나야한다는 점이다. 이렇게 매번 만남을 가지는 것을 사실상 쉽지 않다. 반면 블로깅은 자신의 상황에 알맞게 언제든지 글쓰기를 시작할 수 있다.&lt;/p&gt;
&lt;p&gt;글을 작성하면서 본인 스스로 남을 위해 설명하듯 글을 작성하다보면, 생각이 &lt;code class=&quot;language-text&quot;&gt;정교화(elaboration)&lt;/code&gt; 되고, 이곳저곳 흩어진 생각을 한 곳으로 모으며 지식을 글로 &lt;code class=&quot;language-text&quot;&gt;설명(explain)&lt;/code&gt; 할 수 있다. 타인에게 설명을 하듯 글쓰기를 시작하면, 자연스레 지식과 생각들이 정리된다. 또한 글을 적다가 내가 잘 모르는 부분이 있다면 반드시 설명을 하는데에 막힐 수 밖에 없다. 이 과정에서 메타인지가 활성화된다. 내가 부족한 부분을 글을 작성하며 정확히 알 수 있게된다. &lt;strong&gt;그를 보충하기 위한 추가 학습 키워드가 생기며, 필요에 의해 능동적으로 학습하고, 배우고, 고민하고, 보충할 수 있는 기회가 주어진다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;또한 블로깅은 기억의 장기화에 매우 큰 효과를 보인다. 인간은 망각의 동물이다. 모든것을 기억할 수 없으며, 설령 높은 이해도를 갖고 있던 지식들도 오랜 시간이 지났을 때 잊기 마련이다. 이때 블로깅은 장점을 보인다. 그 당시 내가 갖고 있던 정교화된 생각들을 빠르게 상기할 수 있다. 휘발된 기억을 빠르게 되찾을 수 있다. 이렇게 정교화된 생각들을 내가 필요할 때 마다 계속 꺼내볼 수 있다는 점도 매우 큰 장점이다.&lt;/p&gt;
&lt;p&gt;잘 문서화된 블로그일수록, 방문자수는 점차 많아지기 마련이다. 방문자는 문서화된 포스트를 읽고, 댓글을 남겨주며 작성자와 소통할 수 있다. 작성자는 글을 읽으며 유익한 정보를 얻어가고, 질문하고, 오류를 제보하며 작성자와 소통할 수 있다. 이로써 방문자와 작성자 사이에 자그마한 토론이 활성화된다. 내가 기록한 내용에 이어 더 유익한 지식을 제공받을 수 있고, 잘못 이해한 지식을 제보받고, 도움을 받았다는 의견을 받았을 때 큰 보람을 느낄 수 있다. 이 보람은 또 다음 학습의 큰 원동력이 된다. &lt;strong&gt;결국 열심히 작성한 글쓰기가 선순환이 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;무엇보다 &lt;strong&gt;블로그를 운영하면 학습수단은 몰론, 본인의 브랜딩 효과가 생기며, 포토폴리오를 쌓고, 문서화 능력을 기를 수 있다.&lt;/strong&gt; 현업에선 가장 중요한 것 중 하나가 문서화 능력이다. 평소 블로깅을 통한 문서화 능력을 갖추면 자신만의 차별점이 생기게된다.&lt;/p&gt;
&lt;h3 id=&quot;세컨드-브레인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B8%EC%BB%A8%EB%93%9C-%EB%B8%8C%EB%A0%88%EC%9D%B8&quot; aria-label=&quot;세컨드 브레인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;세컨드 브레인&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cb526d9aab7db85917419a11df28c599/7a18f/image-9.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.23312883435584%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB90lEQVR42o2U6W7TUBCF8zYk8Ron3vfUceLYdR1TaChBbKUsKgIJXoAH4JkPM9cEpWri8mNk+/rq8zln5nowHFvoq7HsQFJcKkfcP7Z/cGxxJNkCoOo+VSBAvMalaAGejExR/w3kkkmVrHiQVA/qJMTMWmEyzTCmdWOWCtXDI9DBQ4suqYhIWQTTWcJycgKcETAncCwUrqpXmNmLf25OAvmlqocC4EUXsL01QQvYfkVVQp+mmDmklBQqmg+Z1LPSQ/v3gPxSm0RwgnME6SWSxRZh0iLOrpAVO7pu6QMrKGpA+zq1XCcVcviKFpKSOdzwAulyhyR/ibL9jPryK9bNR4TzZ/DjDSyvEDCN8j20fQ/IFnQjwdRkyzWy9Rvk5Vvsbn7h9ttvFPUHUn2N6OwKETkQAv5OQY9Cn6ApgmSDvHqPqr1D++Inmu13en4nYH7ciny5gTxWJxVKBJxME2ik0gtrsvcc50/vsNn+wHrzBUXzCfP8Gm5QESgWAnhWe7vMjeFsZM6HwHn5GovyBqv6Vtj3ogYGRTIcd3tltafLh0dNM2KatRzRvIVPecZk1Yta0RDTXdLQ+yKeofTIYI8kS3xVJ6BhZjQmOTUpE3A3bMQEdFbDB0Pde/T4LO8Hl8NnxTrl2/0o3KOwXuA+0311YFsUP5/6OfwBE+zm4DQ4xHEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/cb526d9aab7db85917419a11df28c599/a6d36/image-9.png&quot;
        srcset=&quot;/static/cb526d9aab7db85917419a11df28c599/222b7/image-9.png 163w,
/static/cb526d9aab7db85917419a11df28c599/ff46a/image-9.png 325w,
/static/cb526d9aab7db85917419a11df28c599/a6d36/image-9.png 650w,
/static/cb526d9aab7db85917419a11df28c599/e548f/image-9.png 975w,
/static/cb526d9aab7db85917419a11df28c599/7a18f/image-9.png 1284w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;댜음의 학습수단으로 세컨드 브레인을 꼭 추천하고 싶다. 나는 블로깅이 메인 학습수단이라면, 세컨드 브레인을 서브로 활용하고 있다. 나도 아직 더 깊게 연구해봐야 할 수단이지만, 현재까진 구멍난 이론들을 정제하기 위한 최적의 공간으로 활용하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;세컨드 브레인이란 선정된 학습 키워드를 차근차근 정제하여 쌓아가고, 쌓은 지식간의 연결점을 찾아서 새로운 연결고리를 만드는 일종의 지식 저장소의 개념이다.&lt;/strong&gt; 프로그래밍을 학습하다보면, 하나의 학습 키워드를 선정하다보면 수많은 추가 학습 키워드들이 쏟아져 나왔던 경험을 한번쯤 경험해봤을 것이다. 제한된, 소중한 시간이 하나의 지식을 온전히 내 것으로 체화시키는데만 해도 큰 시간이 소요되며, 하나의 메인 키워드에 몰입하여 이해하는데에 지치기 마련이다.&lt;/p&gt;
&lt;p&gt;세컨드 브레인은 하나의 학습 키워드에 실려오는 추가적인 외부 학습 키워드를 선별적으로 체화하기에 매우 효과적인 학습수단이다. 우선 각 학습 키워드간에 우선순위를 정하여, 높은 우선순위를 갖는 학습 키워드는 블로깅을 통해 인출(Output) 하는 방식으로 학습하는 것이다. 반면 우선순위가 낮은 학습 키워드는 이 지식 저장소내에 생각을 정제하는 방식이다.&lt;/p&gt;
&lt;p&gt;세컨드 브레인을 원활히 활용한다면 쏟아져 나오는 학습 키워드를 나만의 지식 저장소에 영구적으로 정제할 수 있다. 또한 블로깅을 통해 학습하지 못한 주제들을 아쉽게 버리는 것이 아닌, 간략히 글을 발행하여 짧은 시간내로 지식을 습득할 수 있다. 이 과정에서 각 학습 키워드간의 연결 고리를 찾아낼 수도 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;의미있는-경험을-기록하기-위해&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%98%EB%AF%B8%EC%9E%88%EB%8A%94-%EA%B2%BD%ED%97%98%EC%9D%84-%EA%B8%B0%EB%A1%9D%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%B4&quot; aria-label=&quot;의미있는 경험을 기록하기 위해 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;의미있는 경험을 기록하기 위해&lt;/h2&gt;
&lt;p&gt;그럼에도 내가 지금까지 항상 부족했다고 느낀 부분은, 바로 다양한 경험을 쌓는 것이다. 다양한 경험이 쌓여야만 내가 부족한 부분을 매꾸며 성장할 기회가 열리고, 블로깅시 더 의미있는 기록물이 쌓일 것이다. 경험이라하면, 이론이 아닌 실무 경험, 사이트 프로젝트다.&lt;/p&gt;
&lt;h3 id=&quot;부족한-것을-매꿀-수-있는-환경에-속하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%80%EC%A1%B1%ED%95%9C-%EA%B2%83%EC%9D%84-%EB%A7%A4%EA%BF%80-%EC%88%98-%EC%9E%88%EB%8A%94-%ED%99%98%EA%B2%BD%EC%97%90-%EC%86%8D%ED%95%98%EA%B8%B0&quot; aria-label=&quot;부족한 것을 매꿀 수 있는 환경에 속하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;부족한 것을 매꿀 수 있는 환경에 속하기&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;능동적인 학습을 위해선 학습자에게 고민할 기회가 주어져야하며, 필요에 의해 학습할 수 있어야 한다. 그래야 학습에 능동적으로, 더 재밌게 참여할 수 있다. 또한 부족한 부분을 매꿀 환경속에 있어야한다.&lt;/strong&gt; 이런 환경속에 속해있기 위해선 최대한 많은 실무 경험과 프로젝트를 경험해야한다. 블로깅을 통해 이론을 인출(Output) 하는 방식만으론 성장에 한계가 있다. 내가 학습한 이론들이 정확히 어디에, 어떻게 쓰이는지 알 수 있어야하고, 그래야 내가 부족한 부분을 매꿀 수 있는 기회가 주어진다. 많은 트러블슈팅을 만나야 더 학습할 키워드들이 생길것이다.&lt;/p&gt;
&lt;p&gt;또한 개발자의 가장 중요한 자질은 &lt;code class=&quot;language-text&quot;&gt;문제해결력&lt;/code&gt; 이다. 특정 상황에서 마추지는 수많은 장애와 버그, 트러블슈팅에 대해 유연하게 대응할 수 있어야한다. 아무리 뛰어난 이론과 지식을 공부한 학자이라고 한들, 그 지식이 실무 상황에서 쓰이지 못한다면 어떻게 사용해야할지, 언제 어떻게 쓰이는것인지 알기 힘들다. 이 때문에 &lt;strong&gt;최대한 많은 트러블슈팅을 만나기위한 환경에 속해 있어야한다.&lt;/strong&gt; 본인이 지금까지 이론만을 고집하고, 안일하게 학습해왔고, 책과 인강으로만 가만히 학습해왔다면 더 올바른 학습방식에 대해 다시 고민해볼 필요가 있다.&lt;/p&gt;
&lt;p&gt;무엇보다 문제를 해결해나가는 과정은 즐겁기도하다. 내가 이렇게 어려운 문제를 해결했다니. 난 이만큼이나 성장했구나! 라는 성취감을 만끽할 수 있다. 또한 &lt;strong&gt;문제를 해결해가는 과정속엔 학습자에게 깊게 고민하고, 생각하고, 개선해나가는 능동적인 학습이 동반된다.&lt;/strong&gt; 이론과 경험은 동반되어야 함을 명심하자. 현직자라면 현직에서 더 많은 트러블슈팅을 만나길 고대하고, 현직자가 아니라고 한들 유사 실무 경험, 양질의 사이드 프로젝트를 경험할 환경에 속해있도록 하자.&lt;/p&gt;
&lt;p&gt;유사 실무 경험이라하면 인지도 높은 교육기관, 부트캠프가 적합하다. 훌륭한 교육기관, 부트캠프를 보며 항상 느끼지만, 실무에서 충분히 발생할 법한 트러블슈팅과 경험을 쌓게 해주어, 현업에 참여할 수 있을만큼 학습자를 훈련시킨다.&lt;/p&gt;
&lt;h3 id=&quot;why-질문해라-왜-써야하지-왜-필요하지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-%EC%A7%88%EB%AC%B8%ED%95%B4%EB%9D%BC-%EC%99%9C-%EC%8D%A8%EC%95%BC%ED%95%98%EC%A7%80-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%98%EC%A7%80&quot; aria-label=&quot;why 질문해라 왜 써야하지 왜 필요하지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why? 질문해라. 왜 써야하지? 왜 필요하지?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;왜? 라는 질문을 던지며 학습하는 습관은 학습자 본인에게 더 고민할 기회, 생각할 기회를 만들어준다.&lt;/strong&gt; 맹목적인 주입식 학습은 &quot;왜&quot; 라는 질문을 던질 기회가 없다. 이 기술을 왜 써야하는지, 더 효율적인 방법은 없는지 따져볼 때 자신만의 주관, 논리과 명확해진다.&lt;/p&gt;
&lt;p&gt;가령 API 성능 개선을 고민하는 상황을 생각해보자. API 성능 개선을 위해 캐싱 전략을 떠올려볼 수 있다. 여기서 &quot;왜 캐싱을 도입해야할까?&quot; 라는 질문을 넣어보면 더 좋은 학습 경험이 된다. 캐싱외에 더 효과적인 성능 개선 전략은 없을지 고민하다 인덱스를 발견할 수 있다. 인덱스를 적용하고 싶다는 생각이 들때쯤 &quot;과연 모든 컬럼에 인덱스를 적용하는게 올바를까?&quot; 라는 질문은 더 나은 고민거리가 된다. 커버링 인덱스를 발견할 수 있고, 인덱스외에 더 기법이 없을지 또 고민할 수 있다. 이렇듯 &lt;strong&gt;&quot;Why?&quot;&lt;/strong&gt; 라는 질문 습관은 더 나은 학습전략이 되며, 더 효과적인 소프트웨어 품질 향상에 기여할 수 있게된다.&lt;/p&gt;
&lt;h3 id=&quot;하지만-블로깅과-세컨드-브레인을-더-효과적으로-활용하기-위해&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%98%EC%A7%80%EB%A7%8C-%EB%B8%94%EB%A1%9C%EA%B9%85%EA%B3%BC-%EC%84%B8%EC%BB%A8%EB%93%9C-%EB%B8%8C%EB%A0%88%EC%9D%B8%EC%9D%84-%EB%8D%94-%ED%9A%A8%EA%B3%BC%EC%A0%81%EC%9C%BC%EB%A1%9C-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%B4&quot; aria-label=&quot;하지만 블로깅과 세컨드 브레인을 더 효과적으로 활용하기 위해 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;하지만, 블로깅과 세컨드 브레인을 더 효과적으로 활용하기 위해&lt;/h3&gt;
&lt;p&gt;아직 혼자서 결론을 내리지 못해서 못적고 있지만, 내가 가장 해결하고 싶은 학습법 중 하나가 블로깅과 세컨드 브레인의 적절한 비중이다. 블로깅은 내 생각을 인출하여 정교화, 설명하기에 매우 큰 도움이 되지만, 시간이 오래걸린다는 단점이 있다. 이 떄문에 세컨드 브레인을 몇달전부터 시작했는데, 아직 더 연구해봐야할 필요가 있다. 세컨드 브레인에 정리한 것들이 생각보다 잘 정교화되지 않는다.&lt;/p&gt;
&lt;p&gt;향후 찾아낸 나만의 더욱 효과적인 학습법은 이곳에 추가하거나, 또는 새로운 글로 발행하도록 하겠다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;글을 작성하는데는 오랜 시간이 걸린다. 이 회고를 작성하는데도 생각보다 많은 시간이 걸렸고. 그럼에도 난 블로깅을 통한 능동적인 학습법을 할 수 있다고 굳게 믿기에, 꾸준히 글을 작성할 것이다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[2024년 카카오테크 코스 최종 합격까지, 상반기 회고]]></title><description><![CDATA[정말 열심히, 후회없이 살아왔던 2024년 상반기였다. 벌써 상반기가 끝나고 하반기의 시작이라니. 이번 7월…]]></description><link>https://haon.site/회고/2024-with-kakaotech/</link><guid isPermaLink="false">https://haon.site/회고/2024-with-kakaotech/</guid><pubDate>Wed, 26 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;정말 열심히, 후회없이 살아왔던 2024년 상반기였다. 벌써 상반기가 끝나고 하반기의 시작이라니. 이번 7월 1일전까지 잠시 여유가 생겨서 상반기 회고를 적어볼까한다. 이번 상반기는 깊이있고, 의미있는 성장을 많이 이룬 것 같다. 나 혼자서 많은 불안감과 불확실성을 이겨내기 위해 힘든 여정들이 있었다. 그 결과 매우 좋은 성과들이 생겼고 🙂&lt;/p&gt;
&lt;h2 id=&quot;우아한테크코스-탈락-나에게-찾아온-불안함&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-%ED%83%88%EB%9D%BD-%EB%82%98%EC%97%90%EA%B2%8C-%EC%B0%BE%EC%95%84%EC%98%A8-%EB%B6%88%EC%95%88%ED%95%A8&quot; aria-label=&quot;우아한테크코스 탈락 나에게 찾아온 불안함 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;우아한테크코스 탈락, 나에게 찾아온 불안함&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4b042154eea71c60a66e95d0f4194bb0/1320e/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 77.91411042944786%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAADMklEQVR42p1SWU+TQRSdFw1bVygtUj5aWqosFiJFNCSiCUbDAzH+BhSaaEjoBu4vhgcTSkuLPGhCgggx/AF5JiImBKLIvoR938OSAD3OHfzQ+OiXnNw7M3fOvefMx759/QJ3pRNPq13weavg9Xjg8Xjh8dVwPIOn5iW8NS9E7vY+gctTzeETscrtRZXLDTe/86D8Idra28Bam99Bc54hWaeETquAVqOFRqtHnEoPZbwEdWIa1DoTFJokgThlAhTqeKh4DcWoWAVilQowxlBeUQ7W0dYCs0GNLFsqbBYTLBYbzJZsOApuIcdRBHt+Ma7kF8FxleNaMbLtDthsGZBMVhiMEvQXkmGUJETHKVDFlbFPH1sg6RTItBKhlRdnI9WcCZ+PS/W9QoXTxS14DrerGo8eu1BWVo7Se/ehT05BgiEJuiROmGrGuZg4VHL5rKO9FVZjAvLsl5CXY0dhwXWU3i7FnZslKCwsQW7+XdhzbyCdNzJbLsLEFUhp6YJE4mvJmgETnzhaoeQTcsIPze+hjmKwGBM5DLCnpcKReRkGg5VPYIMhJQsJehM08aeexSg1/LIKsSoNYtQ6RKu0iOW+Cw+dTrCfP74jUPcGTaEAGhuCCDeEEAyGEQ41ojH0Fo3hJjTwPBBsgD8QRF19Pfz1AZ6Hec7Xfj/PA3hdW4vPnZ1g+P1FIpE/MYL//tj+/j6GhoYwNjaG0dFRkVMkzM3NYXd3F2tra1hfXxf55uYmtra2BCgnrK6uYmNjAycnJ6eEAwMD6O3tRX9/P/r6+kRO6O7uFo2WlpawuLgoLi4vL2NlZQXz8/MCCwsLojFFQXhwcICJiQkxAU1CxURAl2US6k77FAlUNzs7KwjlJlR7fHwMtre3h8HBQYyPj2NqakoUELlMsr29LQgItCc3JgJZMq0pkv9sZ2cHPT09QipJJomTk5MizszMCE9HRkbO9mSvSSblw8PDYk2k4lEODw/FBk1JXtJlIqYmtJYt+BekhEDnJJlw5iFN19XVJYinp6fFZCSfvCVp8iMQkeyh/Dh0Tms6I/vY0dGRkEMeEhGZLT+ObDoVk0TZS8qJiCL9Pn//x78ANF2hbiJxoIsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/4b042154eea71c60a66e95d0f4194bb0/a6d36/image-1.png&quot;
        srcset=&quot;/static/4b042154eea71c60a66e95d0f4194bb0/222b7/image-1.png 163w,
/static/4b042154eea71c60a66e95d0f4194bb0/ff46a/image-1.png 325w,
/static/4b042154eea71c60a66e95d0f4194bb0/a6d36/image-1.png 650w,
/static/4b042154eea71c60a66e95d0f4194bb0/e548f/image-1.png 975w,
/static/4b042154eea71c60a66e95d0f4194bb0/1320e/image-1.png 1292w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;작년 하반기 무렵, 우아한테크코스 교육과정에 꼭 참여하고 싶은 절실한 마음으로 지원했다. 결과는 아쉽게도 불합격이었다. 평소 양질의 교육코스 필요성이 절실하게 느껴져 지원했지만 탈락 통보를 받았다. 당시 대학교 3학년이 끝날 무렵이었고 4학년을 앞두고 있었기 때문에 더욱 간절하게 느껴졌다. 탈락을 통보받았을시 4학년을 앞둔 나에게 남는것이 그 어떤것도 없었기 떄문이다.&lt;/p&gt;
&lt;p&gt;항상 프로그래밍은 즐거웠지만, 나에겐 우테코가 꼭 필요했던 이유가 있었다. 우선 주변에 몰입하여 끝까지 성장할 동료들이 전혀 존재하지 않았다. 개발자를 꿈꾸는 사람들은 정말 많았지만 다들 그리 열정적이지도 않았으며, 사실 프로그래밍 역량 차이가 심하여 협업을 하기에도 양측 모두가 어려움이 있었다. 또한 불확실성 속에서 내가 작성한 코드, 이끌어줄 코치님들이 존재하지 않았기 떄문에 불확실성에서 학습을 이어가는데 한계를 느꼈다.&lt;/p&gt;
&lt;h3 id=&quot;사실-나는-불합격한-이유을-정말-잘-알고있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%82%AC%EC%8B%A4-%EB%82%98%EB%8A%94-%EB%B6%88%ED%95%A9%EA%B2%A9%ED%95%9C-%EC%9D%B4%EC%9C%A0%EC%9D%84-%EC%A0%95%EB%A7%90-%EC%9E%98-%EC%95%8C%EA%B3%A0%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;사실 나는 불합격한 이유을 정말 잘 알고있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;사실, 나는 불합격한 이유을 정말 잘 알고있다.&lt;/h3&gt;
&lt;p&gt;내가 교육과정에 불합격 통보를 받은 원인을 정말 잘 알고있다. 이유 자체를 몰랐다면 억울했겠지만. 우선 내가 작성한 글엔 진심이 묻어나지 않았다. 평소처럼 글쓰기를 작성하면 충분히 합격했겠지만, 합격하고 싶은 마음에 나 자신을 포장한 모습을 보여줬다. &lt;strong&gt;역시나 모든 글에는 진심이 담겨있어야만 독자에게 감동과 내가 어떤 사람인지를 명확히 전달할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;무엇보다 내가 탈락한 근본적인 원인은 자만심이다. 내가 당시 작성한 글에는 교육이 아닌, 취업을 위한 맹목적인, 자만심에 가득찬 모습을 보여줬다. 이는 교육코스에서 뽑고 싶어하는 사람에 절대 부합하지 않는다. &lt;strong&gt;교육이 필요하다면 왜 해당 코스가 필요한지, 타인과 다르게 어떠한 노력을 해왔는지 진심을 담아 작성해야한다.&lt;/strong&gt; 나는 두 번 다시 이떄처럼 글쓰기를 작성하지 않을 것이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;모든 글에는 진심이 감겨야한다. 또한 자만심, 오만함에 가득찬 글은 독자로 하여금 그 어떠한 감동과 진솔함을 느끼게 할 수 없다. 앞으로도 이 점을 반드시 명시하고 글쓰기를 이어가자 🙂&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;불안감-dmn-으로-가득했던-나의-일상생활&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%88%EC%95%88%EA%B0%90-dmn-%EC%9C%BC%EB%A1%9C-%EA%B0%80%EB%93%9D%ED%96%88%EB%8D%98-%EB%82%98%EC%9D%98-%EC%9D%BC%EC%83%81%EC%83%9D%ED%99%9C&quot; aria-label=&quot;불안감 dmn 으로 가득했던 나의 일상생활 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;불안감, DMN 으로 가득했던 나의 일상생활&lt;/h2&gt;
&lt;p&gt;나는 이제 무엇을 해야할까. 상실감이 너무 컸다. 4학년이 된 나는 불안함만 커져갔고 손에 아무것도 잡히지 않았다. 부끄럽게도 큰 목표를 이루지못하니 며칠간 아무것도 하기 싫었다. 그렇다고 새로운 해를 맞이하며 아무것도 하지않고 의미없는 생활을 보낼 수는 없으니. 2023년을 끝 마무리하며 회고록을 작성하고, 자기계발 책을 많이 읽었다. 특히 &lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000001767887&quot;&gt;타이탄의 도구들&lt;/a&gt; 이라는 명서를 발견했는데, 이 책은 나에게 큰 용기와 도전정신을 다시금 불러일으키게 했다. 며칠간 무기력했던 내게 다시 새로운 길을 걸을 수 있는 길라잡이가 되었다.&lt;/p&gt;
&lt;p&gt;지금 내게 들이닥친 불안함은 어떻게 없앨 수 있을지, 성공한 사람들은 큰 실패를 겪었을 떄 어떤 마음가짐을 갖고 극복했는지가 궁금했다. 여기서 느낀점은, 어쩌만 모두가 아는 사실이지만, &lt;strong&gt;성공한 사람들 모두가 많은 실패를 겪었고, 타인에게 보이는 찬란한 하이라이트를 만들어내기 위해 수많은 노력과 수고가 있었다는 것이다.&lt;/strong&gt; 모두에겐 부족한 점이 가득하다. 심지어 전세계적으로 뛰어난 인물 모두에겐 자신에게 부족한 면이 가득하고, 나는 타인이 하이라이트만을 바라봤던 것이다.&lt;/p&gt;
&lt;h3 id=&quot;dmn-활성화를-최소화하기-위해&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dmn-%ED%99%9C%EC%84%B1%ED%99%94%EB%A5%BC-%EC%B5%9C%EC%86%8C%ED%99%94%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%B4&quot; aria-label=&quot;dmn 활성화를 최소화하기 위해 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DMN 활성화를 최소화하기 위해&lt;/h3&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;400&quot; src=&quot;https://www.youtube.com/embed/hJ7w2FqPfBI&quot; title=&quot;나는 왜 쓸데없는 생각을 많이 해서 삶을 피곤하게 하는 걸까? ⵏ 뇌과학&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;올해에 있어 날 가장 괴롭혔던 타이틀은 &lt;code class=&quot;language-text&quot;&gt;불안함&lt;/code&gt; 이다. 불안함을 제거하기 위해 여러 방법을 시도했다. 앞서 말한 서적들도 여럿 읽었음에도 항상 불안함에 가득찼다. 불안함은 이번학기 학교 수업에도 전혀 집중하지 못하게 하고, 개발을 할때도, 밥을 먹을떄도 온전히 내 일에 집중하지 못하게 만들었다.&lt;/p&gt;
&lt;p&gt;근본적인 원인은 &lt;code class=&quot;language-text&quot;&gt;DMN&lt;/code&gt; 이라는것에 있었다. 나는 여지껏 &lt;code class=&quot;language-text&quot;&gt;DMN&lt;/code&gt; 이라는 존재 자체에 대해 전혀 모르고 살았다. DMN 이 자주 활성화되어 불안 모드가 자주 활성화되었고, 나를 괴롭혔던 것이다.&lt;/p&gt;
&lt;h3 id=&quot;나만의-불안-모드-해소방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%82%98%EB%A7%8C%EC%9D%98-%EB%B6%88%EC%95%88-%EB%AA%A8%EB%93%9C-%ED%95%B4%EC%86%8C%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;나만의 불안 모드 해소방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;나만의 불안 모드 해소방법&lt;/h3&gt;
&lt;p&gt;올해 3월, DMN 과 불안함의 존재, 근본적인 원인을 명확히 알아내고 덜어내고자 &lt;a href=&quot;https://haon.blog/%ED%9A%8C%EA%B3%A0/unrest-behavior/#dmn%EB%8A%94-%EC%84%A0%EC%B2%9C%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%A1%B4%EC%9E%AC%ED%95%98%EB%8A%94-%EA%B2%83&quot;&gt;개발자로 살아가면서 불안과 가면 증후군, 메타인지 학습법을 가꾸는 방법&lt;/a&gt; 라는 글을 작성했다. 이 글을 적으면서 정말 큰 도움이 되었다. 내가 왜 불안해하고 있는지 근본적인 원인을 알 수 있었다. 인간은 원초적으로 불안모드가 장착되어야만 생존이 가능했으며, 나는 DMN 이 자주 활성화되어 내 일에 집중하지 못한 것이다. 이를 해결하기 위해선 나만의 &lt;a href=&quot;https://haon.blog/%ED%9A%8C%EA%B3%A0/unrest-behavior/#dmn%EB%8A%94-%EC%84%A0%EC%B2%9C%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%A1%B4%EC%9E%AC%ED%95%98%EB%8A%94-%EA%B2%83&quot;&gt;의식의 흐름 분산 방법&lt;/a&gt; 이 필요했고, 이를 글쓰기로 삼았다. 앞으로도 나는 불안 모드가 활성화될 때 마다 글쓰기를 이어갈 것이다. 불안의 정도에 따라 블로그에 글을 발행하거나, 짧은 회고만으로 충분할 떄는 세컨드 브레인에 글을 작성해야지. 🙂&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;gastby-starter-haon-블로그-오픈소스-개발&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#gastby-starter-haon-%EB%B8%94%EB%A1%9C%EA%B7%B8-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EA%B0%9C%EB%B0%9C&quot; aria-label=&quot;gastby starter haon 블로그 오픈소스 개발 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Gastby Starter Haon 블로그 오픈소스 개발&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cc1bc08d40e1cede0a0654ec8560206b/6ee58/image-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.533742331288344%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAACpElEQVR42jWSy08TYRTFZwcJO1YYIo9EecQgbDTBf8CtG90awEgUMcaIjQuJrxgi0YUxMUgIC1calMd02kLbaeljSls6bYHSFhXREBVBqgkvg/LzdhJvMvke+c6Zc889yujIKOqoitPuxDvlxQiG0VQNr+4jnZ7HoWkMvhggFpslGo2SSqUxjAh21UE0EsXv9eMRnFNzoY3bUcbeTuBQhcytExYyM2Hi9wfYLGxSrEKhwMrKirU/ODiw1u3tbTxenVQyTSQ8Q8AftEhdQqpoEw7rEA4azMYTZBdzBIV4Y2PDItjf32dnZ5fd3V3+19bWFmZyTn76i8zCIrFonGAgjO72oegeH0bIwDRTZDJZPn1exYjM8H1tzQLvCdHPnwWy2Rw2m43e3tvcu3uXtvYLdHVfJSjYXG6JuFhihCIoRjhCIpFkUZR9+LDMt7V1EkJeVFisosK/ojQhVpSXl1NaWkpFRQVlZWWUlJQwLl6urn5hbm6BpFigzArzvBzy+SXx6hPrGwUy2by0uWMR/v69x5/9PXmcpLGhgerqao7W1dHa2kpzcwsTqouvX9es7hbmMyixSIx0MsW7fJ6Vj8v8+FHg1Rs7XVd7uP+gnwcPH9P36Cnd125SVVXFocpKqmtraGlpprGxEVWbFMwmSyIoLQlQQoEQZjzO0FsPtkGVTO49DpefM2fb6ei8wcXLN+m8cosz59o4XHWEppo66mvrOdZ0QpQe5/XIGKp9ElWSkk6lUIo5CkwHudM3wO2+YYxYgnA0IWuS6XAcr9/AF4wy9HKE8x3d9Fyycb3rFm0Xezh56jT9T55ZXTwfGMacNVFcjkkrgyHJninBLQ7J6fQwOuHGJUoDeoiYfO7JabwOHb+mo49N4ZM7ddyJW7BpGUZxwsXE/AMfg2kXQIliKQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/cc1bc08d40e1cede0a0654ec8560206b/a6d36/image-2.png&quot;
        srcset=&quot;/static/cc1bc08d40e1cede0a0654ec8560206b/222b7/image-2.png 163w,
/static/cc1bc08d40e1cede0a0654ec8560206b/ff46a/image-2.png 325w,
/static/cc1bc08d40e1cede0a0654ec8560206b/a6d36/image-2.png 650w,
/static/cc1bc08d40e1cede0a0654ec8560206b/e548f/image-2.png 975w,
/static/cc1bc08d40e1cede0a0654ec8560206b/3c492/image-2.png 1300w,
/static/cc1bc08d40e1cede0a0654ec8560206b/6ee58/image-2.png 1640w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위와 같은 심리적 불안감과 여정, 이를 해결하기 위한 노력들로 내 불안함은 진정될 수 있었다. 한편으론 올해 가장 큰 업적을 이룬 정량적 성과들이 존재한다. 그 중 하나는 바로 &lt;code class=&quot;language-text&quot;&gt;Gatsby Starter Haon&lt;/code&gt; 블로그 테마 오픈소스를 개발했다는 것이다.&lt;/p&gt;
&lt;p&gt;난 프로그래밍 못지않게 글쓰기에 몰입할떄가 가장 즐겁다. &lt;strong&gt;글쓰기는 머리 밖으로 분산된 생각들을 한 곳에 모아 정교화할 수 있고, &lt;code class=&quot;language-text&quot;&gt;메타인지&lt;/code&gt; 활성화에 아주 큰 도움이 된다. 내가 가진 생각을 가만히 두지않고, 밖으로 내보냈을 때(Output) 학습 효과가 매우 좋다는 것은 이미 뇌과학적으로 증명된 사실이다. 또한 인간은 망각의 동물이다. 이를 해결할 수 있는것이 글쓰기다. 예전에 기록했던 내용을 다시 확인하여 휘발된 기억들을 빠르게 복기하며 내가 알아낸 지식들을 장기화시킬 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이 떄문에 블로그를 다년간 운영해오기도 했고. 그런데 이전까지 Velog, 네이버 블로그등 여러 플랫폼을 사용하면서 항상 아쉬웠던점은 내가 블로그를 직접 커스터마이징 할 수 없다는 점이었다. 글을 작성하다가 불편한점이 있어도, 오류를 발견해도, 개선사항이 있어도 내 선호에 따라 글을 작성할 수 없는것이 큰 단점이었다.&lt;/p&gt;
&lt;h3 id=&quot;내가-직접-오픈소스를-만들자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%82%B4%EA%B0%80-%EC%A7%81%EC%A0%91-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4%EB%A5%BC-%EB%A7%8C%EB%93%A4%EC%9E%90&quot; aria-label=&quot;내가 직접 오픈소스를 만들자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;내가 직접 오픈소스를 만들자.&lt;/h3&gt;
&lt;p&gt;블로그를 운영하는 사람들 모두가 본인의 선호에 따라, 버그와 이슈를 발견할 때 마다, 개선사항이 존재할 떄 마다 즉각 코드를 수정하여 반영하는 블로그를 생각하게 됐다. 이 생각이 들때쯤 마침 동아리 선배이자 토스뱅크 현직자인 &lt;a href=&quot;https://github.com/devHudi&quot;&gt;Hudi&lt;/a&gt; 님이 제작한 블로그가 떠올랐다. 나 또한 본인이 직접 개발자 각자의 취향에 알맞게 직접 블로그를 커스터마이징 할 수 있는 오픈소스를 만들겠다는 굳은 목표를 가졌다. 본 Gatsby 블로그 테마 오픈소스는 Hudi 님의 블로그의 큰 영감을 받아 제작되었음을 밝힌다.&lt;/p&gt;
&lt;p&gt;지금까지 항상 백엔드 개발만을 이어왔기에 프론트엔드의 지식이 전무했다. 부끄럽게도 HTML 의 기초 문법 조차도 잘 모르는 상태였다. 그 만큼 더 많은 시간과 정성을 부어서 만든 오픈소스 프로젝트다. 꼼꼼히 이론을 학습하고, 내 열정과 노력, 많은 고민을 담아서 React 와 Gatsby 기반으로 개발된 프로젝트다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;혹여나 이 회고록을 읽고있는 독자가 있다면 자유롭게 &lt;a href=&quot;https://github.com/msung99/Gatsby-Starter-Haon&quot;&gt;Gatsby Starter 블로그 테마&lt;/a&gt;를 사용해보길 바란다! 🙂&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;세컨드-브레인-운영-시작-️&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B8%EC%BB%A8%EB%93%9C-%EB%B8%8C%EB%A0%88%EC%9D%B8-%EC%9A%B4%EC%98%81-%EC%8B%9C%EC%9E%91-%EF%B8%8F&quot; aria-label=&quot;세컨드 브레인 운영 시작 ️ permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;세컨드 브레인 운영 시작 ✏️&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9c7e785d12913b3dc1d1f068609d3a4a/a2792/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.84662576687117%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB60lEQVR42pWU2XLTQBBF/TdBq7VY+2LJlpRIVmwshSSYQKBCAlXJC1X5A9746UtPK6agiEx46LI1mjm6fbt7Jq9kF+PhQVI8KFrAIf4f3u9i8tyiOCirPnQjhjaNCObT2vBOm8Y4kpz/AQ6bVT1gqFAmwLZTwbCW/Dw1U8iK/zKg2KjpCSlJMPNKDstZELAkUEbwBM36mtekp2xGgeKlUGPOlgjTDbywgePXcMMVRQ2dlM38E1izBVSyQtVDzuRIGgHu0/HjFkl+hnR5jijrkBWXKOorzIsL2G7FMN1I2U+NBIwqFECxySQFQbJBVr3D4vgKzfYL1mcPaF7fIc56RPMt3KBmW3Qj+iPtv4CGlZE/IuU1lvU1ytUnvL/7jpuHHwzMyx0pv6AM+l8WyeoYkKoqFBp2jphUVO1ntP09+t0jtpffcHx6Q6A3pLBjbyVlaK1RheJLpp1xNYP4lA6fo+3uCfaIevOVIy/fwo9W9GFqHTXgPh31cN93mh7Tb0Tpz1E1H0jpLam7pcJ8ZG9NykCShwkaquwc6MMnqPDSdkuqcI8waZFXO0RpR0XpqCAnUPSI033R6O3bx3YLDlEkyykGddSjwjPh9XOzPRmbyb1S0bw8fmaCKVnAawcuismhm+P3QwIsK94/b5yfgvXmQa4XSC8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/9c7e785d12913b3dc1d1f068609d3a4a/a6d36/image-3.png&quot;
        srcset=&quot;/static/9c7e785d12913b3dc1d1f068609d3a4a/222b7/image-3.png 163w,
/static/9c7e785d12913b3dc1d1f068609d3a4a/ff46a/image-3.png 325w,
/static/9c7e785d12913b3dc1d1f068609d3a4a/a6d36/image-3.png 650w,
/static/9c7e785d12913b3dc1d1f068609d3a4a/e548f/image-3.png 975w,
/static/9c7e785d12913b3dc1d1f068609d3a4a/3c492/image-3.png 1300w,
/static/9c7e785d12913b3dc1d1f068609d3a4a/a2792/image-3.png 1462w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;블로그를 항상 운영하면서도 항상 단점이 존재했다. 생각의 정교화를 위해 블로그에 글을 발행할 떄 마다 소요되는 시간이 매우 길다는 점이다. 글을 발행하는 해당 지식에 한정해선 메타인지가 활성화되며 학습효과에 매우 큰 도움이 되지만, 이와 관련한 관련 학습 키워드들을 노션에 정리만해두고 그때그때 학습하지 못한다는 점이 항상 아쉬웠다. 메인 학습 키워드를 학습하는데 많은 시간이 걸리다보니, 다른 토픽을 학습하는데 많은 부담을 느꼈다.&lt;/p&gt;
&lt;p&gt;이를 위해 세컨드 브레인을 운영하기 시작했다. 정확히는 &lt;code class=&quot;language-text&quot;&gt;디지털 가든&lt;/code&gt; 을 운영하기 시작했다. 월간 구독을 결제하고 옵시디언의 예쁜 테마를 활용하여, 생각을 정제하기 위한 나만의 공간을 만들었다. 그런데 사실 몇달 운영하다가 운영 비용이 꽤 큰 부담이라 현재는 비활성화 상태에 있다. 현재는 개인적으로 옵시디언에 세컨드 브레인 형태로 간략히 글을 발행하는 형태로 작성중에있다.&lt;/p&gt;
&lt;h3 id=&quot;더-올바른-학습법을-위해&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%EC%98%AC%EB%B0%94%EB%A5%B8-%ED%95%99%EC%8A%B5%EB%B2%95%EC%9D%84-%EC%9C%84%ED%95%B4&quot; aria-label=&quot;더 올바른 학습법을 위해 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 올바른 학습법을 위해&lt;/h3&gt;
&lt;p&gt;아직 운영 기간이 그리 길지 않아, 세컨드 브레인 운영방법에 대해 더 연구해봐야겠다. 올해 하반기에는 이를 어떻게 효과적으로 활용하여 내 학습법을 개선할지 고민해야 할 듯하다. 글을 발행하는 방식이 좋지 않은것인지, 블로그에 발행하는 글이 아니라서 그런것인지는 모르겠다. 아직까지는 옵시디언에 발행한 글들이 내 생각의 정교화에 있어 다소 아쉬움이 남는다. 아직까진 온전히 내 지식으로 체화시키는데에 있어 100% 만족시킬 수 없는듯하다.&lt;/p&gt;
&lt;p&gt;당장으로 떠오르는 방법은 앞으로 학습을 이어가며 우선순위를 정해야겠다. 높은 우선순위를 가진 학습 키워드에 대해 블로그에 글을 발행하고, 낮은 우선순위 키워드에 대해선 옵시디언에 정제하도록 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;뒤늦은-알고리즘-학습-시작&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%92%A4%EB%8A%A6%EC%9D%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%95%99%EC%8A%B5-%EC%8B%9C%EC%9E%91&quot; aria-label=&quot;뒤늦은 알고리즘 학습 시작 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;뒤늦은 알고리즘 학습 시작&lt;/h2&gt;
&lt;p&gt;한편 커리어를 쌓기 위해, 어디던 입사하기 위해 알고리즘 역량을 필수로 갖춰야했다. 하지만 이전까지 나는 순수 프로그래밍만을 이어왔기에, 알고리즘 공부에서 손을 땐지 1년반이 넘어갔다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/146450977612633055f886bc2b420a40/d6a46/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.668711656441715%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABxElEQVR42o1SSW4UQRCcB8zS+1JVvdZ093g2G8SBC5tkiQNC4jm8ggvivUFG9owNRkY+hLKqMisyclmsowIvRZCYB2yiEkFo/kKYWiyugbSbuHwWgSDKK0WQWATGIp5qROOM0DuswnwmLFwP24xw3YSy3mIVZOokmKystqj7Pd7ff8HbD/fw/hb15yNuf3zEp5/f8O7XV/jvb7BOikeFlBsys1hVJHYtlu9URGW57ZGaDkneIKwqxIcG6bFDdpL3fTsr7KYz6u0BjYDnbjyrLUWxv7kTxYP6ar+HbSe4/gZGfN1wwji9fkBW9Nq2RSWBrttpYDue9HMrwfzEOwlrf0AlJTO2HY5KTBH97ozh8AqjIHd+JqwvhJntsJKH5aV3dD61T89J2QhRr2SxtGTNkjkQIimaf9Zk8981KvVPZlolZa9VYVl5GCmLU2b2JScsWD6D1R8VUIhpBt2OKLusTSFy6cil5KSotYwoq9TyzlJSOROzzz2WLH7+4/TD1F2G0u+0h9y1gmolI1Xz3cqZ91YGYNtR4WQg12VnvO6vvDHxrLCiQpa9VekMYF8yM2cmeSxK2BYOjkTX4bBv7J+TRKo8zPEb+Nk7S9Llh50AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/146450977612633055f886bc2b420a40/a6d36/image-4.png&quot;
        srcset=&quot;/static/146450977612633055f886bc2b420a40/222b7/image-4.png 163w,
/static/146450977612633055f886bc2b420a40/ff46a/image-4.png 325w,
/static/146450977612633055f886bc2b420a40/a6d36/image-4.png 650w,
/static/146450977612633055f886bc2b420a40/e548f/image-4.png 975w,
/static/146450977612633055f886bc2b420a40/d6a46/image-4.png 1008w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;어딘가에 최소 지원자격을 갖추기위해선 알고리즘 역량이 반드시 필요함에도, 그간 학습을 미루었기 떄문에 더 빠른 학습이 필요했다. 이번 상반기에 꾸준히 알고리즘을 학습한 흔적들이 존재한다. 알고리즘 문제를 가장 많이 풀었을 때는 하루에 15문제 가까이 풀었던 것 기억이 난다 🤔 덕분에 커밋이 많이 쌓였다.&lt;/p&gt;
&lt;h3 id=&quot;후회되던-매-순간들&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%9B%84%ED%9A%8C%EB%90%98%EB%8D%98-%EB%A7%A4-%EC%88%9C%EA%B0%84%EB%93%A4&quot; aria-label=&quot;후회되던 매 순간들 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;후회되던 매 순간들&lt;/h3&gt;
&lt;p&gt;신입공채, 인턴, 교육기관등 수많은 업체에선 코딩테스트 역량이 당연시 되었으며, 난 알고리즘을 등한시해왔다. 이전까지 장기간 알고리즘을 버리듯이 했기 때문에, 그 어떠한 지원자격도 충족되지 않았다. 과거의 내가 행동했던 순간들이 많이 후회됐던만큼, 더 필사적으로 노력하여 단기간이 많은 기량을 쌓았다.&lt;/p&gt;
&lt;h3 id=&quot;한가지를-고집피우지-말것&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%9C%EA%B0%80%EC%A7%80%EB%A5%BC-%EA%B3%A0%EC%A7%91%ED%94%BC%EC%9A%B0%EC%A7%80-%EB%A7%90%EA%B2%83&quot; aria-label=&quot;한가지를 고집피우지 말것 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;한가지를 고집피우지 말것&lt;/h3&gt;
&lt;p&gt;알고리즘을 학습하며 확실히 반성한 점이 있다. &lt;strong&gt;한가지 길에만 올곧 고집을 피우며 도전하는 것은 올바르지 못하다는 점이다.&lt;/strong&gt; 이전까지 알고리즘은 프로그래밍에 있어 필요없는 과목이라는 오만한 생각떄문에 학습을 미루었다. 이번 하반기에도 이런 잘못된 고집을 피우며 올바른 방향성으로 나아가는데 방해가 되지 않도록 하자.&lt;/p&gt;
&lt;p&gt;그렇다고 내 의견에 뚜렷한 주관이 없어서도 안된다. 내 주장이 확실해야할땐 그에 마땅한 근거와 논리가 뒷받침 되어야한다. 앞으로 내가 선택한 길과 행동이 올바른지 주기적으로 회고를 이어가도록 해야겠다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;카카오테크-코스-합격까지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%85%8C%ED%81%AC-%EC%BD%94%EC%8A%A4-%ED%95%A9%EA%B2%A9%EA%B9%8C%EC%A7%80&quot; aria-label=&quot;카카오테크 코스 합격까지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;카카오테크 코스 합격까지&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e5f17546ba2ee09be230e27126d661ef/b1ffc/image-8.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.44171779141104%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABb0lEQVR42p2SzUsCURTFx1Jo4SraWxAUbSLKoghs4QRFZIQfuBF3/Qf+AS3Cda1a9S0aBIVYGzEs08oZlYiIcnaWGGJgZJgzc7qpq8jMWRw4vMf93fPufQx4Bo2l+kVt+KuGaXxJhZwaiDOQeQ15VdVX1RqQCjmCpDohpi2QbvsgCTZId6MkPaR7A5DQKAAmtQS0ofIwByltReXRhEraTGdmAnYoefK32qlYVfOJ+hh4tdIZMpDOhiGfkyLdkC905Cchx4YUzJDSiFcd+DxigdAi5KidluEALu2QQkbInLpFYIpBMdCL110Wb8czEK+dKPNLKIatKJ/MQwz31Eeg+geQuosxHQp7E4gu6+F1DQIRC54OF7DpGsGLh0U5QBtvmpCrJXsPdqGwNYvMmgHPq3rkto0QfA5k/U5kPUZ87E8j7xujv9kUSEoyKIW1uHGPQ1ghmHsA+R0T4kE/hNMNlLxTyKyzyB30V5tXQ/wAfgE7XG53Kw70XwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/e5f17546ba2ee09be230e27126d661ef/a6d36/image-8.png&quot;
        srcset=&quot;/static/e5f17546ba2ee09be230e27126d661ef/222b7/image-8.png 163w,
/static/e5f17546ba2ee09be230e27126d661ef/ff46a/image-8.png 325w,
/static/e5f17546ba2ee09be230e27126d661ef/a6d36/image-8.png 650w,
/static/e5f17546ba2ee09be230e27126d661ef/e548f/image-8.png 975w,
/static/e5f17546ba2ee09be230e27126d661ef/3c492/image-8.png 1300w,
/static/e5f17546ba2ee09be230e27126d661ef/b1ffc/image-8.png 1492w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;불안함과 불확실성, 우울했던 암울기가 가득했던 2024년 상반기였다. 불안감을 잠재우기 위해 많은 심리학, 자기계발 책을 읽었으며, 글쓰기를 통한 DMN 의 근본적인 원인을 알아내고 극복하려했다. 모든것을 다 내려놓고 싶던 마음이 가득했지만, 무엇이던 이루어내야 한다는 굳은 마음가짐을 가지고 묵묵히 걸어나가니, 카카오테크 본 과정에 참여할 수 있게 되었다. 최종 합격 소식을 듣게 되었을 때 정말 얼마나 기쁘던지 🙂&lt;/p&gt;
&lt;p&gt;본 과정에 최종 합격하기 전까지 많은 실패와 좌절을 경험했다. 필사적으로 살았음에도 임펙트있게 얻어낸 큰 성과가 없으니 자존감이 많이 내려갔다. 내 감정을 컨트롤하는 능력이 더욱이 중요했고, 내 일에 집중하기 힘들어도 꾸준히 걸어나가는게 힘들었다. 누군가 이 글을 읽고있다면 힘든 여정이 있어도 포기하지 말라는 말을 꼭 전하고 싶다. 나 또한 인생의 하이라이트를 만들어내기 위해 수많은 노력과 수고가 있었기 떄문에 가능한것이다. 타인과 나를 비교하지말고, 어제보다 성장한 나에 대한 성취감을 만끽하며 꾸준한 페이스로 나아가길 바란다.&lt;/p&gt;
&lt;h3 id=&quot;실무-중심의-경험&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%EB%AC%B4-%EC%A4%91%EC%8B%AC%EC%9D%98-%EA%B2%BD%ED%97%98&quot; aria-label=&quot;실무 중심의 경험 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실무 중심의 경험&lt;/h3&gt;
&lt;p&gt;내가 본 과정에서 얻어가야할 가장 큰 키워드는 실무 중심의 경험을 쌓는것이다. 본 과정은 카카오 현직에서 사용하는 애자일 스크럼, 협업 프로세스등을 직접 그대로 경험할 수 있다. 이러한 실무 문화 외에도 카카오에서 직접 사용하는 실무 기술들을 경험할 수 있다고하니, 내 커리어에 있어 정말 큰 도움이 될 것이다. 본 과정에 참여하여 끝 없이 질문하고, 토론하자. 또한 실무 생태계에 대한 이야기를 직접 듣고 원 없이 경험해야겠다.&lt;/p&gt;
&lt;h3 id=&quot;교육자의-길&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B5%90%EC%9C%A1%EC%9E%90%EC%9D%98-%EA%B8%B8&quot; aria-label=&quot;교육자의 길 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;교육자의 길&lt;/h3&gt;
&lt;p&gt;나는 언젠가 꼭 교육자의 길을 걷고 말 것이다. &lt;strong&gt;내가 만들어낸 소프트웨어, 지식들을 타인에게 공유했을 때 얻을 수 있는 보람은 내게있어 큰 원동력이 된다.&lt;/strong&gt; 이 원동력은 내 학습에 있어 큰 즐거움을 느끼게 해주고, 지금까지 꾸준히 걸어올 수 있게해준 큰 요인이라고 생각한다. 본 과정에서 코치님들을 통해 교육자에 대한 이야기를 자세히 듣고, 조언받고 싶다.&lt;/p&gt;
&lt;p&gt;카카오테크 최종 면접을 보면서 들었던 말이 아직도 생생하다. 카카오테크 자소서에 교육자가 되고싶은 이유, 가치관에 대한 내용을 진심을 담아 글을 작성했더니 면접관님들에게 인상깊게 잘 읽었다는 말씀을 들을 수 있었다. 특히 &lt;code class=&quot;language-text&quot;&gt;Gatsby Starter Haon&lt;/code&gt; 에 대한 오픈소스 제작 경험을 기반으로 작성한 내용을 보고 감명 깊었다고 말씀하셨다. 역시 글은 진솔함을 담아 작성해야지 최고의 글쓰기가 될 수 있다. 🙂&lt;/p&gt;
&lt;h3 id=&quot;부가적인-목표&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%80%EA%B0%80%EC%A0%81%EC%9D%B8-%EB%AA%A9%ED%91%9C&quot; aria-label=&quot;부가적인 목표 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;부가적인 목표&lt;/h3&gt;
&lt;p&gt;본 과정에 참여하면서도 부가적으로 신경써야 할 것들이 많다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ICT 자격증&lt;/li&gt;
&lt;li&gt;어학 점수&lt;/li&gt;
&lt;li&gt;코딩테스트&lt;/li&gt;
&lt;li&gt;본 과정에서의 상세한 기록, 블로깅, 꼼꼼한 문서화&lt;/li&gt;
&lt;li&gt;미흡한 CS 보완 및 면접대비&lt;/li&gt;
&lt;li&gt;카카오 현직자님들에게 이력서 조언받기&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;부끄럽게도 ICT 자격증은 취득하는 것이 취업에 있어 최소한의 조건임에도 신경쓰지 못했다. IT 개발자 직군은 자격증을 보지 않는다, 실력만 본다라는 말은 앞으로 맹목적으로 받아들이지 않을 것이다. 앞서 &lt;strong&gt;항상 모든 판단에 있어 하나만 올곧 좋은 것이라 고집 피우는 것은 좋지 않다고 했는데, 이 또한 해당한다.&lt;/strong&gt; 항상 누군가, 특히 권위있는 사람, 또는 여러 군집단이 주장하는 발언에 대해선 의심하는 습관을 가지자. 확실한 것은, 특정 기업을 제외한 우리나라의 대부분의 기업에선 최소한의 자격증을 요구한다.&lt;/p&gt;
&lt;p&gt;코딩테스트는 아직 급한것은 아니다. 이미 알고리즘은 기본기가 잘 다져있으므로, 이번년도 후반기에 급하지 않게 준비하도록 하자. 아직 스스로 미흡하다고 느낀 CS 를 보완해야한다. 이는 아직 급한것은 아니며, 향후 본 과정 참여후 어떤 방식으로 학습할지 알 수 없으므로 학습 리스트 중 하나로 꼭 기억해두자.&lt;/p&gt;
&lt;p&gt;무엇보다 가장 중요한 것이 본 과정에서 성장하는 것이다. &lt;strong&gt;내가 어떤 사람인지 증명할 수 있는 정량적 지표가 바로 블로그에 남긴 기록, 문서화 내용이다.&lt;/strong&gt; 기록만이 살 길이다. 문서화는 내게있어 앞으로 취업시 이력서에 내세울 최고의 강점임을 꼭 기억하자. 분명히 본 코스에 참여했을 때 내가 이미 잘 알고있는 지식들이 존재할것이다. 이미 알고있는 지식이라고 할지라도, 100% 이해한 것이 아니라는 의심이 단 0.1초라도 든다면 문서화를 고려하자. 또한 학습 키워드에 있어 간혹 문서화까지 필요없는 것도 있을 것이니, 우선순위를 잘 정하여 블로깅, 문서화를 하자. 이전에 이미 블로그에 학습한 내용일지라도, 새롭게 적어보는 것을 고려해보자.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;내가-감사해야-할-사람들-거리두어야할-사람들&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%82%B4%EA%B0%80-%EA%B0%90%EC%82%AC%ED%95%B4%EC%95%BC-%ED%95%A0-%EC%82%AC%EB%9E%8C%EB%93%A4-%EA%B1%B0%EB%A6%AC%EB%91%90%EC%96%B4%EC%95%BC%ED%95%A0-%EC%82%AC%EB%9E%8C%EB%93%A4&quot; aria-label=&quot;내가 감사해야 할 사람들 거리두어야할 사람들 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;내가 감사해야 할 사람들, 거리두어야할 사람들&lt;/h2&gt;
&lt;p&gt;올해 상반기에 대인관계에 있어서도 많은 생각이 들었다. 내가 감사해야 할, 고마워해야 할 사람들이 있고, 거리를 두어야할 사람들이 있음을 느꼈다.&lt;/p&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;400&quot; src=&quot;https://www.youtube.com/embed/hU4kULhOdNE?si=y-aO6l5hCT5SSqOq&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;항상 거리감을 두고, 최소한의 관계를 유지해야 할 사람들이 있다. &lt;strong&gt;내게있어 질투심에 찌들어 성장을 막기위한 사람들이 생각보다 많음을 느꼈다.&lt;/strong&gt; 내가 하는 행동에 이해할 수 없다는 말, 그리고 질투심에 눈이 멀어 내가 가는길이 틀렸음을 지적하고, 자신에게 위안을 삼는 사람들이 세삼 존재할 때가 있다. &lt;a href=&quot;https://haon.blog/%ED%9A%8C%EA%B3%A0/unrest-behavior/&quot;&gt;개발자로 살아가면서 불안과 가면 증후군, 메타인지 학습법을 가꾸는 방법&lt;/a&gt; 에서도 이와 관련해 회고했던 적이 있다. 드림코딩님도 말씀했듯이, 앞으로 질투심에 나를 가로막는 사람과는 반드시 거리를 두자.&lt;/p&gt;
&lt;p&gt;또한 나를 이용하여 본인의 이득을 취하는 사람과도 거리를 둘 것이다. 내가 알고있는, 보유한 지식, 배움, 기타등등 모두 뽑아내어 본인의 욕심을 채우는 사람 또한 마찬가지로 거리를 두어야한다. 최근 프로젝트에서 겪은점도 있고.&lt;/p&gt;
&lt;p&gt;반대로 감사해야할 사람들이 있다. 나를 진심으로 응원해주고, 올바른 길로 나아가도록 만들어주는 사람들이다. 이런 내 주변 사람들에게 항상 감사한 마음을 가지고, 좋은 관계를 계속 이어가도록 하자.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;컴포트-존에서-벗어나기-위해&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%B4%ED%8F%AC%ED%8A%B8-%EC%A1%B4%EC%97%90%EC%84%9C-%EB%B2%97%EC%96%B4%EB%82%98%EA%B8%B0-%EC%9C%84%ED%95%B4&quot; aria-label=&quot;컴포트 존에서 벗어나기 위해 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컴포트 존에서 벗어나기 위해&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7b5e04510ee88e3f17967502950bc2f5/df88b/image-7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.190184049079754%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACcklEQVR42oWTWU+TQRSG+U9qjERMvJOwRSXFGoGQ0gq0bKW1LNJQ+rEWulmgrAaTQqHIUiIiLgkQIW6gBCJCoqI3RCQYFkVoefwoQgBLnOTczJx55rznnROys7NDsNhdy+sbjM3PMvj2NeOz0yz9WAnsH845fi8kGOjn1jae4UFyrflklylJNiq5ImiItwq0DHlZ3VgPCjsC9Pv9f2FbtHoc2IwSXBXXaRPDY0umxaIgoyKbSCEXTbONlbXVQL7/ZKAvkDD+vJt+cwITd3OYchcw0pxDtyOVPmcGHmsy2VVaIow6zD2ukyXvV7e+tozbIMd0NQIhJhxPvoz5nlJetahxVyWI1UppqEggsVpPpFHDzMLHwD2fWMwRoM+3HTiYezmKNuw8ZdJ4TIlJ5IRdxFusYOGhhWf16fQ1aRloykZnK0BeV8XAm7E9oO84cHsPONLpQXXqNOVSKSXX4kgPvYA9KZbprlKe1Cpps6fRZVNwqzyLeLOA0OjY66OoMChw4ukQmWfPoY+IRncpHMWZUJzKOMab1PSYZDiFGzQWxSLTpxKmSaHIYd51JWDMPjTksCGLi1+w35RQHBmFIToGR5KEd615zHYU0muTcf+OglaLHLnNgMSk58XcTHDJgRBf2LXG63XSL1YybFYx1a5n0lXAA1Fuh1WOWzRFXZ5JeLEWdaOVTfGLBZV82OmF79+orLuNy5LEoENBrzmRbruC9hoVhVYd0moDUlPhgcOHYf9Myj7009IShns1pAhpZAgppJaouFycRVRJHvmttbz/+vn/k3J8/H6LfRmb/0D94wEq+zppeORldHqSjc1fB7BgH/sPa1JbI+WedT0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;&quot;
        src=&quot;/static/7b5e04510ee88e3f17967502950bc2f5/a6d36/image-7.png&quot;
        srcset=&quot;/static/7b5e04510ee88e3f17967502950bc2f5/222b7/image-7.png 163w,
/static/7b5e04510ee88e3f17967502950bc2f5/ff46a/image-7.png 325w,
/static/7b5e04510ee88e3f17967502950bc2f5/a6d36/image-7.png 650w,
/static/7b5e04510ee88e3f17967502950bc2f5/e548f/image-7.png 975w,
/static/7b5e04510ee88e3f17967502950bc2f5/3c492/image-7.png 1300w,
/static/7b5e04510ee88e3f17967502950bc2f5/df88b/image-7.png 1906w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;그간 컴포트 존에서 장기간 머물렀기 때문에 항상 애를 먹었다. 스터디, 프로젝트를 최대한 이어가보려 해도 나에비해 아는것이 많이 없는 동료들과 협업을 진행하거나, 대부분의 그룹에서 내가 가장 잘하는 사람이었다. 이 속에서 느낀점은, 컴포트 존에서 자기만족을 하면서 머무른다면 절대 성장할 수 없는것이다. 이를 잘 알고 있기 때문에, 올해 상반기에 어떻게든 벗어나기위해 필사적으로 살았다.&lt;/p&gt;
&lt;p&gt;다행히도 한층 컴포트 존에서 벗어날 수 있었다. 카카오테크 과정에서 정말 열심히 성장할 것이다. 성장하는 과정속에서 겪는 모든 불안은 나를 위한 성장통임을 꼭 기억하자.&lt;/p&gt;
&lt;h3 id=&quot;가장-못하는-사람이-되라&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%80%EC%9E%A5-%EB%AA%BB%ED%95%98%EB%8A%94-%EC%82%AC%EB%9E%8C%EC%9D%B4-%EB%90%98%EB%9D%BC&quot; aria-label=&quot;가장 못하는 사람이 되라 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;가장 못하는 사람이 되라&lt;/h3&gt;
&lt;p&gt;난 어디서든 가장 못하는 사람이 되어야한다. &lt;strong&gt;새로운 것에 대한 두려움과 불안이 있어야만 내가 성장할 수 있음을 항상 기억하자.&lt;/strong&gt; 또한 나보다 잘하는 사람들을 보며 비교될 때 마다, 내가 현재 불안해 하는것은 성장통이라고 생각할 것이다. 소프트웨어 교육자 박재성님이 &lt;a href=&quot;https://brunch.co.kr/@javajigi/46&quot;&gt;가장 못하는 사람이 되라&lt;/a&gt; 에서 말했던 내용과 부합한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;올해 상반기는 많은 사건들과 우울기, 극복기등 많은 경험들과 배움이 있었다. 당장 다음주부터 카카오테크 과정에 참여할 때도 지금까지 얻은 배움들로 열심히 성장해보자. 특히 블로그, 세컨드 브레인을 활용하여 몰입하고, 빠르게 성장해야겠다.&lt;/p&gt;
&lt;p&gt;글을 완전히 마무리짓기전, 생각나는 한 사람이 있다. 올해 상반기에 많이 힘들었던 내게 큰 힘이 되어주었던 &lt;a href=&quot;https://github.com/sean2337&quot;&gt;대학교 멋쟁이사자처럼 동아리내 가장 친한 동생이자 친구&lt;/a&gt; 가 생각난다. 항상 내가 힘들어했던 시기마다 큰 도움을 주었던 내 사람들에게 감사하며 살아야겠다. 😆 올해 하반기에는 더 멋진 나로 성장해있길 기대한다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Slf4j MDC(Mapped Diagnostic Context) 로 멀티쓰레드 환경에서 요청별 고유 ID 로그 생성하기]]></title><description><![CDATA[현 포스트의 학습 동기는 Slf4j logback…]]></description><link>https://haon.site/haon/spring/mdc-mapped-diagnostic-context/</link><guid isPermaLink="false">https://haon.site/haon/spring/mdc-mapped-diagnostic-context/</guid><pubDate>Thu, 02 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;현 포스트의 학습 동기는 &lt;a href=&quot;https://haon.blog/spring/logging-slf4j/&quot;&gt;Slf4j logback 이란 무엇이며, 왜 로깅을 해야할까? 💁‍♂️&lt;/a&gt; 에서 이어지는 내용이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;로그가-뒤섞이는-문제-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EA%B7%B8%EA%B0%80-%EB%92%A4%EC%84%9E%EC%9D%B4%EB%8A%94-%EB%AC%B8%EC%A0%9C-%EC%83%81%ED%99%A9&quot; aria-label=&quot;로그가 뒤섞이는 문제 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로그가 뒤섞이는 문제 상황&lt;/h2&gt;
&lt;p&gt;서비스를 운영하다보면 로그를 통해 모니터링을 하고, 발생한 애러에 대응하기 위해 로그를 추적하고, 기록을 남긴다. 이떄 문제점은 멀티 쓰레드 환경에서 여러 쓰레드의 동시 요청이 유입되는 경우 로그가 뒤죽박죽 쌓인다는 점이다. 가령 회원가입을 위한 요청이 동시다발적으로 유입된다면 아래와 같이 로그가 쌓일 것이다.)&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a0b6c2202f67f5d948770c046257c1f5/5bd27/image-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.12883435582822%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABbUlEQVR42j2S7W6cMBQFeZq0aasCNhj8bWA3CcuSpGlTqVLV93+L6V0q9cfoCOsee2xR9VNgnSNX4fWcmaInWcfT4Fhd4skGNldYbWQtjudT5uR7bH2PbT+T6k+8N/d8k+9dqMxbYfs58/I+8/3XmeU1U7bCvs3s1xPbNh257wvXH4UXmZ0vBr/U+LlhflNc/mjW3x2rZPXlweMfA0mYLgmzeLriiSUKiZDjQXnM5EvEnx3h1jnZg3BxpGfpikjYLdVdkEIUq1RYphk9RlqdyHoidTOxKyQtVnEhJyn5QJSOt7Kp80dGeY4pT/TKUH20I4NzOCHGgBostR4ZlcPqwKj/pRsC1lqsswzjgFIKpRVaa3SnMcbQNA3Vh3S7VqKUwmmZ6ZwY9pHUZ5KZCN0tZ7IVZDbnzCDluq6PDW60bXMc0MhadTdajKhbIcRI01u+tmKlxE5FBuWPtEM8Zkb5A4y5GWox7P7Ty1rTKv4CNl/tuJRz/ngAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;image 4&quot;
        title=&quot;&quot;
        src=&quot;/static/a0b6c2202f67f5d948770c046257c1f5/a6d36/image-4.png&quot;
        srcset=&quot;/static/a0b6c2202f67f5d948770c046257c1f5/222b7/image-4.png 163w,
/static/a0b6c2202f67f5d948770c046257c1f5/ff46a/image-4.png 325w,
/static/a0b6c2202f67f5d948770c046257c1f5/a6d36/image-4.png 650w,
/static/a0b6c2202f67f5d948770c046257c1f5/e548f/image-4.png 975w,
/static/a0b6c2202f67f5d948770c046257c1f5/3c492/image-4.png 1300w,
/static/a0b6c2202f67f5d948770c046257c1f5/5bd27/image-4.png 1432w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위 로그는 파악하기 쉬운가? 로그를 보면 1개의 요청에 대한 로그가 순차적으로 나열되는 것이 아닌, 여러 사용자의 요청에 대한 로그가 뒤섞여서 파악하기가 힘들다. 우리는 요청별로 로그를 확인해야 로그를 추적하기에 편할 것이다. 자연스레 각 요청별 식별자가 필요함을 느낄 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;mdc-mapped-diagostic-context&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mdc-mapped-diagostic-context&quot; aria-label=&quot;mdc mapped diagostic context permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MDC (Mapped Diagostic Context)&lt;/h2&gt;
&lt;p&gt;우리는 클라이언트 요청별로 고유 값을 부여한 후 로그에 함꼐 출력되기를 기대한다. 이때 사용되는 것이 MDC 이다. &lt;code class=&quot;language-text&quot;&gt;MDC(Mapped Diagnostic Context)&lt;/code&gt; 란 &lt;strong&gt;현재 실행중인 쓰레드에 메타 정보를 넣고 관리하는 공간&lt;/strong&gt;이다. MDC 는 내부적으로 Map 을 관리하고 있어 key-value 형태로 값을 저장할 수 있다. 메타 정보를 각 쓰레드 별로 관리하기 위해 내부적으로 &lt;code class=&quot;language-text&quot;&gt;ThreadLocal&lt;/code&gt; 을 사용하고 있다. 아래와 같이 key-value 메타 데이터에 대해 저장, 조회, 초기화가 가능하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;MDC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;my-key&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;my-value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token constant&quot;&gt;MDC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;my-key&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token constant&quot;&gt;MDC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;threadlocal&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#threadlocal&quot; aria-label=&quot;threadlocal permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ThreadLocal&lt;/h3&gt;
&lt;p&gt;ThreadLocal 이란 &lt;strong&gt;오직 하나의 쓰레드에 의해서 읽고 쓰여질 수 있는 고유한 변수다.&lt;/strong&gt; 두 쓰레드가 같은 코드를 실행하고 이 코드가 하나의 ThreadLocal 변수를 참조한다고 한들, 서로가 ThreadLocal 변수를 볼 수 없다. 이 점에서 ThreadLocal 변수를 활용하면, 멀티 쓰레드 환경에서 각 쓰레드마다 독립적인 변수로 접근할 수 있음을 보장할 수 있게된다.&lt;/p&gt;
&lt;p&gt;이런 특징 때문에 MDC 라는 일종의 독립적인 ThreadLocal 공간에 메타정보를 저장하여 고유 ID 를 부여하고, 문맥(Context) 를 저장함으로써 식별 가능해지게 되는 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;멀티쓰레드-환경에서-mdc-로-고유-id-로그-남기기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%80%ED%8B%B0%EC%93%B0%EB%A0%88%EB%93%9C-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-mdc-%EB%A1%9C-%EA%B3%A0%EC%9C%A0-id-%EB%A1%9C%EA%B7%B8-%EB%82%A8%EA%B8%B0%EA%B8%B0&quot; aria-label=&quot;멀티쓰레드 환경에서 mdc 로 고유 id 로그 남기기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;멀티쓰레드 환경에서 MDC 로 고유 ID 로그 남기기&lt;/h2&gt;
&lt;p&gt;백문이불여일타. 직접 MDC 로 로그를 남기는 코드를 구현하면서 이해해보자. 우리는 스프링에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;Filter&lt;/code&gt; 를 구현하여 MDC Filter 를 만들 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;filter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#filter&quot; aria-label=&quot;filter permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Filter&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c382d971796c5c4bbc4328365c4bb8c5/387f2/image-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.34969325153374%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACKUlEQVR42n2S208TQRSH+TeN8d0H33w0XJ68YHwz0QdAjPpCDFCKQpWLqQ1UBQwBAwqE0oLAtttuu9vdndt+zhaCKNVJfsmcmcl3zpzz69Fac3mFYUjYbmOMQSmFtlI6lUaZBKmNVbpPZTrS5ozRbDbp+RtYq7kdmcTQjgKOnAq+iNB+lXh7EblXxBxuoMrr6NIa6mgLIePuwCRJOpVhYcrGqvCRZGQI4dRQbhmxMIz7bpjW/DOStSnYWiTZLSJswivAFJZYUPlU89PRmBSYmYT+fuLyIXHtAGYeYT4Mo4tjiMIr1PIYybcF4m4VBkGA53lMLSdklyS+ilBvpuH+Pfz9A/zKFsHILdqZQZKVCdpzQwS5J5iv0xYorgLDsI3v+2QKAZm8hy8jvMkJqr29NHb28EqbVJ/epDV+F7WWpZl/SWPxOdF6rnsPU1ir1WL2C8x+EgQqJp7OEgwMoI9P0d4x5B5jlsYIV3O0l22VS+OIzbwFim49POtjvaVp+qbTQz3zFgYf2EmeIMITktKE1WvUj1Hi7y8wO6Mklaz9cvS/Kdv5ph5LgelQ+vqQlRNUuA/bD2H9NmH+Os77G9YF10g27hCL8Bzo0SOl7FglVQoXQiKlQto42t9FrHzGd+vW7DVEbRVxWkAczxNUFpDHc8ROkejcNo1G46qxL6+04ihNYpPGsUAq8893F192HAfXdS9Ur9d/Kz2rVi/d1+zZmf7cux3LlUolfgHBOoAI+wTIugAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;image 1&quot;
        title=&quot;&quot;
        src=&quot;/static/c382d971796c5c4bbc4328365c4bb8c5/a6d36/image-1.png&quot;
        srcset=&quot;/static/c382d971796c5c4bbc4328365c4bb8c5/222b7/image-1.png 163w,
/static/c382d971796c5c4bbc4328365c4bb8c5/ff46a/image-1.png 325w,
/static/c382d971796c5c4bbc4328365c4bb8c5/a6d36/image-1.png 650w,
/static/c382d971796c5c4bbc4328365c4bb8c5/e548f/image-1.png 975w,
/static/c382d971796c5c4bbc4328365c4bb8c5/3c492/image-1.png 1300w,
/static/c382d971796c5c4bbc4328365c4bb8c5/387f2/image-1.png 1518w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Filter 는 스프링 컨테이너와 별개인 &lt;code class=&quot;language-text&quot;&gt;Servlet Container&lt;/code&gt; 에서 제공하는 기능으로, 스프링 컨텍스트가 아닌 웹 컨텍스트에 속한다. &lt;strong&gt;즉, 스프링 컨테이너가 아닌 웹 컨테이너(Tomcat)에 의해 관리된다.&lt;/strong&gt; 이는 &lt;a href=&quot;https://haon.blog/spring/argument-resolver/&quot;&gt;스프링에서 Argument Resolver 기반 커스텀 어노테이션으로 인증 책임을 분리해보자! (vs Interceptor)&lt;/a&gt; 에서 다루었던 &lt;code class=&quot;language-text&quot;&gt;Interceptor&lt;/code&gt; 와도 다소 혼동되는 개념일 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Filter&lt;/code&gt; 는 &lt;code class=&quot;language-text&quot;&gt;Interceptor&lt;/code&gt; 와 관리되는 주체가 다르다. 인터셉터가 일반적인 스프링 컨테이너에 의해서 관리된다면, Filter 는 서블릿 컨테이너라는 주체에 의해 관리된다. 이로써 또 한가지 특징을 알아낼 수 있는데, Filter 는 인터셉터보다 더 앞단에서 요청을 먼저 처리한다. 이 둘은 &lt;code class=&quot;language-text&quot;&gt;DispatcherServlet&lt;/code&gt; 전후로 처리 작업을 지원한다. &lt;code class=&quot;language-text&quot;&gt;DispatcherServlet&lt;/code&gt; 이 호출되기전에 Filter 가 전처리 작업을, 후처리는 인터셉터가 수행한다.&lt;/p&gt;
&lt;h4&gt;오해하지 말 것&lt;/h4&gt;
&lt;p&gt;나는 Filter 를 사용하는 방식으로 구현했다. 하지만 Filter 외에도 인터셉터에도 MDC 를 구현할 수 있다는 점을 유의하자.&lt;/p&gt;
&lt;p&gt;스프링에서 MDC에 고유 식별값을 만들어 넣을만한 곳은 필터, 인터셉터 등이 있는데 가장 앞단인 필터에 적용하는 것이 좋다. 그리고 필터들 중에서도 가장 먼저 등록되도록 해주면 좋을 것이다.&lt;/p&gt;
&lt;h3 id=&quot;servlet-filter-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#servlet-filter-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;servlet filter 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Servlet Filter 구현&lt;/h3&gt;
&lt;p&gt;그러면 Filter 를 구현할 떄 어떤 방식으로 구현해야할까? 이야기했듯이 MDC 는 각 쓰레드별 고유 식별값(ID)을 저장해야한다. 고유 ID 를 부여하는 방법엔 여러가지가 있겠지만, 나는 &lt;code class=&quot;language-text&quot;&gt;UUID&lt;/code&gt; 를 활용하여 부여하겠다.&lt;/p&gt;
&lt;p&gt;구현 방법은 어렵지 않다. 우선 &lt;code class=&quot;language-text&quot;&gt;Filter&lt;/code&gt; 인터페이스를 구현한 구현체를 만들고 Bean 으로 등록해준다. 이 안의 &lt;code class=&quot;language-text&quot;&gt;doFitler()&lt;/code&gt; 에서 &lt;code class=&quot;language-text&quot;&gt;MDC.put()&lt;/code&gt; 을 사용해서 UUID 를 넣어준다. 이렇게 구현되면 가장 앞단에서 요청을 처리하는 Servlet Filter 구현체인 MDCFilter 에 의해 요청이 처리되고, MDC 공간내의 Key 값에 UUID 식별자 값이 저장된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Ordered&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HIGHEST_PRECEDENCE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MDCLoggingFilter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;doFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletRequest&lt;/span&gt; servletRequest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletResponse&lt;/span&gt; servletResponse&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FilterChain&lt;/span&gt; filterChain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServletException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UUID&lt;/span&gt; uuid &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;randomUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;MDC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;req_id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; uuid&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        filterChain&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;doFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servletRequest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; servletResponse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;MDC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;주의할 점은 &lt;code class=&quot;language-text&quot;&gt;doFilter()&lt;/code&gt; 가 끝나서 나오면 clear 를 해준다는 것이다. Spring MVC 는 &lt;code class=&quot;language-text&quot;&gt;쓰레드 풀&lt;/code&gt; 에 쓰레드를 여럿 만들어두고, 유입된 요청을 처리하고 다시 쓰레드 풀에 반납된다. 그런데 MDC 는 각 쓰레드 별로 &lt;code class=&quot;language-text&quot;&gt;ThreadLocal&lt;/code&gt; 을 사용하므로, 요청이 완료될 때 &lt;code class=&quot;language-text&quot;&gt;clear()&lt;/code&gt; 를 해주지 않으면 다른 요청이 이 쓰레드를 재사용할 때 이전 데이터가 남아있을 수 있다. 마치 통장 비밀번호 같은 데이터를 MDC 에 저장하고 삭제하지 않을 경우 다른 사용자가 조회할 수 있는 상황과 유사하다.&lt;/p&gt;
&lt;h3 id=&quot;로그에-고유-id-출력하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EA%B7%B8%EC%97%90-%EA%B3%A0%EC%9C%A0-id-%EC%B6%9C%EB%A0%A5%ED%95%98%EA%B8%B0&quot; aria-label=&quot;로그에 고유 id 출력하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로그에 고유 ID 출력하기&lt;/h3&gt;
&lt;p&gt;Filter 를 구현했다면 고유 UUID 를 출력해보자. 간단히 &lt;code class=&quot;language-text&quot;&gt;INFO&lt;/code&gt; 레벨로 출력해본다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; uuid &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MDC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;req_id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;[${req_id}] $name 회원 가입 요청 받음&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;logback-spring.xml&lt;/h4&gt;
&lt;p&gt;간단히 xml 파일을 통해 logback 을 설정하여 모든 로그에 MDC 맥락을 적용할 수도 있을것이다. 아래와 같이 작성해보자. xml 에 대한 상세한 설정 방법은 추가 학습이 필요하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token prolog&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;configuration&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scan&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;springProfile&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;prod&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;include&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;org/springframework/boot/logging/logback/defaults.xml&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;

        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;include&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stdout-appender.xml&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;include&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;info-appender.xml&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;include&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;error-appender.xml&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- ... --&gt;&lt;/span&gt;

        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;root&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;appender-ref&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;STDOUT&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;appender-ref&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;FILE-INFO&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;appender-ref&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;FILE-ERROR&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- ... --&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;root&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;springProfile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;configuration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;MDC는 단순히 콘솔에서만 출력되고 휘발되는 것이 아니라 Appender에 따라 파일, 데이터베이스등 다양한 소스에 로그 메시지와 함께 맥락을 남길 수 있으므로 유용하게 사용될 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;코루틴 (아직 멀었지만 언젠가는 🤣)&lt;/li&gt;
&lt;li&gt;FilterChain&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/266&quot;&gt;https://mangkyu.tistory.com/266&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://yeonbot.github.io/java/ThreadLocal/#threadlocal&quot;&gt;https://yeonbot.github.io/java/ThreadLocal/#threadlocal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/slf4j-mapped-diagnotics-context/#MDC-%EC%A0%84%ED%8C%8C%EA%B0%80-%EC%95%88%EB%90%98%EB%8A%94-%EC%BC%80%EC%9D%B4%EC%8A%A4&quot;&gt;https://hudi.blog/slf4j-mapped-diagnotics-context/#MDC-%EC%A0%84%ED%8C%8C%EA%B0%80-%EC%95%88%EB%90%98%EB%8A%94-%EC%BC%80%EC%9D%B4%EC%8A%A4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@yukeon97/spring-filter-interceptor-aop-%EC%A0%95%EB%A6%AC-247125b4acac&quot;&gt;https://medium.com/@yukeon97/spring-filter-interceptor-aop-%EC%A0%95%EB%A6%AC-247125b4acac&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@soyeon207/Spring-Filter-Interceptor-AOP&quot;&gt;https://velog.io/@soyeon207/Spring-Filter-Interceptor-AOP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[스프링에서 Argument Resolver 기반 커스텀 어노테이션으로 인증 책임을 분리해보자!]]></title><description><![CDATA[현재 포스트는 harmony 팀 기술 블로그에 게시된 글 입니다. Argument Resolver 를 사용하지 않는 상황 현재 우리 팀의 프로젝트의 인증/인가 도메인을 맡아 개발하면서 ArgumentResolver…]]></description><link>https://haon.site/haon/spring/argument-resolver/</link><guid isPermaLink="false">https://haon.site/haon/spring/argument-resolver/</guid><pubDate>Sun, 28 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;현재 포스트는 &lt;a href=&quot;https://kakaotech-harmony.netlify.app/backend/argument-resolver/&quot;&gt;harmony 팀 기술 블로그&lt;/a&gt;에 게시된 글 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;argument-resolver-를-사용하지-않는-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#argument-resolver-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EC%83%81%ED%99%A9&quot; aria-label=&quot;argument resolver 를 사용하지 않는 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Argument Resolver 를 사용하지 않는 상황&lt;/h2&gt;
&lt;p&gt;현재 우리 팀의 프로젝트의 인증/인가 도메인을 맡아 개발하면서 ArgumentResolver 에 기반한 커스텀 어노테이션을 개발했다. 이 어노테이션에 대한 구현의 이유(근거), 왜 사용되는지에 대한 생각의 정교화를 위해 글을 작성한다.&lt;/p&gt;
&lt;p&gt;백문이불어일타. 직접 코드를 작성해보면서 ArgumentResolver 에 대해 이해해보자.&lt;/p&gt;
&lt;p&gt;Argument Resolver 의 활용 유무는 프레젠테이션 계층에서 가장 차이를 보인다. 일반적인 상황이라면, 즉 Argument Resolver 에 기반한 커스텀 어노테이션이 존재하지 않는 경우라면 어떻게 클라이언트의 요청을 처리할까? 당연하게도 컨트롤러에서 &lt;code class=&quot;language-text&quot;&gt;@RequestBody&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@RequestParam&lt;/code&gt; 등 이미 스프링내에 정의된 편리한 어노테이션으로 가변적인 변수를 바인딩시킬 수 있을 것이다. 예를들어 아래처럼 사용자의 정보를 &lt;code class=&quot;language-text&quot;&gt;LoginMember&lt;/code&gt; 라는 객체로 바인딩한다고 해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/me&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;about&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestBody&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; accessToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JwtTokenProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseLong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jwtTokenProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPayload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accessToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;LoginMember&lt;/span&gt; loginMember &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoginMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ... (service 로직 수행)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 문제점이 하나있다. &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 사이에 있는 로직이 여러 컨트롤러에서 중복되는 로직이 라고 생각해보자. 가령 위와 같이 Jwt 의 Payload 에서 유저의 정보를 추출하는 로직은 대부분의 컨트롤러에서 구현해야하는 로직일 것이며, 모든 컨트롤러에서 직접 매번 구현하자니 중복 코드가 발생한다.&lt;/p&gt;
&lt;h3 id=&quot;중복-로직-제거-데이터-바인딩&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A4%91%EB%B3%B5-%EB%A1%9C%EC%A7%81-%EC%A0%9C%EA%B1%B0-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B0%94%EC%9D%B8%EB%94%A9&quot; aria-label=&quot;중복 로직 제거 데이터 바인딩 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;중복 로직 제거, 데이터 바인딩&lt;/h3&gt;
&lt;p&gt;우리는 프레젠테이션 계층, 즉 클라이언트의 요청을 처리하는 모든 컨트롤러의 중복 코드를 제거하고 싶다. 정확히는 HTTP Header, 세션, 쿠키 등 직접적이지 않은 방식으로 데이터를 바인딩하고, 중복 로직을 제거하고 싶다. 이때 사용하는 것이 바로 Argument Resolver 이다. &lt;strong&gt;Argument Resolver 를 사용하면 컨트롤러 메소드의 파라미터 중 특정 조건에 맞는 파라미터가 있다면, 요청에 들어온 값을 원하는 객체를 만들어 바인딩해줄 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;다시말해, 특정 요청으로 들어온 값을 원하는 객체로 생산해내는 작업을 Argument Resolver 에게 위임할 수 있다. 위의 경우 클라이언트 요청으로 전달받은 Dto 를 LoginMember 라는 객체로 바인딩시키는 것이다. 또한 이 작업의 책임을 제 3자인 Argument Resolver 구현체에게 인가함으로써 많은 컨트롤러의 중복 로직을 제거할 수 있게된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;argument-resolver-구헌체&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#argument-resolver-%EA%B5%AC%ED%97%8C%EC%B2%B4&quot; aria-label=&quot;argument resolver 구헌체 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Argument Resolver 구헌체&lt;/h2&gt;
&lt;p&gt;ArgumentResolver 의 혜택을 누리기 위해선 이에 대한 구현체가 핋요하다. 간단하게 앞선 특정 유저의 정보를 조회하는 API 를 가정하고. ArgumentResolver 를 적용해보자. 즉, 우리의 최종적인 목적은 &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 사이의 로직을 제 3자인 Argument Resolver 기반 구현체에게 책임을 인기하여, &lt;code class=&quot;language-text&quot;&gt;LoginMember&lt;/code&gt; 객체를 자동 생성하는 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/me&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;about&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestBody&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; accessToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JwtTokenProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseLong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jwtTokenProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPayload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accessToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;LoginMember&lt;/span&gt; loginMember &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoginMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ... (service 로직 수행)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또한 UserRequest 라는 DTO 는 아래와 같이 정의되었음을 가정해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRequest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;handlermethodargumentresolver-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#handlermethodargumentresolver-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;handlermethodargumentresolver 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HandlerMethodArgumentResolver 구현&lt;/h3&gt;
&lt;p&gt;Argument Resolver 를 만들기 위해선 스프링 내에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;HandlerMethodArgumentResolver&lt;/code&gt; 인터페이스를 구현해야한다. 이 인터페이스를 구현한 클래스가 바로 Argument Resolver 가 되는 것이다. 인터페이스는 아래 2가지 메소드를 구현하도록 명시하고 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;supportsParameter() : 요청받은 메소드의 파라미터에 원하는 어노테이션이 붙어있는지 확인하고, 원하는 어노테이션을 포함하고 있다면 true 를 리턴한다.&lt;/li&gt;
&lt;li&gt;resolveArgument() : supportsParameter() 에서 true 를 리턴받은 경우, 즉 특정 어노테이션이 붙어있는 어느 메소드가 존재하는 경우 파라미터가 원하는 형태로 정보를 바인딩하여 리턴하는 메소드다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;나는 이에 대한 구현체를 아래와 같이 구현했다. &lt;code class=&quot;language-text&quot;&gt;JwtTokenProvider&lt;/code&gt; 는 jwt 토큰을 추출하는 객체이며, &lt;code class=&quot;language-text&quot;&gt;BearerTokenExtractor&lt;/code&gt; 는 토큰에 대한 유효성을 검증하는, 즉 &lt;code class=&quot;language-text&quot;&gt;Bearer&lt;/code&gt; 가 명시되어있는지 검증하는 객체다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthArgumentResolver&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerMethodArgumentResolver&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JwtTokenProvider&lt;/span&gt; jwtTokenProvider&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BearerTokenExtractor&lt;/span&gt; bearerTokenExtractor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthArgumentResolver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JwtTokenProvider&lt;/span&gt; jwtTokenProvider&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BearerTokenExtractor&lt;/span&gt; bearerTokenExtractor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jwtTokenProvider &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; jwtTokenProvider&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bearerTokenExtractor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bearerTokenExtractor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;supportsParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MethodParameter&lt;/span&gt; methodParameter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; methodParameter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasParameterAnnotation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AuthPrincipal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resolveArgument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MethodParameter&lt;/span&gt; methodParameter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                  &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelAndViewContainer&lt;/span&gt; modelAndViewContainer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                   &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NativeWebRequest&lt;/span&gt; nativeWebRequest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                  &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebDataBinderFactory&lt;/span&gt; webDataBinderFactory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nativeWebRequest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getNativeRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BadRequestException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; accessToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bearerTokenExtractor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;extractValidAccessToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseLong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jwtTokenProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPayload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accessToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoginMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;supportsParameter&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;supportsParameter()&lt;/code&gt; 를 보면 파라미터로 &lt;code class=&quot;language-text&quot;&gt;MethodParameter&lt;/code&gt; 를 전달받는다. 우리가 가정한 현 상황에선 컨트롤러의 &lt;code class=&quot;language-text&quot;&gt;about()&lt;/code&gt; 메소드의 파라미터인 &lt;code class=&quot;language-text&quot;&gt;UserRequest&lt;/code&gt; 가 MethodParameter 에 바인딩 될 것이다. 즉, 요청받은 메소드 about 의 파라미터인 UserRequest 에 어노테이션이 붙어있는지 확인한다.&lt;/p&gt;
&lt;p&gt;아직은 UserRequest 에 어노테이션을 명시한것이 없기 떄문에 &lt;code class=&quot;language-text&quot;&gt;suppertsParameter()&lt;/code&gt; 는 false 를 리턴하게 될 것이다. 우리는 true 를 리턴하도록 해야 Argument Resolver 가 원활히 동작하도록 만들 수 있다. 따라서 향후 커스텀 어노테이션 개발의 필요성을 인지하고 넘어가자. 또한 나의 경우 &lt;code class=&quot;language-text&quot;&gt;AuthPrincipal&lt;/code&gt; 이라는 커스텀 어노테이션을 만들었다.&lt;/p&gt;
&lt;h4&gt;resolveArgument&lt;/h4&gt;
&lt;p&gt;supportsParameter 로 부터 true 를 리턴받았다면, 즉 커스텀 어노테이션을 개발 후 컨트롤러 메소드에 파라미터로 명시해줬다면 파라미터가 원하는 형태로 정보를 바인딩하여 리턴할 수 있게된다. 나는 커스텀 어노테이션을 개발 후 about 메소드의 파라미터가 &lt;code class=&quot;language-text&quot;&gt;LoginMember&lt;/code&gt; 로 바인딩되도록 구현했다.&lt;/p&gt;
&lt;h3 id=&quot;커스텀-어노테이션-개발하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EA%B0%9C%EB%B0%9C%ED%95%98%EA%B8%B0&quot; aria-label=&quot;커스텀 어노테이션 개발하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커스텀 어노테이션 개발하기&lt;/h3&gt;
&lt;p&gt;앞선 요구사항에 따라 커스텀 어노테이션을 구현해준다. 이 어노테이션 타입의 객체를 컨트롤러의 요청 파라미터로 명시하면, 이를 명시한 특정 컨트롤러 메소드에서만 객체가 자동으로 Argument Resolver 를 통해 바인딩된다. 이 메소드에선 컨트롤러에서 중복해서 발생하는 JWT payload 추출 로직, 유효성 검증 로직등이 담기게 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ElementType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PARAMETER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Retention&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RetentionPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RUNTIME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthPrincipal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;webmvcconfigurer-에-argument-resolver-를-등록하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webmvcconfigurer-%EC%97%90-argument-resolver-%EB%A5%BC-%EB%93%B1%EB%A1%9D%ED%95%98%EA%B8%B0&quot; aria-label=&quot;webmvcconfigurer 에 argument resolver 를 등록하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;WebMvcConfigurer 에 Argument Resolver 를 등록하기&lt;/h3&gt;
&lt;p&gt;또한 &lt;code class=&quot;language-text&quot;&gt;WebMvcConfigurer&lt;/code&gt; 를 구현한 클래스를 생성 후, 앞서 만든 Argument Resolver 를 등록해주자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthResolverConfig&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebMvcConfigurer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthArgumentResolver&lt;/span&gt; authArgumentResolver&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthResolverConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthArgumentResolver&lt;/span&gt; authPrincipalArgumentResolver&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;authArgumentResolver &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; authPrincipalArgumentResolver&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addArgumentResolvers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HandlerMethodArgumentResolver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; argumentResolvers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        argumentResolvers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;authArgumentResolver&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;컨트롤러에-argument-resolver-적용하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC%EC%97%90-argument-resolver-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; aria-label=&quot;컨트롤러에 argument resolver 적용하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨트롤러에 Argument Resolver 적용하기&lt;/h3&gt;
&lt;p&gt;마지막으로 컨트롤러에 커스텀 어노테이션 &lt;code class=&quot;language-text&quot;&gt;@AuthPrincipal&lt;/code&gt; 이 명시된 객체를 메소드 파라미터로 명시해주자. 이로써 여러 컨트롤러에서 중복되어 발생하던 인증/인가 중복 로직이 깔끔하게 제거되었다. 이 어노테이션이 파라미터로 명시된 컨트롤러 메소드에서만 자동으로 객체가 Argument Resolver 를 통해 바인딩된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/about&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MemberResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;about&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@AuthPrincipal&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoginMember&lt;/span&gt; loginMember&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;MemberResponse&lt;/span&gt; memberResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; memberService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;loginMember&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;memberResponse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 함으로써 검증의 책임을 컨트롤러에게 책임지지않고 제 3자에게 위임할 수 있게 되었다. 어노테이션만 붙여주면 유효한 토큰을 사용하는 것이 검증된 사용자가 필요한 정보를 가지고 필요한 객체로 바인딩되니 매우 깔끔하고 편리한 코드로 개선되었다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;스프링-인터셉터-spring-interceptor-와의-차이점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9D%B8%ED%84%B0%EC%85%89%ED%84%B0-spring-interceptor-%EC%99%80%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; aria-label=&quot;스프링 인터셉터 spring interceptor 와의 차이점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링 인터셉터 (Spring Interceptor) 와의 차이점&lt;/h2&gt;
&lt;p&gt;미트윈 프로젝트에서 Argument Resolver 를 학습하고 구현하면서 가장 비교되고 혼동되는 대상이 스프링의 인터셉터였다. 일반적으로 인증에 대한 로직을 Arguement Resolver 와 Interceptor 를 함께 활용하여 구현하는 경우가 많은듯했다. 또는 세부 요구사항에 따라 Interceptor 만을 구현하는 경우도 있는듯하다. 그럼에도 나는 Interceptor 를 추가적으로 활용하지 않고 Argument Resolver 만을 활용하여 인가의 책임을 떠넘기도록 구현했다.&lt;/p&gt;
&lt;h3 id=&quot;interceptor&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#interceptor&quot; aria-label=&quot;interceptor permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Interceptor&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0eb051c35b5f35e0748b73d5f4f00c39/9b1e2/image-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.963190184049076%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACCklEQVR42n2TW09TQRSF+ZXG+At88ME3HzHwYEQfTIyJMRESImkMXqDcBSOXCghGhRBBK6aW0mLh1LbnPnNmPvcp1GCpTrIeJpP5Zu299vTQscIwbMkYQ2ISrLUkSYKWvZbzC5JzI2qvnvOw9HLNqeGcOC2grwIOfx3hxiGJ6xDlc+jCe0xlF13aQRe3UeU9Yh13B6YQa42QxV36+toqZniY+PgE7RQJF4dw5gdpLAxiP2RhbxGTXyVWUXdgWlrZMVRrAk6B2XHo6yMullACZPYeZmmIZO0pejVDsvEM+/l1d4e+79Fo1MmuW6bXFW4UoGam4c4A7vcCzYNd3MfX8MZuYzdG8ecf4c8+wIjTWKsuQC8FNhnLBUys1PGikPr4GNXeXur5fRqFHU4eXqX54hbmYxZ3JUNz6Qnh1hw6Ud0duq7L9KZUtiEOw4BwcgKvvx91WEbXDmHuPuZtBn9zBjf3XKCjRNtvUN2ArZQllFozoe6e9TAteWBAEi2jgp/YgvT0x0tUfoTwawbzbQRzMCUlR/9LWfIVJe1QbvaiDyok/r6kehc+XSdcvkz11RV07hJ264akHLYtdQItWgtMlA5slP9CtLJM8/gYzz0irr5DVRZQpUmC4pQ4nyCuLKL+NYft4W4r/QGeUvjSTz+QH6QMJn24Q/bc/QvADvqpzsr5sz8b/lPZv678BsHBhtYJHf+cAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;image 3&quot;
        title=&quot;&quot;
        src=&quot;/static/0eb051c35b5f35e0748b73d5f4f00c39/a6d36/image-3.png&quot;
        srcset=&quot;/static/0eb051c35b5f35e0748b73d5f4f00c39/222b7/image-3.png 163w,
/static/0eb051c35b5f35e0748b73d5f4f00c39/ff46a/image-3.png 325w,
/static/0eb051c35b5f35e0748b73d5f4f00c39/a6d36/image-3.png 650w,
/static/0eb051c35b5f35e0748b73d5f4f00c39/e548f/image-3.png 975w,
/static/0eb051c35b5f35e0748b73d5f4f00c39/3c492/image-3.png 1300w,
/static/0eb051c35b5f35e0748b73d5f4f00c39/9b1e2/image-3.png 1880w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;스프링 인터셉터는 &lt;code class=&quot;language-text&quot;&gt;서블릿 필터(servlet filter)&lt;/code&gt; 와 같이 웹과 관련한 공통 관심 사항을 효과적으로 해결할 수 있는 기술이다. 이 점에서 AOP 와 같이 횡단 관심사를 깔끔하게 처리할 수 있다는 특징을 지닌다.&lt;/p&gt;
&lt;p&gt;그럼에도 서블릿 필터와 스프링 인터셉터는 다소 다른 개념이다. &lt;code class=&quot;language-text&quot;&gt;서블릿 필터&lt;/code&gt; 는 서블릿이 제공하는 기술이라면, 스프링 인터셉터는 &lt;code class=&quot;language-text&quot;&gt;스프링 MVC&lt;/code&gt; 가 제공하는 기술이다. &lt;strong&gt;스프링 인터셉터는&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;DispatcherServlet&lt;/code&gt; &lt;strong&gt;과 컨트롤러 이 둘 사이에서 컨트롤러 호출 직전에 호출된다는 특징을 지닌다.&lt;/strong&gt; 즉, 클라리언트가 스프링부트 서버의 한 API 를 호출한다면 &lt;strong&gt;HTTP 요청 -&gt; WAS -&gt; Filter -&gt; DispatcherServlet -&gt; 스프링 인터셉터 -&gt; 컨트롤러&lt;/strong&gt; 순으로 요청이 넘어가게 된다.&lt;/p&gt;
&lt;h3 id=&quot;prehandle-posthandle&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#prehandle-posthandle&quot; aria-label=&quot;prehandle posthandle permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;preHandle, postHandle&lt;/h3&gt;
&lt;p&gt;더 정확히는 바로 위에서 설명한 인터셉터는 컨트롤러 호출 전에 대해서만 다룬 것이다. 사실 엄밀히 말해, 인터셉터는 컨트롤러 호출 전과 호춯 후에 모두 호출될 수 있다. 이는 &lt;code class=&quot;language-text&quot;&gt;Handler Interceptor&lt;/code&gt; 인터페이스의 &lt;code class=&quot;language-text&quot;&gt;preHandle()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;postHandle()&lt;/code&gt; 을 구현함으로써 가능해진다. 가령 아래와 같이 구현하면 컨트롤러 호출전에 수행할 인증(authentication) 로직을 미리 수행할 수 있다. 미리 수행할 로직은 &lt;code class=&quot;language-text&quot;&gt;preHandle()&lt;/code&gt; 에 구현하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoginInterceptor&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HandlerInterceptor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;preHandle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; requestURI &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestURI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;[interceptor] requestURI : &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; requestURI&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ... (Authentication 로직 구현)&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// false -&gt; 이후에 진행을 하지 않는다.&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;postHandle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; handler&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token class-name&quot;&gt;ModelAndView&lt;/span&gt; modelAndView&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;[interceptor] postHandle&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;afterCompletion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpServletRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; handler&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; ex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;[interceptor] afterCompletion&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;차이점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; aria-label=&quot;차이점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;차이점&lt;/h3&gt;
&lt;p&gt;이러한 인터셉터는 Argument Resolver 와 무슨 차이가 있을까? 이 둘은 &lt;code class=&quot;language-text&quot;&gt;호출되는 시점&lt;/code&gt; 에서 차이를 보인다. Argument Resolver 가 특정 컨트롤러에 요청이 들어왔을 때 동작하는 것이라면, 인터셉터는 컨트롤러가 요청되기 이전 단계에 호출된다.&lt;/p&gt;
&lt;p&gt;또한 &lt;code class=&quot;language-text&quot;&gt;특정 객체의 반환 가능 여부&lt;/code&gt; 에서도 차이를 보인다. ArgumentResolver 는 인터셉터 이후에 도작을 하며, 어떠한 요청이 컨트롤러에 들어왔을 때, 요청에 들어온 값으로부터 원하는 객체를 반환하는 역할을 수행한다. 반면 인터셉터는 실제 컨트롤러가 실행되기전에 요청을 가로채며, &lt;strong&gt;특정 객체를 반환할 수 없다.&lt;/strong&gt; 또한 오직 boolean 혹은 void 반환 타입만 존재한다.&lt;/p&gt;
&lt;h3 id=&quot;인터셉터를-사용하지-않은-이유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%ED%84%B0%EC%85%89%ED%84%B0%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%80-%EC%9D%B4%EC%9C%A0&quot; aria-label=&quot;인터셉터를 사용하지 않은 이유 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인터셉터를 사용하지 않은 이유&lt;/h3&gt;
&lt;p&gt;결론적으로 Argument Resolver 구현만으로도 충분히 인증/인가에 대한 효과적인 로직을 충분히 수행할 수 있다는 판단으로 인터셉터를 굳이 도입하지 않았다. (명확한 이유, 근거없는 기술 도입은 되려 의미없는 행위라고 생각하니깐 🙂)&lt;/p&gt;
&lt;p&gt;현재 인증이 필요한 로직 대부분에서 유저 id 값을 필요로 하기 때문에 굳이 Argument Resolver 와 Interceptor 두 곳에 나눌 필요가 없다고 판단했다. 다만 추가적으로 interceptor가 필요한 시점에 분리하는 것도 좋을 것 같다는 결론을 내렸다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-05-24-spring-interceptor/&quot;&gt;https://tecoble.techcourse.co.kr/post/2021-05-24-spring-interceptor/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://steady-coding.tistory.com/601&quot;&gt;https://steady-coding.tistory.com/601&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/spring-argument-resolver/#WebMvcConfigurer%EC%97%90%EC%84%9C-Argument-Resolver-%EB%93%B1%EB%A1%9D&quot;&gt;https://hudi.blog/spring-argument-resolver/#WebMvcConfigurer%EC%97%90%EC%84%9C-Argument-Resolver-%EB%93%B1%EB%A1%9D&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/250&quot;&gt;https://mangkyu.tistory.com/250&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[개발자로 살아가면서 불안과 가면 증후군, 메타인지 학습법을 가꾸는 방법]]></title><description><![CDATA[…]]></description><link>https://haon.site/회고/unrest-behavior/</link><guid isPermaLink="false">https://haon.site/회고/unrest-behavior/</guid><pubDate>Wed, 13 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;오늘 할 일도 다 끝났고, 꼭 적어보고 싶었던 내용을 적어본다.&lt;/p&gt;
&lt;p&gt;항상 개발자로써 성장하고 싶은 마음은 한가득이다. 분명 열심히 살고 난 이렇게나 많이 성장했어! 라는 객관적인 지표가 여럿 있음에도 불구하고 불안, 걱정은 내 머릿속을 지배하고 있다. 그럼에도 평소 &lt;code class=&quot;language-text&quot;&gt;&quot;불안&quot;&lt;/code&gt; 을 자주 느껴서 내 평소 할 일에 집중하지 못할떄가 많다. &lt;strong&gt;나는 왜 못할까. 왜 이것밖에 성장하지 못했을까. 다른 동료들은 저렇게 잘 하는데..&lt;/strong&gt; 라는 악순환이 돈다. 타인과 나를 비교하기 시작하는 습관도 생겼다.&lt;/p&gt;
&lt;p&gt;난 개발자로써 커리어, 성장에 정말 욕심이 많고, 정말 최선을 다해 살아가고있다. (프로그래밍을 너무 좋아하기도 하고) 단순히 맹목적인 취업 목표로 주구장창 알고리즘만 올인하여 푼다거나, 서비스에 이것저것 기능을 급급하게 추가하려고 애먹는 사람이 아니다. 항상 느끼는 것이지만 프로그래밍을 좋아하고 노력하기 더해진 사람과, 단순 취업을 목표로 애타게 억지로 공부하는 사람과의 성장 속도는 정말 확연하게 다르다.&lt;/p&gt;
&lt;h3 id=&quot;그럼에도-불구하고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%9F%BC%EC%97%90%EB%8F%84-%EB%B6%88%EA%B5%AC%ED%95%98%EA%B3%A0&quot; aria-label=&quot;그럼에도 불구하고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그럼에도 불구하고&lt;/h3&gt;
&lt;p&gt;그럼에도 나는 괜한 불안함에 휩싸여 평소 내 할일에 집중하지 못하고 있다. 불확실성, 두려움, 불안에 대한 생각이 한 번 시작되면 악순환에 빠져서 계속 빠져나오지 못한다. 최근에도 느꼈지만, 학교 전공 수업을 듣다가도 한 번 다른 생각으로 빠지면 다시 교수님의 말씀에 집중하기가 정말 힘들었다. 당장 오늘만 해도 OS 수업을 들으면서 절반은 집중하지 못했다.&lt;/p&gt;
&lt;p&gt;또한 최선을 다해, 남들보다 쉬지않고 노력했음에도 불구하고 &lt;strong&gt;&quot;운이 좋아서&quot;, 난 밑천이 드러나면 진짜 보잘것 없는 사람인데&quot;&lt;/strong&gt; 라는 생각이 자주 든다. 분명 노력을 통해 가꾸어낸 정량적 지표를 만들었음에도 내가 한 것이 아닌 기분이 들떄가 많다.&lt;/p&gt;
&lt;p&gt;인터넷에서 좋은 영상들을 여럿 발견했다. &lt;a href=&quot;https://www.youtube.com/watch?v=hJ7w2FqPfBI&quot;&gt;나는 왜 쓸데없는 생각을 많이 해서 삶을 피곤하게 하는 걸까? ⵏ 뇌과학&lt;/a&gt; 를 비롯한 좋은 영상들을 시청했다. 내 나쁜 버릇을 고치고자, 좋은 생활 습관을 실천하기 위해, &lt;strong&gt;글쓰기의 인출(Output) 을 통한 기억의 장기화를 위해 글을 적어본다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;dmn-우리가-멍-때릴때-사실-뇌는-일을-한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dmn-%EC%9A%B0%EB%A6%AC%EA%B0%80-%EB%A9%8D-%EB%95%8C%EB%A6%B4%EB%95%8C-%EC%82%AC%EC%8B%A4-%EB%87%8C%EB%8A%94-%EC%9D%BC%EC%9D%84-%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;dmn 우리가 멍 때릴때 사실 뇌는 일을 한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DMN: 우리가 멍 때릴때, 사실 뇌는 일을 한다.&lt;/h2&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;400&quot; src=&quot;https://www.youtube.com/embed/hJ7w2FqPfBI&quot; title=&quot;나는 왜 쓸데없는 생각을 많이 해서 삶을 피곤하게 하는 걸까? ⵏ 뇌과학&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;우리가 멍 때릴 때, 사실 뇌는 일을 한다. 그때 &lt;code class=&quot;language-text&quot;&gt;DMN&lt;/code&gt;, 즉 디폴트 모드 네트워크라는 것이 활성화된다. 컴퓨터로 따지자면 화면 보호기, 절전모드라고 보면 된다. DMN 이 활성화되면 우리의 마음은 내부에서 방황하기 시작한다.&lt;/p&gt;
&lt;p&gt;DMN 은 여러 정보들을 이리저리 조합해서 자아성찰도 하고, 미래에 대한 계획도 세운다. 걱정거리, 쓸 데 없는 상상에, 잊고만 있었던 흑역사까지도 모조리 꺼내서 &lt;strong&gt;&quot;어떄? 이게 너의 나쁜 모습이야~&quot;&lt;/strong&gt; 하면서 방해거리를 만들어낸다. (마치 &quot;넌 실패작이야&quot; 왜 같은 나쁜 생각을 만들어낸다.) 편히 쉬고 싶은 그 순간 조차도 DMN 이 말을 계속해서 걸어온다. 쉬는시간에도 우리의 자아랑 의식이 &quot;연속적&quot; 인 느낌을 갖게해서 자아 정체성을 유지한다. 그럼 DMN은 자아 정체성만 유지하게 할 것이지, 왜 이렇게 부정적인 생각을 자꾸 떠올리게 할까?&lt;/p&gt;
&lt;h3 id=&quot;인간은-원초적으로-불안해야지-생존이-가능했다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EA%B0%84%EC%9D%80-%EC%9B%90%EC%B4%88%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%B6%88%EC%95%88%ED%95%B4%EC%95%BC%EC%A7%80-%EC%83%9D%EC%A1%B4%EC%9D%B4-%EA%B0%80%EB%8A%A5%ED%96%88%EB%8B%A4&quot; aria-label=&quot;인간은 원초적으로 불안해야지 생존이 가능했다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인간은 원초적으로 &quot;불안해야지&quot; 생존이 가능했다.&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 612px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6bf46c7aa792140307afc85edae151ca/a18e1/homo.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.87116564417178%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAgABBf/EABUBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAHtF5KpSf/EABkQAAIDAQAAAAAAAAAAAAAAAAECABEhEv/aAAgBAQABBQItoYmJq86EqAUP/8QAFREBAQAAAAAAAAAAAAAAAAAAECH/2gAIAQMBAT8Bh//EABURAQEAAAAAAAAAAAAAAAAAABAh/9oACAECAQE/Aaf/xAAZEAACAwEAAAAAAAAAAAAAAAAAARARMVH/2gAIAQEABj8CpdoQrNNj/8QAGhAAAwEBAQEAAAAAAAAAAAAAAAERMUEhYf/aAAgBAQABPyHGcB9X3yDNrTQ9r2nA+EhWn//aAAwDAQACAAMAAAAQnD//xAAXEQADAQAAAAAAAAAAAAAAAAAAATER/9oACAEDAQE/EE8QdP/EABYRAQEBAAAAAAAAAAAAAAAAAAAxAf/aAAgBAgEBPxCmR//EAB0QAQADAQACAwAAAAAAAAAAAAEAESExUYGRodH/2gAIAQEAAT8QU0UYvNLfqMPCNHW58R7lAqlL6lrq3lRmV4iU46cPyI6qFWk//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;homo&quot;
        title=&quot;&quot;
        src=&quot;/static/6bf46c7aa792140307afc85edae151ca/a18e1/homo.jpg&quot;
        srcset=&quot;/static/6bf46c7aa792140307afc85edae151ca/d2f63/homo.jpg 163w,
/static/6bf46c7aa792140307afc85edae151ca/c989d/homo.jpg 325w,
/static/6bf46c7aa792140307afc85edae151ca/a18e1/homo.jpg 612w&quot;
        sizes=&quot;(max-width: 612px) 100vw, 612px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;우리의 조상님인 호모 사피엔스는 약 20 ~ 30만년 전쯤 등장했으며, 약 300 ~ 400만년 전쯤에는 오스트랄로 피테쿠스와 같은 모습이였다. 지금의 우리가 태어나서 존재할 수 있었던 것은, 몇 억년전 원시 조상님들이 생존에 성공했기 때문이다.&lt;/p&gt;
&lt;p&gt;단순히 생각해봐도 우리 조상님들이 지능이 낮은 생물이었다면 당연히 지금까지 인류란 존재는 생존하지 못했을 것이다. 뒤에서 위화감이 느껴지는데 아무런 신경을 쓰지 않았던 조상님들이라면 사자, 하이에나등 야생동물에게 수차례 위협을 당하고, 생존하지 못했을 것이다. &lt;strong&gt;우리 인간은 원초적으로 지능을 높지만 생태계에서 나약한 존재였다. 따라서 부정적 생각, 즉 &lt;code class=&quot;language-text&quot;&gt;불안모드&lt;/code&gt; 가 기본적으로 장착되어 있어야 생존이 가능했다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;불안하고 불편해야지 생존이 가능했다. 그래야 불을 피우고, 발명하고, 무기를 만들고, 동물을 잡고를 반복하여 생태계의 최강 생물이 된 것이다. DMN이 인간의 생존을 가능하게 만들었다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;dmn는-선천적으로-존재하는-것&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dmn%EB%8A%94-%EC%84%A0%EC%B2%9C%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%A1%B4%EC%9E%AC%ED%95%98%EB%8A%94-%EA%B2%83&quot; aria-label=&quot;dmn는 선천적으로 존재하는 것 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DMN는 선천적으로 존재하는 것&lt;/h2&gt;
&lt;p&gt;이 DMN 는 그대로 현재 시대까지 이어져 와서 나쁜 기억만 떠올리게 하고, 흑역사를 생각나게 하는것이다. DMN 는 제 역할을 충실히 수행하고 있었고, 떄문에 우리를 우울하고 불안하게 만드는것이다.&lt;/p&gt;
&lt;p&gt;따라서 우리는 &lt;strong&gt;당연하게도 DMN가 인간에게 선척적으로 존재함을 인정해야한다. 단, 당연하게도 나쁘고 우울한 기억들을 그만 떠오르게 노력해야하고, 현재에 집중해야만 비로소 행복해질 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;하버드 대학의 연구에 따르면, 50%는 일을 할 때 잡 생각을 한다고 한다. DMN가 자주 켜지는 것이다. 또한 그 잡 생각 50%는 나머지 50%보다 덜 행복하다고 한다. 이미 불안이나 우울감으로 고생하는 사람은 DMN 모드가 지나치게 자주 켜지는 상태이다.
이런 상태에서는 부정적 생각이 나도 모르게 떠오르고, 그 생각 떄문에 감정이 다운되고, 그래서 다시 부정적 생각이 떠오르는 악순환 구조가 생기는 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;현재에-집중해야한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%98%84%EC%9E%AC%EC%97%90-%EC%A7%91%EC%A4%91%ED%95%B4%EC%95%BC%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;현재에 집중해야한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;현재에 집중해야한다&lt;/h2&gt;
&lt;p&gt;이런 DMN의 기능을 끌 (OFF 시킬) 유일한 방법은 지금의 눈 앞에 일에 &lt;code class=&quot;language-text&quot;&gt;집중&lt;/code&gt; 하는 것이다. 책을 읽을 때 종이의 질감, 책을 넘기는 느낌이나 내가 쉬는 숨의 느낌, 폐의 느낌, 이런 것들에 집중하는 것이다.&lt;/p&gt;
&lt;p&gt;우리의 뇌는 2가지 부분으로 나뉜다고 한다. 하나는 수동모드 뇌, 다른 하나는 메크로 뇌이다. 사람은 자기 행동을 거의 90% 정도는 자동화 모드로 돌리고 있는 것인데, &quot;지식은 날리지&quot; 님은 그것을 자동으로 DMN 모드가 발생한다면 의식적으로 OFF 하기 위해 노력해 볼 것을 권장한다. 그래야지 DMN 모드가 OFF 될 수 있다고 한다. 몰론 노력하는 그 와중에도 DMN이 자동을 켜저서 나쁜 기억이 다시 떠올를 수 있지만, 최대한 무시하기 위해 노력해야한다. 떠오른 그 나쁜 생각에 대해 또 생각의 늪에 빠져서 이런저런 생각을 하면 안된다. &quot;응 왔니?&quot; 정도의 인사 정도는 괜찮다.&lt;/p&gt;
&lt;h3 id=&quot;무시하고-현재에-집중하고-뇌를-변화시키기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B4%EC%8B%9C%ED%95%98%EA%B3%A0-%ED%98%84%EC%9E%AC%EC%97%90-%EC%A7%91%EC%A4%91%ED%95%98%EA%B3%A0-%EB%87%8C%EB%A5%BC-%EB%B3%80%ED%99%94%EC%8B%9C%ED%82%A4%EA%B8%B0&quot; aria-label=&quot;무시하고 현재에 집중하고 뇌를 변화시키기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;무시하고, 현재에 집중하고, 뇌를 변화시키기&lt;/h3&gt;
&lt;p&gt;무시하기, 그리고 현재에 집중하기를 의식적으로 훈련하고, 노력하다보면 뇌는 변화하기 시작한다. DMN 모드를 점점 약화하는 쪽으로 변화하기 시작한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;현대-사회는-부정적-정보가-넘쳐난다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%98%84%EB%8C%80-%EC%82%AC%ED%9A%8C%EB%8A%94-%EB%B6%80%EC%A0%95%EC%A0%81-%EC%A0%95%EB%B3%B4%EA%B0%80-%EB%84%98%EC%B3%90%EB%82%9C%EB%8B%A4&quot; aria-label=&quot;현대 사회는 부정적 정보가 넘쳐난다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;현대 사회는 부정적 정보가 넘쳐난다&lt;/h2&gt;
&lt;p&gt;또한 우리는 부정정 정보의 홍수 속에서 살고있다. 그런데 &lt;code class=&quot;language-text&quot;&gt;부정적 정보&lt;/code&gt; 가 너무나도 많다. 그래서 정보를 차라리 적으면 적을수록 좋다. 뉴스만 인터넷만 봐도 절대로 세상이 좋아졌다는 긍정적인 이야기는 찾아볼 수 없다. 이들은 아래와 같은 2가지 원인들로 인해 사회에는 부정적인 정보가 가득한 것이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;생존자 편향 (Survivor Bias)&lt;/li&gt;
&lt;li&gt;부정성 편향 (Negativity Bias)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/67e68a60a51d381a7ea9a40c72c46f33/081d5/sns.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70.5521472392638%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABYlAAAWJQFJUiTwAAADiklEQVR42jWS2W8bVRTG5w8A0Sf+ABD/Bw8gXqh4QDxWDREPCCG1CPpSBAiEkFCbUGhFqkiUAklLNocsduK1sZ14GW8zcTzxmvES2+M1dpw9TX5cW8qRvjlz58757ne/cySzT2F5IzbAkoDpRZAFTwiLLyagYPErg7wi8mpQxbwRYckjY/bKLLsDmFaceGQF63qIRW8EaXU9jM0TYCOi4lfipApFNsLiB78qoGATWBHkiy4f83Yvq6KwVCqy3ztgzWpm5uk4O5kUiVSWxbUgkqwmMGpVlM0t5LUAuqyiiU1bQGXRuc6y+Da1ZGXB4WXe5sG85qfTOyQju7A+uotj7Bu8ljmK1QYr60KhHNfIBzdZH5slOmkh5ZbFaRmcQaHSG2R+1cWqN4DDF8Yb2cIucr3dIRuwEpsaRX52H11TKBt1VtxCoS8aJ7edxtit0Ozu0drvUS6XUeIJvNEE7pDKvMWOxekltJ3DE4kT29LI6QXS2SzxhEapUqFareIJRpFiKR1/VKXRahGWA/w395xCIc9uxSBTaZJvdHGuBwmq2+SMNvl6B61QQ95KkoptMf/7b0zcv4eeTKMks0jJfBlZEHa6XUJBP3NTT8kIk32+AJ99fovbX9whpmVI6mV+vjfKrdtfsWCxoeklYh4/ppFRZh88JptKDnik7Wx+ILdYLLLX6VAqG6hamh+/+5qbH77P69de4+GjMWZMi1x75VWGP7rOTz98S65UQ9GS7IraTm+fqmGgJTNIpWqdivCg0WzSbrfZEd5ki1UcC8/pbTl4+P2X3Lhxk4+Hhrn+7tvkA4skNmwUjSa1VleQpNF1ndJumXypgtTp9ptQpdlso4kTq0aNw5MzEtEgHdXKnU+HGRr6hLfeeJMP3nuHgmca1W2hd3JOOiPGy+agVm+K2/Xo7h8gHR6fcCQIzl5eYNQb2B2uQZf7s5bNZvj78V3qNZ3pBRNj479QLuns7XVpNRvMzprYFPPbj97hMQdHJ0inZ+ecnJ1xcnrKpdhwOR1M//MHcSUsjA6jR0copj3YQ27+ND8hEldIprexmCaYHP+VlpiOcyGmz/Py4gLpQjyu0I/OXpto0ElSWUJ2jNDYfkBm08y/7lkmbU94ZplgxjbNiw0HrVp5UHN5ecmZENQW5FJ/cYUr0v1OnXxGRlNd5GJ/ifcIjrALu39F2GLQOzgYEFyR9eP4+Eg0Z4f/AVi1s6/2SQN/AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;sns&quot;
        title=&quot;&quot;
        src=&quot;/static/67e68a60a51d381a7ea9a40c72c46f33/a6d36/sns.png&quot;
        srcset=&quot;/static/67e68a60a51d381a7ea9a40c72c46f33/222b7/sns.png 163w,
/static/67e68a60a51d381a7ea9a40c72c46f33/ff46a/sns.png 325w,
/static/67e68a60a51d381a7ea9a40c72c46f33/a6d36/sns.png 650w,
/static/67e68a60a51d381a7ea9a40c72c46f33/e548f/sns.png 975w,
/static/67e68a60a51d381a7ea9a40c72c46f33/081d5/sns.png 1264w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;가령 뉴스에 대한민국에 거주하는 평범한 고등학교 학생이 인하대학교 컴퓨터공학과에 합격했다! 라는 소식이 등장한다면 시청자들을 그를 흥미롭게 시청하지 않는다. 아무도 관심이 없을 것이다. 사람들이 관심을 갖는 부정적이고 충격적인 스토리만이 뉴스에 보도되는 것이고, 그를 보곤 사람들은 &lt;strong&gt;&quot;아, 세상은 오늘도 부정적인 일들만 가득하구나. 이런 헬 조선.&quot;&lt;/strong&gt; 라고 하는 것이다.&lt;/p&gt;
&lt;p&gt;SNS 도 똑같다. 사람들은 집에서 잠옷을 입고, 나사빠진 모습으로 라면을 먹는 모습을 인스타그램에 올리지 않는다. 반대로 &lt;strong&gt;SNS 에 자신이 가장 행복할 찰나의 모습, 성공한 모습만을 보여주고 싶어한다. 자랑할것을 올린다.&lt;/strong&gt; 실제 인스타그램을 보면 무슨 행복 한가득이고, 친구들 많은 엄청난 인싸이고, 부자인 사람들만 넘쳐나보인다. &lt;strong&gt;SNS를 보면 &quot;나 빼고 다들 행복하네. 잘 살고있네.&quot;&lt;/strong&gt; 가 되어 나 자신이 초라해진다. 결국 SNS 도 부정적 정보가 된다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;SNS, 뉴스등의 부정적 정보를 줄여야 행복해진다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;난 몇달전부터 인스타그램 활동을 중단했다. 의도치않게 불필요한 타인의 하이라이트를 봐야하는 느낌을 받았기 때문이다. 사실 계정을 아예 삭제한것은 아니기에, 며칠전에 인스타그램 본 계정을 오랜만에 잠깐 들어가 보긴했다. 하지만 약 3분만에 다시 삭제했다. 무의미한 핸드폰 스크롤은 내 행복 지수를 되려 낮추었다. 역시 SNS 활동을 최소화하는 것이 좋았다. 비슷한 이유로 유튜브 쇼츠 시청을 최소화하기 시작했다. 덕분에 평소 활력이 점점 높아지는 느낌을 받는다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;현실에-집중하는-방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%98%84%EC%8B%A4%EC%97%90-%EC%A7%91%EC%A4%91%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;현실에 집중하는 방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;현실에 집중하는 방법&lt;/h2&gt;
&lt;p&gt;한편 현실에 집중하는 방법으로 이런 방법들을 권장된다. 다이어트 도중에 식욕이 넘쳐날 떄 이마를 30초 정도 떄린다면 그 욕구가 줄어든다는 얘기가 있다.&lt;/p&gt;
&lt;h3 id=&quot;악순환의-근본적인-발생-원인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%85%EC%88%9C%ED%99%98%EC%9D%98-%EA%B7%BC%EB%B3%B8%EC%A0%81%EC%9D%B8-%EB%B0%9C%EC%83%9D-%EC%9B%90%EC%9D%B8&quot; aria-label=&quot;악순환의 근본적인 발생 원인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;악순환의 근본적인 발생 원인&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1b1007cd0244808d449143f6063c9d04/d9199/brain.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.03067484662577%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAABYlAAAWJQFJUiTwAAACAUlEQVR42m1Sa28SQRTd3+snv5mYVGNibIqSKG2R2vAoFlyqBlsJmlprU9I2tgUL5VHRVlhkwS677HOWmZ1lvbDtSg3nw2Rmcs895z4YjLGu6z2xpxvGYDDAJqKW5VxhOBxSON2HTalFiGVZAzwgYzDwUBW5yTVVTYO7hbFDbVXsffm0uZpMJdi3SfZ1bnfHRAZBCLIjhH41LnqiAKoMJKCUggQQ4Q4K3U7b51+YeRzzRbeerOXnN4oPF19FQyHgU9smGJumicdgHM+ie9r0+dKyb+VDMFPwJz6HE29i6czSVv3+08jB3u7IvG17lAnycERvNS4ePVsO53741zYDkcUHczORaCB1UAlkDjOZdU9jOvm8Xp0NrQRTO6v35hZmY+/e5+aTG/Ht05cfc9nsOhToTJJdjudHU+RwPJ6+dbtz985ekM2mj9hk7FIQoCGXQleW+zfI0ED4kmVZ0zTDMCgh1eK3fTZRexHKp7ePixVDV9xQalPos/VvkA5jIsRxTZ7nRVFU1FEcjL1a/65ho/6TK1dK41GPICsyz7fHZV+ZZaAM+xpuVtA/LRUVRSidVAqFPKXEDf3dbkmS5HXnRsM8QCGdDt/vC+XC2eHXfdM0CMEI6S2uOel5Onm8LESShOZ5o1Y+UVX5rFb+021Lovhf5BSy5wp2GWqB5dU01ZmGv14GxCr4lJ6eAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;brain&quot;
        title=&quot;&quot;
        src=&quot;/static/1b1007cd0244808d449143f6063c9d04/a6d36/brain.png&quot;
        srcset=&quot;/static/1b1007cd0244808d449143f6063c9d04/222b7/brain.png 163w,
/static/1b1007cd0244808d449143f6063c9d04/ff46a/brain.png 325w,
/static/1b1007cd0244808d449143f6063c9d04/a6d36/brain.png 650w,
/static/1b1007cd0244808d449143f6063c9d04/d9199/brain.png 960w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;우리 뇌는 감정을 담당하는 &lt;code class=&quot;language-text&quot;&gt;변연계&lt;/code&gt;, 이성적 사고를 담당하는 &lt;code class=&quot;language-text&quot;&gt;신피질&lt;/code&gt;이 서로 경쟁을 한다. 변연계와 신피질 중에서 우리 뇌에게 먼저 말을 거는 쪽이 승리하는 것이다. 그런데 보통은 변연계가 이긴다. 그래서 충동적으로 행동하게 되는 경우가 많다. 재수없는 신피질은 뒤 늦게와서 비난질이나 해 대니, 그렇게 부정적 생각이 늘고, 악순환이 반복되는 것이다.&lt;/p&gt;
&lt;h3 id=&quot;의식을-의도적으로-잠깐-다른곳으로-분산시킨다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%98%EC%8B%9D%EC%9D%84-%EC%9D%98%EB%8F%84%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%9E%A0%EA%B9%90-%EB%8B%A4%EB%A5%B8%EA%B3%B3%EC%9C%BC%EB%A1%9C-%EB%B6%84%EC%82%B0%EC%8B%9C%ED%82%A8%EB%8B%A4&quot; aria-label=&quot;의식을 의도적으로 잠깐 다른곳으로 분산시킨다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;의식을 의도적으로 잠깐 다른곳으로 분산시킨다&lt;/h3&gt;
&lt;p&gt;그런데 이마를 30초 떄리는 행동은 의식을 잠깐 다른곳으로 돌려서 시간을 버는 사이, 신피질이 활동할 수 있게한다. 꼭 이미가 아니더라도 벽이나 책상을 두드려도 무려 60% 는 감소한다고 한다.&lt;/p&gt;
&lt;p&gt;또한 의식을 의도적으로 잠깐 다른곳으로 분산시켜서 집중력을 유지시킬 수도 있다. &lt;strong&gt;중요한 일에 집중해야만 할 떄는 간단한 낙서나 다른 일을 해보는 것이다.&lt;/strong&gt; 우리 뇌는 멀티테스킹도 못하지만, 그렇다고 한가지 일에 집중을 잘하는 것도 아니다. 아주 모순덩어리다. &lt;strong&gt;딱 한가지 일에 집중하면 뇌에 과부하가 와서 금방 지치는데, 낙서와 같은 무의식적으로 할 수 있는 행동은 오히려 집중력을 살짝 분산시켜서 뇌가 금방 지치지 않게 도와주고, 잡생각이 일어나는 것을 방지할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;스트래스, 불안은 인간이라면 누구가 당연히 가지고 있는 것이다. 왜냐면 그게 없었던 사람은 생존이 불가능했기 떄문이다.
따라서 남들이 모두 겪고있는 그 감정을 어떻게 처리하는 것이냐에 따라 인생이 달라진다. 억지 웃음으로 스트래스를 풀어서 행복해지는 방법, &quot;그렇군&quot; 병먹금으로 멘탈 강화하는 방법등 &lt;strong&gt;나만의 효과적인 방법을 찾아내어 머릿속 생각 스위치를 끄고 불안감에서 벗어나야한다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;가면-증후군-impostor-syndrome&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%80%EB%A9%B4-%EC%A6%9D%ED%9B%84%EA%B5%B0-impostor-syndrome&quot; aria-label=&quot;가면 증후군 impostor syndrome permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;가면 증후군 (Impostor syndrome)&lt;/h2&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;400&quot; src=&quot;https://www.youtube.com/embed/_h4zWa7B6f8?si=TmnLR2-lWY-uX1Rz&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;한편 걱정과 불안의 근본적인 원인 중 하나로 임포스터 증후군(Imposter Syndrome), 즉 가면 증후군을 의심해볼 수 있다.
가면 증후군은 생각보다 정말 많은 사람들이 겪고 있는, 심지어 전세계에서 유명한 연예인과 AWS, Google 과 같은 유명 대기업을 다니는 다년차 개발자들도 흔히 겪는 증상이다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;성공이 언제 끝날지 몰라서 두렵고, 진짜 내 모습을 들킬까봐 무서움에 빠진 현상이다.&lt;/strong&gt;
특히 완벽주의의 성향에서 자주 드러나며, 주변에 뛰어난 동료들과 함께 있을 때 자신의 부끄러운 모습을 숨키고자 가면을 쓰는 것이다. 모든 분야에서 부족함 없이 잘 해내야 한다는 압박감에 가면으로 자신을 포장하며 본 모습을 숨겨버린다. 가령 자신이 프로그래밍 분야에서 많은 사람들에게 인정받는 실력가라면 집안일을 잘 못할 수도 있음에도 불구하고, 그 부족한 모습을 내 스스로가 용납하지 못하는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;하나 쯤은 부족하고 모자른 부분이 당연히 있어도 괜찮은데도, 모든 걸 자신이 완벽하게 잘 해내야 한다는 가면속으로 숨겨버린다.&lt;/strong&gt; 심한 경우 작은 실수 하나라도 못하거나, 남에게 부끄러운 모습을 보인다면 &quot;나는 모든 걸 실패했어. 난 능력이 없는 사람인가봐.&quot; 라는 생각과 함께 우울감, 번아웃이 찾아올 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;전문직-학자일수록-가면-증후군에-훨씬-취약하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%EB%AC%B8%EC%A7%81-%ED%95%99%EC%9E%90%EC%9D%BC%EC%88%98%EB%A1%9D-%EA%B0%80%EB%A9%B4-%EC%A6%9D%ED%9B%84%EA%B5%B0%EC%97%90-%ED%9B%A8%EC%94%AC-%EC%B7%A8%EC%95%BD%ED%95%98%EB%8B%A4&quot; aria-label=&quot;전문직 학자일수록 가면 증후군에 훨씬 취약하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전문직, 학자일수록 가면 증후군에 훨씬 취약하다.&lt;/h3&gt;
&lt;p&gt;박서희 의사님은 &lt;strong&gt;특히 자신의 특정 분야의 전문 지식과 기술이 있는 전문직, 또는 학자라면 가면 증후군에 취약해짐&lt;/strong&gt;을 언급했다. 자신의 전문 지식에 대해서는 당연히 정확하게 알고 있어야하하는 것은 맞다. 그렇지만 그 정도가 지나쳐서 개미 콧구멍만 한 것들, 모래 한 알 같은 사소한 것까지 전부 다 알 순 없을 것임에도 불구하고 모른다는 사실을 들켰을 때에 대한 두려움에 빠져 불안함에 빠지게 된다. 그 아주 작은 하나라도 자신이 모르고 있음을 인지할 때 &lt;strong&gt;&quot;내가 모르는걸 들키면 사람들이 무능력하다고 생각할거야&quot;&lt;/strong&gt; 라는 생각으로 불안감, 초조함을 가지게 된다.&lt;/p&gt;
&lt;p&gt;실제로 가면 증후군의 대가라고 알려지신 유명한 심리학자 교수님 &quot;리사 손&quot; 님도 임포스터 증후군이 찾아왔다고 한다. &quot;내가 교수가 될 자격이 없는데도 된 것 같아. 내가 학생들에게 배움을 가르칠 자격이 있는걸까?&quot; 와 같은 현상이 나타났다고 한다. 다른 사람이 봤을때는 매우 저명학 학자분들이 &quot;도대체 왜 그러실까? 라는 생각이 들 정도로 불안함, 두려움을 안겨준다. &lt;strong&gt;가면 증후군은 앓고 있다면 내 스스로 내 능력을 믿지 못하게 된다. 아무리 내가 노력하고 학식이 가득 차 있는데도 불구하고 두려움에 휩사여 능력을 발휘할 수 없게된다.&lt;/strong&gt; 좋은 기회가 찾아와도 괜한 두려움 떄문에 소중한 시간을 잃게된다.&lt;/p&gt;
&lt;p&gt;나 또한 프로그래밍을 열심히 하면 할 수록, 성장하면 성장해 있을수록 불안감에 휩싸일 때가 너무 많다. 간혹 되려 부족한 모습을 매꾸기 위해 공부하고, 뒤돌이켜보면 정말 많이 성장해 있음에도 기분은 항상 우울해진다. 더 열심히 살면 살수록 행복 지수는 많이 낮아졌다. 오늘까지만 해도 평소처럼 &lt;strong&gt;&quot;난 아직도 정말 못한다. 많이 부족한 사람이다.&quot;&lt;/strong&gt; 를 생각했다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;n사-nginx-웹서버-성능-개선-컨설팅-경험&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#n%EC%82%AC-nginx-%EC%9B%B9%EC%84%9C%EB%B2%84-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0-%EC%BB%A8%EC%84%A4%ED%8C%85-%EA%B2%BD%ED%97%98&quot; aria-label=&quot;n사 nginx 웹서버 성능 개선 컨설팅 경험 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;N사 Nginx 웹서버 성능 개선 컨설팅 경험&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/68044b5f1ca5a59600a237f26c133be4/acf8f/email.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.6441717791411%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAABTUlEQVR42q2SWU/CQBSF+f+/xRAE9EEMgkXfjImECERq93a62ZXK0h7njoHEBBog3uR0kpn0m3vPmUZVAaqqYzyeYLGQwRiD8qmAOQymacFzXeiajtlsLjSdziDLMoqiwKFqVJxoWRb/2cTkfY5W+wbNVoevt7hqtrmu0R9ISJIUcRwjCEKxbjabemD0FeFDMdHs3GEoPeNhKGHw+IT7/hAvr284tRr0YcyF53kI4xxRkokDsuJYVTWHAkjtkyd5ngmlaYr1eo3tdvursjyvwywjUI7/KAGkcV2eJkGjKOIBJOKSi4DkRxiGsG0bjuNA0zQYhgHf9/d+1Xl2EEieBUEgwCSCrVary0fudrsYjUb7zW8eCL21nejCU7sUQOqKPNx1aLsMuqGL0Un0TglMyZ8cCnlIoxKwPOOZHB251+uJblRVxXJZ/AnknFB+AK9c7SWZNFxBAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;email&quot;
        title=&quot;&quot;
        src=&quot;/static/68044b5f1ca5a59600a237f26c133be4/a6d36/email.png&quot;
        srcset=&quot;/static/68044b5f1ca5a59600a237f26c133be4/222b7/email.png 163w,
/static/68044b5f1ca5a59600a237f26c133be4/ff46a/email.png 325w,
/static/68044b5f1ca5a59600a237f26c133be4/a6d36/email.png 650w,
/static/68044b5f1ca5a59600a237f26c133be4/e548f/email.png 975w,
/static/68044b5f1ca5a59600a237f26c133be4/3c492/email.png 1300w,
/static/68044b5f1ca5a59600a237f26c133be4/acf8f/email.png 1340w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;작년 여름 대학교 3학년 무렵, 평소처럼 열심히 프로그래밍을 이어가다가 여러 회사로부터 메일을 받았다.
그 중 가장 인상깊게 기억에 남는것은 &lt;strong&gt;N사의 Nginx 웹서버를 컨설팅 했던 경험이 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;당사의 교육 솔루션에서 &quot;온라인평가&quot;와 관련된 제품이 있었다. 그런데 동시접속자가 많아진 경우에 was, db 부분은 여유가 있지만 Nginx 부분에 bottlenet 이 생기는 것 같다는 것이 문제가 된다고 했다. 회사의 내부 사정을 파악하고 웹서버 및 아키텍처를 어떻게 개선해야할지 간단히 컨설팅해드린 적이 있다.&lt;/p&gt;
&lt;p&gt;그 당시 나에게 메일 컨텍을 주셨던 분이 같은 학교인 인하대학교 전기 공학과 출신 연구실장님이었다. 너무 반가웠다. 같은 학교 출신이라고 하니 반갑기도 했지만, &lt;strong&gt;한편으론 감히 3학년따리 학부생이 연구실장(상무)님과 N사의 관리 및 사업부에게 조언과 컨설팅을 해드린다는 것이 매우 부담스러웠다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;그-당시의-나는-전형적인-임포스터였다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B7%B8-%EB%8B%B9%EC%8B%9C%EC%9D%98-%EB%82%98%EB%8A%94-%EC%A0%84%ED%98%95%EC%A0%81%EC%9D%B8-%EC%9E%84%ED%8F%AC%EC%8A%A4%ED%84%B0%EC%98%80%EB%8B%A4&quot; aria-label=&quot;그 당시의 나는 전형적인 임포스터였다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그 당시의 나는 전형적인 임포스터였다.&lt;/h3&gt;
&lt;p&gt;그 당시 내가 느꼈던 감정은 아직도 정말 확실하게 기억에 남는다. &lt;strong&gt;&quot;내가 뭐라고 저런 분들에게 조언을 드려도 되는걸까? 왜 나를 찾아오신거지? 난 정말 부족한 사람인데.. 당연히 내 바보같은 모습을 보지 못해서 컨설팅을 요청하신거겠지?&quot;&lt;/strong&gt; 라고 생각했었다. 지금 다시 떠올려보면, 이 또한 전형적인 임포스터 증후군에 해당했다. &lt;strong&gt;이 기회를 잡아낸것도 내가 남들보다 엄청난 노력과 시간 투자가 있었기에 가능한 것이다.&lt;/strong&gt; 나에겐 정말 많은 노력이 있었다. 이를 만들어낸 것은 절대 운이 아니다. 설령 운이 따라줬다고 한들, 그 운을 이렇게 잡아낼 수 있었던 것도, 소중한 경험을 할 수 있었던 것도 모두 내 노력과 시간이 있었기에 가능한것이다.&lt;/p&gt;
&lt;p&gt;내 멋지고 훌륭한 모습을 부정하지 말자. 이 모습이 내 진짜 모습이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;내 인생의 하이라이트를 의심하지 말아야겠다. 하이라이트를 만들어내기 위해 노력한 것은 나 자신임을 잊지 말아야지. 🙂&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;개발자를-망하게-하는-사고방식-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A5%BC-%EB%A7%9D%ED%95%98%EA%B2%8C-%ED%95%98%EB%8A%94-%EC%82%AC%EA%B3%A0%EB%B0%A9%EC%8B%9D-&quot; aria-label=&quot;개발자를 망하게 하는 사고방식  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;개발자를 망하게 하는 사고방식 😱&lt;/h2&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;400&quot; src=&quot;https://www.youtube.com/embed/hU4kULhOdNE?si=y-aO6l5hCT5SSqOq&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;드림코딩 님의 개발자 마인드셋에서도 임포스터 증후군은 모든 개발자 70% 이상이 흔하게 겪는 증상임을 말씀하신다. 또한 &lt;strong&gt;성장의 길을 택하자! (Growth MindSet)&lt;/strong&gt; 을 강조하신다. 정말 특출난, 처음부터 타고난 개발자는 어딘가엔 분명 존재할 수 있겠지만, 그렇지 못한 사람들이 대부분이다. 모두가 공부하고, 노력하고, 계속해서 실력을 향상 하려 했기에 성장할 수 있었던 것이다.&lt;/p&gt;
&lt;p&gt;&quot;저 사람은 타고날때부터 저런거야&quot; 라는 마인드가 아닌, &lt;strong&gt;&quot;나는 이 순간 저 사람의 하이라이트 순간을 바라보고 있다. 저 사람은 하이라이트가 오기 전까지 얼마나 많은 연습과 노력을 했을까? 어떻게 지금의 하이라이트를 만들어 냈는지 내가 한 번 알아봐야지. 나도 저렇게 열심히해보자!&quot;&lt;/strong&gt; 라는 생각을 해보자. 또한 잘하는 곳에만 머무는 &lt;code class=&quot;language-text&quot;&gt;컴포트 존(Comfort Zone)&lt;/code&gt; 에서 벗어나자. 내가 지금 임포터스 증후군을 겪는것도 성장하고 있는 삶을 살아가고 있는 것이다. 새로운 것에 대한 두려움과 불안이 있어야만 내가 성장할 수 있음을 인지하자.&lt;/p&gt;
&lt;p&gt;임포터스 증후군이 찾아올 떄 마다 &quot;아, 난 왜 이렇게 부족하지?&quot; 가 아니라 &quot;내가 지금 겪고 있는 불안은 성장통이구나. 성장하고 있기 떄문에 이런 생각이 찾아온거구나.&quot; 라고 생각해보자.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;지금까지-오해했던-메타인지-학습법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%80%EA%B8%88%EA%B9%8C%EC%A7%80-%EC%98%A4%ED%95%B4%ED%96%88%EB%8D%98-%EB%A9%94%ED%83%80%EC%9D%B8%EC%A7%80-%ED%95%99%EC%8A%B5%EB%B2%95&quot; aria-label=&quot;지금까지 오해했던 메타인지 학습법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;지금까지 오해했던 메타인지 학습법&lt;/h2&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/9vjJC7TwA3Y?si=JAC3T8xy67oT589s&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;가면 증후군은 메타인지와도 밀접한 연관이 있다. 평소 나는 메타인지가 무엇인지 잘 알고있었고, 메타인지 학습법을 잘 실천하고 있었는 줄 알았다. 단순히 메타인지란 &lt;strong&gt;내가 무엇을 잘 알고 잘 모르는지 깨닫는 것&lt;/strong&gt; 정도로 생각하고 있었다.
그런데 컬럼비아대학교 리사 손 교수님의 말씀을 듣고 다소 신선한 충격을 얻었다. 메타인지의 정의는 아래와 같다고 한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;메타인지 (Meta Cognition)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(1) 자기 자신을 보는 거울&lt;/li&gt;
&lt;li&gt;(2) 스스로를 믿는 능력&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;(3) 나의 완벽하지 않은 모습을 인정하는 것&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;리사 손 교수님은 특히 (3) 이 가장 중요함을 강조했다. &lt;strong&gt;메타인지란 &quot;나의 완벽하지 않는 모습을 인정하는 것&quot;&lt;/strong&gt; 이다. 나 자신이 무엇을 알고 모르는지도 메타인지로 볼 수 있지만, &quot;내 부족하고, 아직 완벽하지 않은 모습을 스스로 받아들이고 인정하는 것&quot; 이라고 생각이 든다.&lt;/p&gt;
&lt;p&gt;이를 보면서 진짜 메타인지란 그 부족한 부분을 겸허히 받아들이고, 스스로 생각하고 사고하는 능력을 기르는 과정속에서 성장하는게 아닌가 싶다. &lt;strong&gt;&quot;완벽한&quot; 사람이 되기위해 자신이 모르는것을 찾아내는 것은 절대 메타인지가 아니라 생각한다. 🤔&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;모니터링monitoring-과-컨트롤controll&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81monitoring-%EA%B3%BC-%EC%BB%A8%ED%8A%B8%EB%A1%A4controll&quot; aria-label=&quot;모니터링monitoring 과 컨트롤controll permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;모니터링(Monitoring) 과 컨트롤(Controll)&lt;/h3&gt;
&lt;p&gt;또한 메타인지는 크게 2가지 과정으로 구성된다고 한다. &lt;code class=&quot;language-text&quot;&gt;모니터링&lt;/code&gt;이 선수의 과정으로, &lt;code class=&quot;language-text&quot;&gt;컨트롤&lt;/code&gt;이 이후의 과정으로 일련의 과정이 일어난다.
&lt;code class=&quot;language-text&quot;&gt;모니터링&lt;/code&gt;이란 자신이 가진 지식의 질과 양에 대해 스스로 평가하는 과정, 즉 자기자신을 거울로 관찰하는 것을 말한다. &lt;code class=&quot;language-text&quot;&gt;컨트롤&lt;/code&gt;이란 모니터링을 토대로 학습 방향을 설정하는 것을 말한다.&lt;/p&gt;
&lt;h3 id=&quot;가면을-쓸때-메타인지-학습법을-실천하지-못한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%80%EB%A9%B4%EC%9D%84-%EC%93%B8%EB%95%8C-%EB%A9%94%ED%83%80%EC%9D%B8%EC%A7%80-%ED%95%99%EC%8A%B5%EB%B2%95%EC%9D%84-%EC%8B%A4%EC%B2%9C%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;가면을 쓸때 메타인지 학습법을 실천하지 못한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;가면을 쓸때 메타인지 학습법을 실천하지 못한다.&lt;/h3&gt;
&lt;p&gt;모니터링이 원활히 수행되어야 컨트롤이 잘 수행된다. 다시말해, 모니터링을 잘 못하면 컨트롤도 잘 못한다. 그런데 가면을 썼다면 모니터링과 컨트롤의 일련의 메타인지 과정을 방해한다. &lt;strong&gt;겸손의 가면을 쓰면 모니터링을 애당초 시작하지도 못한다.&lt;/strong&gt; 가령 누군가가 프로그래밍을 배울래? 라는 것을 권할때, 본인에게 가면이 씌워졌다면 시작하기도 전부터 &quot;아. 나 이런거 잘 못해. 무서우니 안할래.&quot; 가 된다.&lt;/p&gt;
&lt;p&gt;메타인지는 내가 살면서 겪는 모든 배움과 도전에, 그리고 문제 해결에 있어서 반드시 필요하다. &lt;strong&gt;메타인지란 살아가면서 모든 배움과 도전에 있어 가면 없이, 가면 뒤의 솔직한 나를 드러내는 것이다.&lt;/strong&gt; 이건 이렇게 생각하는데, 아직 이건 모르겠어. 와 같이 솔직하게 아는것과 모르는것에 대해 말이 나오는 것이 메타인지다.&lt;/p&gt;
&lt;p&gt;임포스터들은 메타인지가 힘듫다. 솔직한 나 자신을 드러내고, 내가 솔직하게 무엇을 잘 알고 모르는지 감추기 떄문에 메타인지 활성화가 힘듷다. 임포스터들은 가면 뒤에 숨어서 도전을 피하고 난관에 굴복할 수 있다. &lt;strong&gt;즉 임포스터가 되지 않고, 가면을 벗기고 삶을 나만의 개성적인 색깔로, 주체적으로 살도록 도와주는 것이 메타인지다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;난 지금까지 그래왔듯이 메타인지 학습법을 통해 성장하고 싶다. 따라서 임포스터가 되지 않기 위해 노력해야겠다. 가면없이 진솔한 내 모습만이 무엇을 알고 모르는지 인지할 수 있을 것이다. 또한 완벽하지 않으려 해야겠다. 완벽하기 위해 노력하는 순간 나는 가면을 쓴 임포스터로 되돌아가지 않을까? 🤔 모든것은 &quot;진솔함, 솔직함&quot; 에 있다. 정말 반성해야겠다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;포장된-가면을-벗어내기-위해&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8F%AC%EC%9E%A5%EB%90%9C-%EA%B0%80%EB%A9%B4%EC%9D%84-%EB%B2%97%EC%96%B4%EB%82%B4%EA%B8%B0-%EC%9C%84%ED%95%B4&quot; aria-label=&quot;포장된 가면을 벗어내기 위해 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;포장된 가면을 벗어내기 위해&lt;/h2&gt;
&lt;h3 id=&quot;그래-나-못한다-그래서-어쩌라고-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%9E%98-%EB%82%98-%EB%AA%BB%ED%95%9C%EB%8B%A4-%EA%B7%B8%EB%9E%98%EC%84%9C-%EC%96%B4%EC%A9%8C%EB%9D%BC%EA%B3%A0-&quot; aria-label=&quot;그래 나 못한다 그래서 어쩌라고  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그래, 나 못한다. 그래서 어쩌라고? 🤔&lt;/h3&gt;
&lt;p&gt;박서희 의사님은 가면 증후군을 해결하기 위해 본인의 당당한, 자신감 있는 모습을 드러내기를 권장한다. 다른 사람들이 가면 벗은 내 모습이 마음에 안 들고 실망할 수 있다. &lt;strong&gt;&quot;그런데 그게 뭐 어쩌라고, 이게 내 모습이다! 저는 이런 제 모습이 좋습니다. 어쩌라고요, 이게 난데.&quot;&lt;/strong&gt; 라는 솔루션을 제안했다. 가면을 벗은 내 솔직한 모습을 인정하고, 진솔한 모습에 사랑할 수 있어야함이 느껴진다.&lt;/p&gt;
&lt;p&gt;나는 모든걸 다 아는 완벽한 전문가가 될 수 없음을 인정하자. 나는 정말 당연하게도 수많은 프레임워크, 라이브러리, 아키텍처, 코드, 문서 내용을 파악하고 이해할 수 없다.&lt;/p&gt;
&lt;h3 id=&quot;나-스스로를-단정짓지-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%82%98-%EC%8A%A4%EC%8A%A4%EB%A1%9C%EB%A5%BC-%EB%8B%A8%EC%A0%95%EC%A7%93%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;나 스스로를 단정짓지 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;나 스스로를 단정짓지 않는다.&lt;/h3&gt;
&lt;p&gt;나 스스로를 어떤 존재라고 단정짓지 않아야한다. 내 스스로를 어떤 사람이라고 정의내리지 말자. 우리의 모습은 스냅샷과 같은 한 장의 모습만이 존재하는 것이 아닌, 변화 무쌍한 동영상과 같은 다양한 모습을 지니고 있다. 나 스스로가 내 모습을 고정시켜버리면 그때부터 불안이 시작된다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;상대방에 기대에 맞추기 위해서 내 스스로의 진솔한 모습을 일지말자. 포장된 가면속에 살다간 결국엔 나 조차도 내가 어떤 사람인지를 잃어버리게 된다.&lt;/strong&gt; 절대로 그 틀에 나를 가두지 말자.&lt;/p&gt;
&lt;p&gt;우리 모두는 부족함이 없는, 정말 멋져보이는 모습이 있을 것이고, 한편으론 덜렁거리는 모습이 있다. 우리에겐 다양한 모습이 있고, 그 모습 자체가 정말 아름답다. 여러가지 모습, 가면을 쓰고 있는 것이 당연하며, 그게 절대 나쁜것이 아니다. 다만 한 가지 가면에만 자신을 고집하고, 정의하고, 그 가면 뒤에 숨어서 불안해하고, 그 가면이 벗겨질까 전전긍긍하지 말자. 또한 타인의 기대에 맞춰 가면을 쓰지말자.
내가 자유롭게 다양한 모습, 가면들을 쓰고 벗었다 하면된다.&lt;/p&gt;
&lt;h3 id=&quot;내-스스로의-노력-성과를-인정해주자-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%82%B4-%EC%8A%A4%EC%8A%A4%EB%A1%9C%EC%9D%98-%EB%85%B8%EB%A0%A5-%EC%84%B1%EA%B3%BC%EB%A5%BC-%EC%9D%B8%EC%A0%95%ED%95%B4%EC%A3%BC%EC%9E%90-&quot; aria-label=&quot;내 스스로의 노력 성과를 인정해주자  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;내 스스로의 노력, 성과를 인정해주자 🙂&lt;/h3&gt;
&lt;p&gt;우리가 노력한 시간이 분명있다. &lt;strong&gt;단순히 운이 좋았다? 몰론 운도 따라줬겠다. 하지만 그 운을 잡는것도, 기회를 잡기 위해 노력한 나의 시간이 있었기 때문에 가능한 것이다.&lt;/strong&gt; 또한 운을 통해 기회를 잡아도 그걸 유지하기 위한 노력이 분명히 있었기 때문에 가능한것이다. 그 노력한 시간들을 절대 그냥 흘려보내지 말고, 내 스스로에게 칭찬하는 습관을 길들이자.&lt;/p&gt;
&lt;h3 id=&quot;다른-사람들의-하이라이트를-인정하고-배워보자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EB%A5%B8-%EC%82%AC%EB%9E%8C%EB%93%A4%EC%9D%98-%ED%95%98%EC%9D%B4%EB%9D%BC%EC%9D%B4%ED%8A%B8%EB%A5%BC-%EC%9D%B8%EC%A0%95%ED%95%98%EA%B3%A0-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90&quot; aria-label=&quot;다른 사람들의 하이라이트를 인정하고 배워보자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다른 사람들의 하이라이트를 인정하고, 배워보자!&lt;/h3&gt;
&lt;p&gt;앞서 말했듯이 &quot;저 사람은 타고날때부터 저런거야&quot; 라는 마인드가 아닌, &lt;strong&gt;&quot;나는 이 순간 저 사람의 하이라이트 순간을 바라보고 있다. 저 사람은 하이라이트가 오기 전까지 얼마나 많은 연습과 노력을 했을까? 어떻게 지금의 하이라이트를 만들어 냈는지 내가 한 번 알아봐야지. 나도 저렇게 열심히해보자!&lt;/strong&gt;&quot; 라는 생각을 해보자.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;&quot;난 아직도 못한다.&quot; 라는 마인드 셋을 버리자. 또한 내가 노력해서 만들어낸 정량적 결과물들을 인정해주자. 나 자신을 칭찬하고, 좋은 습관을 만들기위해 계속 고민해야겠다. 더 이상 포장하는 습관을 버리자.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;나 자신을 조차도 속이는게 가장 슬픈 일이다. 진솔한 내 자신을 직면하는 용기와 불안을 떨칠 수 있어야한다. 완벽하지 않는것이 불안을 극복하지는 가장 현명한 방법이다. 또한 메타인지는 사실 자기 삶의 주인이 되고 행복하게 알아가는 도구임을 잊지 말아야겠다 😆&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[Default Private]]></title><description><![CDATA[Default Private Posts This is a default private post. It's recommended not to delete this posts 😵]]></description><link>https://haon.site/default/private-default/</link><guid isPermaLink="false">https://haon.site/default/private-default/</guid><pubDate>Mon, 01 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;default-private-posts&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#default-private-posts&quot; aria-label=&quot;default private posts permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Default Private Posts&lt;/h2&gt;
&lt;p&gt;This is a default private post. It&apos;s recommended not to delete this posts 😵&lt;/p&gt;</content:encoded></item><item><title><![CDATA[2023년, 나는 어떻게 성장했고 앞으로 무엇이 필요한가?]]></title><description><![CDATA[202…]]></description><link>https://haon.site/회고/2023/</link><guid isPermaLink="false">https://haon.site/회고/2023/</guid><pubDate>Mon, 18 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;2023년이 끝나갈 시기이다. 올해를 슬슬 마무리하면서 올해동안 무엇을 학습했으며, 앞으로 내가 나아가야할 &quot;학습&quot; 방향성에 대해 회고해보고자 한다. 학습 회고 외의 다른 내용들은 온전히 생략하고, 따로 회고를 진행해볼까한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;평생-학습기반을-가꾸기-위해-노력한-글쓰기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8F%89%EC%83%9D-%ED%95%99%EC%8A%B5%EA%B8%B0%EB%B0%98%EC%9D%84-%EA%B0%80%EA%BE%B8%EA%B8%B0-%EC%9C%84%ED%95%B4-%EB%85%B8%EB%A0%A5%ED%95%9C-%EA%B8%80%EC%93%B0%EA%B8%B0&quot; aria-label=&quot;평생 학습기반을 가꾸기 위해 노력한 글쓰기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;평생 학습기반을 가꾸기 위해 노력한 글쓰기&lt;/h2&gt;
&lt;p&gt;나는 지금까지 그 어떠한 교육기관의 도움없이 독학으로 나만의 개발 철학, 공부법을 만드는데 묵묵히 많은 시간을 투자했다. 웹 프로그래밍을 처음 시작한지는 약 1년 반이 넘어가는 중이며, 자바 프로그래밍을 공부한지는 1년 조금 넘은듯하다.&lt;/p&gt;
&lt;p&gt;하루도 빠짐없이 카페, 도서관에 가서 글쓰기, 사이드 프로젝트에 그 누구보다 열심히 몰입했던 것 한 해였다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e40414d3-c868-458c-8e8b-b94feffcda60/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/%EB%8A%A5%EB%8F%99%EC%A0%81%EC%9D%B8-%ED%95%99%EC%8A%B5%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%B8%94%EB%A1%9C%EA%B9%85-%EC%84%A4%EB%AA%85%ED%95%98%EA%B8%B0%EC%9D%98-%ED%95%99%EC%8A%B5%ED%9A%A8%EA%B3%BC-p60yepgo&quot;&gt;책임감 있는 블로깅 : 설명하기의 학습효과와 능동적인 공부법&lt;/a&gt; 로 올해의 첫 글쓰기를 시작했다. 난 메타인지를 실천하기 위한 최고의 학습법으로 블로깅만큼 최고인 것이 없다고 굳게 믿는다. 처음에는 서투른 글쓰기였지만, 정말 많은 시간을 학습법을 가꾸는데 투자하니 어느새 글쓰기 속도와 학습 내용에 대한 이해도, 기억률이 꽤나 향상되었다. 이 학습법을 정립하는데 까지 &lt;a href=&quot;https://hudi.blog/&quot;&gt;동아리 선배&lt;/a&gt; 로 부터 도움을 받고 기반을 다질 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/9a2ee450-1b7b-4ba7-9c73-ea382849d122/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;남을 위해 공유하는 습관을 길들인 글쓰기가 곧 나를 위한 것이다. 위에서 다룬 공부법을 더 발전시켜서, &lt;a href=&quot;https://velog.io/@msung99/%ED%9A%8C%EA%B3%A0-%EA%B8%80%EC%93%B0%EA%B8%B0%EA%B0%80-%EC%A0%95%EB%A7%90-%EB%82%98%EC%97%90%EA%B2%8C-%EB%8F%84%EC%9B%80%EC%9D%B4%EB%90%A0%EA%B9%8C&quot;&gt;[회고] 글쓰기가 정말 나에게 도움이될까?&lt;/a&gt; 에서 개선된 공부법에 대해 새롭게 다루었다. &lt;strong&gt;배움을 글쓰기로 인출하는 것이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;하나의 지식을 습득하는 과정은 매우 고통스러워야 한다.&lt;/strong&gt; 가만히 책과 인강을 듣는것으로 안일하게 학습을 끝내선 안된다. &lt;strong&gt;또한 인간은 망각의 동물이다.&lt;/strong&gt; 이 공부법의 기원은 미국 버즈니아 NTL 연구소의 논문 결과에 기반하여 발전시킨 것이다.&lt;/p&gt;
&lt;p&gt;다만 내 블로그도 앞으로 어떻게 관리하고 다듬어야 할지에 대한 회고 내용이 가득한데, 자세한 내용은 별도로 다루도록 한다. 이런 방법으로 많은 글들을 쌓아왔는데, 그 중 인상깊게 기억이 남는 토픽 몇가지에 대해서만 즐겁게 회고해보고자 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;동시성-이슈-제어&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88-%EC%A0%9C%EC%96%B4&quot; aria-label=&quot;동시성 이슈 제어 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동시성 이슈 제어&lt;/h2&gt;
&lt;p&gt;가장 먼저 떠오르는 것은, 동시성 문제에 대해 많은 학습과 고민이 있었다. 사이드 프로젝트에서도 동시성 문제가 발생할 가능성이 충분히 존재했었고, 이와 관련한 다양한 동시성 제어 기법에 대해 익힐 필요가 있었다. 프로젝트에 적용하는 기법 외에도 다양한 락 메커니즘에 대해서도 학습했었다.&lt;/p&gt;
&lt;h3 id=&quot;redis&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis&quot; aria-label=&quot;redis permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/3c5af58d-679f-4d27-8f59-f69348f46707/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;가장 먼저 떠오르는 내용은 &lt;a href=&quot;https://velog.io/@msung99/Redis-%EB%B6%84%EC%82%B0-%EB%9D%BD%EC%9D%84-%EA%B5%AC%ED%98%84%ED%95%B4-race-condition-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0&quot;&gt;[Redis] 분산락(Distribution Lock) 을 구현해 다중화 서버에서 발생하는 동시성 문제 제어하기&lt;/a&gt; 에서 다룬 내용이다. 이떄 Redis 를 단순히 캐싱의 용도를 넘어서 동시성 제어에도 활용할 수 있고, 위와 같이 &lt;code class=&quot;language-text&quot;&gt;Kafka&lt;/code&gt; 처럼 (당연히 둘은 많이 다르긴 하지만) &lt;code class=&quot;language-text&quot;&gt;메시지브로커&lt;/code&gt; 로도 활용 가능하다는 것에서 정말 신기했었다. &lt;code class=&quot;language-text&quot;&gt;Publisher&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;Subscriber&lt;/code&gt; 서버를 배치하여 높은 확장성, 고가용성을 갖는 구조를 만들 수 있다는 점을 알게되었다. 이러한 Redis 의 &lt;code class=&quot;language-text&quot;&gt;Pub/Sub&lt;/code&gt; 관련 내용은 &lt;a href=&quot;https://velog.io/@msung99/Redis-%EB%B6%84%EC%82%B0-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%97%90%EC%84%9C-PubSub-%EB%A9%94%EC%8B%9C%EC%A7%80-%EB%B8%8C%EB%A1%9C%EC%BB%A4%EB%A5%BC-%ED%86%B5%ED%95%9C-%EB%8B%A4%EC%A4%91-%EB%A1%9C%EC%BB%AC%EC%BA%90%EC%8B%9C-%EB%8F%99%EA%B8%B0%ED%99%94-ex3yodp7&quot;&gt;[Redis] 분산 시스템에서 Pub/Sub 메시지브로커를 활용한 다중 로컬캐시 동기화&lt;/a&gt; 에서 다루었다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6e5410e2-8b10-4702-bf1d-10b36d645af3/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이때 막연하게 CS 의 중요성도 깨달았다. 항상 수업으로만 듣던 전공 수업 내용들이 어떻게 적용되고, 왜 중요하다는 것인지 의문이 가득했었다. 동시성 이슈를 제어하기 위한 기법들이 나에게 처음으로 CS 의 필요성을 깨닫게 해준 토픽이었다.&lt;/p&gt;
&lt;h3 id=&quot;다양한-락-메커니즘--분산-락-db-락-낙관적-락--&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%96%91%ED%95%9C-%EB%9D%BD-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98--%EB%B6%84%EC%82%B0-%EB%9D%BD-db-%EB%9D%BD-%EB%82%99%EA%B4%80%EC%A0%81-%EB%9D%BD--&quot; aria-label=&quot;다양한 락 메커니즘  분산 락 db 락 낙관적 락   permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다양한 락 메커니즘 ( 분산 락, DB 락, 낙관적 락, ... )&lt;/h3&gt;
&lt;p&gt;Redis 의 분산락 외에도, 이 토픽을 내 머릿속에 온전히 채화하기위해 많은 부가 지식들이 필요했고, 다양한 동시성 기법들에 대해 공부할 필요가 있었다. 때문에 JPA 단에서 제공해주는 &lt;a href=&quot;https://velog.io/@msung99/JPA-%EB%82%99%EA%B4%80%EC%A0%81-%EB%9D%BDOptimistic-Lock-%EA%B3%BC-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BDPessimistic-Lock-%EC%9D%84-%ED%86%B5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0&quot;&gt;낙관적 락(Optimisstic Lock)&lt;/a&gt;, &lt;a href=&quot;https://velog.io/@msung99/JPA-%EC%9D%98-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BD%EC%97%90-%EB%8C%80%ED%95%9C-LockModeType-%EA%B3%B5%EC%9C%A0-%EB%9D%BD%EA%B3%BC-%EB%B0%B0%ED%83%80-%EB%9D%BD&quot;&gt;비관적 락(Pessimistic Lock)&lt;/a&gt; 을 처음 접했는데, 단순히 스프링부트 프레임워크의 근본.핵심적인 지식을 넘어서 프레임워크가 정말 다양한 케이스를 고려해서 잘 만들어졌다는 것을 체감했다. &lt;code class=&quot;language-text&quot;&gt;@Version&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@Lock&lt;/code&gt; 같은 어노테이션을 통해서 제어가 가능하다니.. 이걸 만든 사람들은 정말 천재라는 것을 느꼈다. 난 아직 우물 안 게구리이다. 아직 배워나갈 내용들이 산더미라는 것을 체감했다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/15ea3efd-4cbe-45df-bfd6-434f01fe641c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/MySQL-%EB%84%A4%EC%9E%84%EB%93%9C-%EB%9D%BDNamed-Lock-%EC%9C%BC%EB%A1%9C-%EB%B6%84%EC%82%B0%EB%9D%BD%EC%9D%84-%EA%B5%AC%ED%98%84%ED%95%98%EC%97%AC-%EB%B6%84%EC%82%B0-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88%EB%A5%BC-%ED%95%B4%EA%B2%B0%ED%95%B4%EB%B3%B4%EC%9E%90&quot;&gt;MySQL 의 네임드 락(Named Lock)&lt;/a&gt; 에 대해서도 공부해보면서, 분산락과 일반적인 DB 락 기법의 차이점에 대해서도 정리했었다. 이 과정에서 JPA 의 &lt;code class=&quot;language-text&quot;&gt;NativeQuery&lt;/code&gt; 의 존재를 처음 알게 되기도 했다.&lt;/p&gt;
&lt;p&gt;다만 아쉬운점은, 아직 100% 체화시키지 못했다는 것을 느꼈다. 사이드 프로젝트를 진행하는 과정에서 락 기법에 대해 잘못 이해를 하면서 팀원들과의 소통에서 어려움을 겪은 일화가 있다... 나만의 지식으로 더욱이 습득할 필요가 있다.&lt;/p&gt;
&lt;p&gt;위와 같은 지식들을 이해하기 위해, 원초적인 근본 지식인 &lt;code class=&quot;language-text&quot;&gt;트랜잭션의 격리수준과 전파, ACID&lt;/code&gt; 에 대해서도 공부하면서 DB 에 대해서도 실무적인 관점에서 필요에 의해 이론 지식을 공부하게 되었다. 사실 이번 1학기 수업때 학교 DB 수업을 들으면서 ACID, 트랜잭션, 인덱스, 정규화, ... 등을 배웠다. 그때만 해도 왜 이런걸 내가 왜 배워야하나라는 무식한 생각을 했었는데, 이렇게 공부했던 CS 지식들이 실제 개발에서 활용할 줄은 몰랐다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/b1c07552-0276-4573-a57a-ae7fdaa60337/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B4-%ED%82%A4%EC%9B%8C%EB%93%9C-volatile-%EC%99%80-synchornized-3zgpzzoh&quot;&gt;자바의 동시성 제어 키워드&lt;/a&gt; 들도 학습했다. &lt;code class=&quot;language-text&quot;&gt;synchornized&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;atomic 변수&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;volatile&lt;/code&gt; 등 여러 키워들에 대해 꼼꼼히 차이점, 활용 상황을 정리해봤다. 이들을 왜 잘 활용하지 않는지, 특히나 왜 분산 환경에서 사용하기에 부적합한지도 알 수 있었다. 추후 학교 OS 수업을 들을때 &lt;code class=&quot;language-text&quot;&gt;CAS 알고리즘&lt;/code&gt; 을 접했을때 너무나도 반갑기도 했다. CAS 알고리즘이 내부적으로 이렇게 구현되는구나..! 유레카! 라는 느낌이었다.&lt;/p&gt;
&lt;p&gt;이러한 에피소드로 점차 하나씩 시야가 넓어지는듯한 느낌을 받았다. 또한 이런 경험을 통해 맹목적으로, 수동적으로 지식을 이유없이 주입하는 습관에서 한층 벗어날 수 있게 되었다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;날-힘들게했던-하이리스크-프로젝트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%82%A0-%ED%9E%98%EB%93%A4%EA%B2%8C%ED%96%88%EB%8D%98-%ED%95%98%EC%9D%B4%EB%A6%AC%EC%8A%A4%ED%81%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8&quot; aria-label=&quot;날 힘들게했던 하이리스크 프로젝트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;날 힘들게했던 하이리스크 프로젝트&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c70a71fe-d878-4aea-9a25-34b22d722a8e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;올해 사이드 프로젝트를 진행하는 과정에서 많은 트러블슈팅을 만났다. 아주 간단한 CRUD 구현만 가능했던 내가 특정 도메인 개발을 맏게 되었고, 개발을 위해 필사적으로 방대한 지식들을 습득해야했다. 무엇보다 모놀리틱 아키텍처 에만 익숙했던 내가 &lt;code class=&quot;language-text&quot;&gt;헥사고날 아키텍처&lt;/code&gt; 환경에서 개발을 진행하니, 이떄만 떠올리면 정말정말 프로젝트에 적응하는게 많이 힘들어했었다. 부족한 나였던만큼, 허겁지겁 던져지는 키워드들을 밤을 새우며 미친듯이 공부하고, 개발에 몰입했던 기억이 남는다.&lt;/p&gt;
&lt;h3 id=&quot;msa-헥사고날-아키텍처-멀티모듈-애자일--그게-다-뭔데-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#msa-%ED%97%A5%EC%82%AC%EA%B3%A0%EB%82%A0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EB%A9%80%ED%8B%B0%EB%AA%A8%EB%93%88-%EC%95%A0%EC%9E%90%EC%9D%BC--%EA%B7%B8%EA%B2%8C-%EB%8B%A4-%EB%AD%94%EB%8D%B0-&quot; aria-label=&quot;msa 헥사고날 아키텍처 멀티모듈 애자일  그게 다 뭔데  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MSA.. 헥사고날 아키텍처.. 멀티모듈.. 애자일 .. 그게 다 뭔데 😭&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e1197bff-a8f4-4768-a0e1-d9dc3008e223/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;뿐만 아니라 프로젝트가 &lt;a href=&quot;https://velog.io/@msung99/%EB%A9%80%ED%8B%B0-%EB%AA%A8%EB%93%88Multi-Module-%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EA%B3%A0-%EC%99%9C-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C&quot;&gt;멀티모듈&lt;/a&gt; 구조를 취하고 있었다. &lt;code class=&quot;language-text&quot;&gt;MSA&lt;/code&gt; 로 확장하기 쉽도록 이것저것 체계를 갖추고 있었다. MSA 는 Monolithic 아키텍처와 달리 다중 모듈간의 순환 참조 구조, 의존관계, 패턴, 설계가 한 눈에 파악하기 정말 정말 힘들다. 어댑터와 포트는 어떻게 적용하고, 모듈간의 참조를 어떻게 설계하고 파악해야할지... 지금 다시 떠올려봐도 정말 막막했다. 이 많은 아키텍처 관련 지식을 습득하고, 담당한 도메인 및 요구사항에 대한 이해를 빠삭하게 이해하고.. 애자일 프로세스에 대해 이해하고... 머리가 지끈지끈 했다. 요구사항, 기능 명세서를 어떻게 구체화시키고, Tech Spec, 스크럼을 어떻게 구체화하여 팀원들에게 공유해야 할지, 또 어떻게 설득시킬지 많은 생각을 해야했다. &lt;strong&gt;난 경험이 매우 적은 상태인 초보였기에, 무엇을 모르는치 조차도 모르는 바보였다.&lt;/strong&gt; 많이 우왕자왕했던 시기였다.. 😓&lt;/p&gt;
&lt;h3 id=&quot;높은-난이도의-도메인-개발-극단적인-상황-가정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%86%92%EC%9D%80-%EB%82%9C%EC%9D%B4%EB%8F%84%EC%9D%98-%EB%8F%84%EB%A9%94%EC%9D%B8-%EA%B0%9C%EB%B0%9C-%EA%B7%B9%EB%8B%A8%EC%A0%81%EC%9D%B8-%EC%83%81%ED%99%A9-%EA%B0%80%EC%A0%95&quot; aria-label=&quot;높은 난이도의 도메인 개발 극단적인 상황 가정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;높은 난이도의 도메인 개발, 극단적인 상황 가정&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ec6d9ef0-749e-4105-a0a1-d723546e938d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;프로젝트에 유저도 좀 있는 편이었다. 사실 그래서 부담이 정말정말 컸다. 비즈니스 로직을 조금이라도 잘못 설계했다만 모든 책임은 나에게 돌아올 수 밖에 없기에, 정책을 꼼꼼히 체크하고 명세서를 작성해야했다. 그래서 스트래스를 많이 받지 않았나 싶다. 사이드 프로젝트 치곤 규모가 매우 컸기 때문에.. 🤔 자그마한 스타트업 규모로 생각하면 좋을듯하다.&lt;/p&gt;
&lt;p&gt;프로젝트 자체가 맨 처음에 합류한 후 겉보기엔 단순해 보였지만, 요구사항 및 정책이 정말 복잡하고, 얽히고 꼬인게 많았다. 또한 &lt;strong&gt;사소한 것 하나까지 극단적으로 최악의 상황을 가정해야 하는게 많았다.&lt;/strong&gt; 가령 서비스는 대한민국 내에서만 유저가 있는게 아니였다. 중국, 미국, 홍콩, 프랑스, 터키, ... 등 많은 국적의 사람들이 있었고, 타국에서 서비스를 이용할 떄 고려해야 할 비즈니스 정책들이, 고려해야 할 요소들이 너무 많았다. 단순 정책을 넘어서도 인프라는 어떻게 구축해야할지, 순간 특정 리전에서 트래픽이 몰길 경우 어떻게 대처해야할 것인지, 다중 디바이스로 다른 리전에서 로그인을 시도할 경우 어떻게 로직을 설계한 것인지 등 꽤나 큰 하이리스크였다.&lt;/p&gt;
&lt;p&gt;몰론 내가 많이 경험을 가진 사람이었다면 얘기가 정반대였을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4ebc7f48-9224-4aaa-8190-ca6e93787c1e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;또한 &lt;a href=&quot;https://velog.io/@msung99/Clean-Architecture-%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%97%90%EC%84%9C%EB%8A%94-%EC%9D%98%EC%A1%B4%EC%84%B1-%EB%AC%B8%EC%A0%9C%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%B4%EA%B2%B0%ED%95%A0%EA%B9%8C&quot;&gt;클린 아키텍처&lt;/a&gt; 의 등장배경부터 도입 이유, 아키텍처의 세부 구성요소 (&lt;code class=&quot;language-text&quot;&gt;UseCase&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Adpater&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Port&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Entity&lt;/code&gt; 등) 의 낯선 개념을 내것으로 만드는데까지 많이 고생했다. 이 과정에서 객체지향, 디자인패턴의 중요성을 다시 깨닫게 되었다. 진짜 아무것도 몰랐을 땐 &lt;code class=&quot;language-text&quot;&gt;SOLID&lt;/code&gt; 가 스프링 Bean 컨테이너를 개발할 때만 도입되는 개념이라고만 알고 있었는데, 이는 큰 오산이었다. 클린 아키텍처를 비롯한 수많은 이론들이 결국 객체지향으로 귀결된다.&lt;/p&gt;
&lt;h3 id=&quot;가장-못하는-사람이-되라&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%80%EC%9E%A5-%EB%AA%BB%ED%95%98%EB%8A%94-%EC%82%AC%EB%9E%8C%EC%9D%B4-%EB%90%98%EB%9D%BC&quot; aria-label=&quot;가장 못하는 사람이 되라 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;가장 못하는 사람이 되라&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/b7463f7d-3f00-495e-87d4-1aee619f9984/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이 힘든 과정을 포기하고 벗어날까도 엄청 고민했었다. 하지만 묵묵히 버텨낸 결과, 아직 남에게 설명할 수 있을 정도는 절대 아니지만 병아리 정도는 된듯하다. 너무 못한다는 부족함을 느낀탓에, &lt;strong&gt;하루에 순수 프로그래밍 시간으로 14시간 이상을 투자했었다.&lt;/strong&gt; 밥먹는 시간도 최대한 줄여갔다. 원래 혼자 식사를 할때 유튜브를 보면서 여유 생활을 즐기는데, 이때는 이런 생활도 없이 밥을 먹으면서 도메인 개발 및 기술적 이슈에 대한 고민이 머릿속에 가득했다. 다행히도 이런 극단적인 연습괴 성장하고 싶은 절실한 마음을 통해 많이 성장할 수 있었다.&lt;/p&gt;
&lt;p&gt;사실 올해에 학술 동아리 운영진도 병행했었는데, 나조차도 바쁘고 힘든 심정에 부원들을 챙겨줄 마음이 없었다. 나름 도와주려고, 시간을 투자해야겠다고 생각은 조금 했지만 채력이 많이 빠져서 잘 못 챙겨준거 준 것 같다. 동아리 세션떈 Django 를 기반으로 진행했기에, 열심히 공부해서 도움을 줘야겠다! 라고 생각해도 ... 흠 세션은 매주 2일밖에 없겠지만 이것조차 내겐 너무 큰 부담이었다. 당시 새벽 3&lt;del&gt;4시경쯤이 되서야 겨우 잠자리에 들고 7&lt;/del&gt;8시쯤에 기상하기를 반복했다. 동아리 부원들에겐 더 신경을 써주지 못해 조금은 아쉬운 마음이 있다.&lt;/p&gt;
&lt;p&gt;이와 관련한 나의 극복 에피소드와 비슷하게, 자바지기 박재성님이 많은 사람들에게 응원의 메시지로 전달하는 포스트를 최근에 봤다. &lt;a href=&quot;https://brunch.co.kr/@javajigi/46&quot;&gt;가장 못하는 사람이 되라 : 박재성님 씀&lt;/a&gt; 글을 보면, 박재성님은 가장 빠르게 효과적으로 성장하기 위한 방법은 역량이 뛰어난 개발자들과 함께 하는것이라고 말한다. 이 글을 보자마자 크게 공감할 수 있었다.&lt;/p&gt;
&lt;h3 id=&quot;어제보다-나아진-나만을-바라보자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EC%A0%9C%EB%B3%B4%EB%8B%A4-%EB%82%98%EC%95%84%EC%A7%84-%EB%82%98%EB%A7%8C%EC%9D%84-%EB%B0%94%EB%9D%BC%EB%B3%B4%EC%9E%90&quot; aria-label=&quot;어제보다 나아진 나만을 바라보자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어제보다 나아진 나만을 바라보자&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;가장 못하는 사람이 되는 환경에 처했을때, 빠르게 효과적으로 성장할 수 있다. 나는 이 말을 경험을 통해서 이미 굳게 믿는다.&lt;/strong&gt; 하지만 이 점에서 주의해야 할 점은, 타인과 나를 비교하는 것은 당연하고 자존감이 떨어질 수 밖에 없다는것이다. 어느 그룹에 속해있던, 어떤 사람들을 만나던 &lt;strong&gt;어제와 오늘의 나만의 비교하자.&lt;/strong&gt; 꾸준한 페이스로 나를 바라보며 발전하는 것에 집중하자. 타인과 나를 계속 비교한다면, 언젠간 분명 크게 번아웃이 찾아올 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;고가용성을-위한-mysql-innodb-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%A0%EA%B0%80%EC%9A%A9%EC%84%B1%EC%9D%84-%EC%9C%84%ED%95%9C-mysql-innodb-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;고가용성을 위한 mysql innodb 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;고가용성을 위한 MySQL InnoDB 아키텍처&lt;/h2&gt;
&lt;h3 id=&quot;데이터베이스-replication-을-통한-쿼리-성능-개선&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-replication-%EC%9D%84-%ED%86%B5%ED%95%9C-%EC%BF%BC%EB%A6%AC-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0&quot; aria-label=&quot;데이터베이스 replication 을 통한 쿼리 성능 개선 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 Replication 을 통한 쿼리 성능 개선&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/49502f9f-3e6c-41e4-9c02-59e46987904e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;한편 MySQL 의 아키텍처 구조에 대해서도 꽤나 깊게 학습했었다. &lt;a href=&quot;https://velog.io/@msung99/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81Clustering-%EA%B3%BC-%EC%83%A4%EB%94%A9Sharding-%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EA%B3%A0%EA%B0%80%EC%9A%A9%EC%84%B1%EA%B3%BC-%EC%8A%A4%EC%BC%80%EC%9D%BC%EC%95%84%EC%9B%83&quot;&gt;데이터베이스 클러스터링(Clustering) 과 샤딩(Sharding)&lt;/a&gt; 을 학습했으며, &lt;a href=&quot;https://velog.io/@msung99/MySQL-8.0-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%8C%8C%ED%8B%B0%EC%85%94%EB%8B%9DPartitioning-%EA%B3%BC-%EC%83%A4%EB%94%A9Sharding-twlr9yuz&quot;&gt;파티셔닝(Partitioning)&lt;/a&gt; 을 학습하며 어떻게 데이터베이스의 고가용성을 높일 수 있을지에 대해 학습한 적이 있다. 아직 프로젝트에는 적용해보지 못했지만, 현 프로젝트 구조상 언젠간 반드시 적용될 키워드들이다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1e9e763a-386d-43d7-a98a-46430520da26/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;무엇보다 &lt;a href=&quot;https://velog.io/@msung99/MySQL-%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EA%B3%BC-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-DataSource-%EB%B6%84%EA%B8%B0%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%A1%B0%ED%9A%8C-%EC%BF%BC%EB%A6%AC-%EC%84%B1%EB%8A%A5-2%EB%B0%B0-%ED%96%A5%EC%83%81%EA%B9%8C%EC%A7%80%EC%9D%98-%EC%B5%9C%EC%A0%81%ED%99%94-%EA%B3%BC%EC%A0%95&quot;&gt;데이터베이스 레플리케이션&lt;/a&gt; 을 적용하는 과정이 가장 험난하면서도 즐거웠다. &lt;code class=&quot;language-text&quot;&gt;Master&lt;/code&gt; 서버는 Read Only 로, &lt;code class=&quot;language-text&quot;&gt;Slave&lt;/code&gt; 서버는 Write Only 로 배치하는 방식이었는데, 스프링부트의 &lt;code class=&quot;language-text&quot;&gt;Multi DataSource&lt;/code&gt; 라우팅 환경을 구축하는 과정이 많이 어려웠다.&lt;/p&gt;
&lt;p&gt;또한 레플레케이션을 이해하고 구축하려면 &lt;a href=&quot;https://velog.io/@msung99/MySQL-%EC%9D%98-MasterSlave-%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98Replication-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%99%80-%ED%86%A0%ED%8F%B4%EB%A1%9C%EC%A7%80-%EA%B5%AC%EC%84%B1-%EB%B0%A9%EC%8B%9D&quot;&gt;Master-Slave 토폴로지 구성 방식&lt;/a&gt; 에 대해 선수 지식으로 빠삭하게 이해해야했다. &lt;code class=&quot;language-text&quot;&gt;바이너리 로그 기반 복제&lt;/code&gt; 방식이 눈에 보이지 않는 추상화 개념이여서 이해하기 힘들었다. 하지만 여러번 정리한 내용을 읽어보고 글쓰기로 인출하니, 다행히도 지식을 내 것으로 만들고 라우팅 환경을 구축할 수 있었다.&lt;/p&gt;
&lt;h3 id=&quot;인덱스index&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4index&quot; aria-label=&quot;인덱스index permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스(Index)&lt;/h3&gt;
&lt;p&gt;인덱스 공부하는 과정도 너무 재밌었다. 내가 진짜 &quot;찐&quot; 실력 개발자가 된 것 같았다. 아무래도 인덱스가 DB 쿼리 성능의 &lt;code class=&quot;language-text&quot;&gt;최적화&lt;/code&gt; 와 가장 밀접하고 중요한 키워드여서 그런듯 하다. &quot;쿼리 성능을 35초에서 0.06초 개선했다&quot; 와 같은 표현을 보면 너무 멋지다. &lt;del&gt;(간지가 철철 흐르는 개발자가 된것같다!)&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;처음 인덱스를 접했을떄는 너무 어려워서 이해가 가지 않았다. 하지만 끝까지 지적 호기심을 해결하고자 하는 굳은 마음으로 끝내 내 지식으로 만들었다. 구글링을 통해 최대한 여러 인사이트를 참고하고, 도저히 이해가 가지 않는 부분들은 동아리 선배에게도 카톡으로 계속 질문하며 궁금증을 해결했었다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/MySQL-8.0-MySQL-%EC%97%90%EC%84%9C%EC%9D%98-B-Tree-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%ED%86%B5%ED%95%9C-%EB%A0%88%EC%BD%94%EB%93%9C-%EC%8A%A4%EC%BA%94-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B0%A9%EC%8B%9D-Index-Scan&quot;&gt;[MySQL 8.0] MySQL 에서 B+ Tree 인덱스 스캔을 통한 성능 최적화 방식 (Index Scan)&lt;/a&gt; 에서도 다루었듯이, 인덱스에 대해 나름대로 깊이 학습했던 경험이 있다. &lt;code class=&quot;language-text&quot;&gt;커버링 인덱스&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;인덱스 레인지 스캔&lt;/code&gt;, ... 등등에 대한 특정 상황에 적합한 인덱스 스캔 방식을 &lt;code class=&quot;language-text&quot;&gt;실행계획(Execution Plan)&lt;/code&gt; 을 통해 직접 확인했었다. 이 과정에서 &lt;code class=&quot;language-text&quot;&gt;옵티마이저(Optimizer)&lt;/code&gt; 에 대해서도 공부하면 좋겠다는 생각이 들었는데, 향후 이 키워드를 깊게 다루어볼 예정이다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/496aa94b-7d83-4dae-a2ec-5f9aa192ccac/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이렇게 인덱스의 스캔 방식을 공부하니, 자연스레 등장한 키워드가 바로 &lt;a href=&quot;https://velog.io/@msung99/MySQL-8.0-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%ED%98%95-%EC%9D%B8%EB%8D%B1%EC%8A%A4Clustered-Index-%EC%99%80-%EB%B9%84%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%ED%98%95-%EC%9D%B8%EB%8D%B1%EC%8A%A4Non-Clustered-Index-%EA%B8%B0%EB%B3%B8%ED%82%A4%EB%A5%BC-%ED%86%B5%ED%95%9C-%EA%B5%B0%EC%A7%91%ED%99%94&quot;&gt;클러스터링 인덱스, 논 클러스터링 인덱스&lt;/a&gt; 였다. 이 글쓰기를 통해 데이터베이스의 PK 로 어떤 타입을 지정하는 것이 좋은지에 대해 나름의 기준을 만들 수 있었다. 비슷한 맥락으로 &lt;a href=&quot;https://velog.io/@msung99/MySQL-8.0-%EC%B9%B4%EB%94%94%EB%84%90%EB%A6%AC%ED%8B%B0Cardinality-%EC%99%80-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%EC%A0%81%EC%9A%A9%ED%95%98%EB%8A%94-%EA%B8%B0%EC%A4%80-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EC%99%80-PK-%ED%99%9C%EC%9A%A9&quot;&gt;카디널리티(Cardinality)&lt;/a&gt; 에 대해서도 학습하면서, 보편적으로 알려진 인덱스의 적용 기준을 정리해볼 수 있었다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;bluegreen-무중단-배포-아키텍처-수동-구축&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bluegreen-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%88%98%EB%8F%99-%EA%B5%AC%EC%B6%95&quot; aria-label=&quot;bluegreen 무중단 배포 아키텍처 수동 구축 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Blue/Green 무중단 배포 아키텍처 수동 구축&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/54642bb3-924d-470d-983d-0233b4031c26/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;무중단 배포 아키텍처에 대해서도 너무너무 재밌게 공부했었다. &lt;a href=&quot;https://velog.io/@msung99/%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EB%A5%BC-%EC%9C%84%ED%95%9C-%EB%B0%B0%ED%8F%AC%EC%A0%84%EB%9E%B5Rolling-BlueGreen-Canary-%EC%A0%84%EB%9E%B5&quot;&gt;무중단 배포 아키텍처의 다양한 배포전략 (Rolling, Blue&amp;#x26;Green, Canary 배포에 대해)&lt;/a&gt; 에서 이론적으로만 다루었던 내용을 플러그인에 의존하지 않고 직접 수동으로 구축했었다. 블루 서버와 그린 서버를 배치해두고, 재배포가 발생할때 마다 리버스 프록시의 포인터를 매번 바꾸는 방식이다. 이 과정에서 10초에 1번의 주기로 헬스채킹의 과정도 일어난다.&lt;/p&gt;
&lt;h3 id=&quot;다운타임downtime-을-완벽하게-제거할-수-있을까--nginx-graceful-shutdown&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%9A%B4%ED%83%80%EC%9E%84downtime-%EC%9D%84-%EC%99%84%EB%B2%BD%ED%95%98%EA%B2%8C-%EC%A0%9C%EA%B1%B0%ED%95%A0-%EC%88%98-%EC%9E%88%EC%9D%84%EA%B9%8C--nginx-graceful-shutdown&quot; aria-label=&quot;다운타임downtime 을 완벽하게 제거할 수 있을까  nginx graceful shutdown permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다운타임(Downtime) 을 완벽하게 제거할 수 있을까? : Nginx Graceful Shutdown&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6c9d17ae-ea14-4b3e-9f08-dbe18f850c5a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;지금껏 어려운 이론들을 깊게 공부했었지만, 이만큼 집요허게 파고들었던 큰 주제는 절대 없었던 것 같다. 무중단 배포를 구축하는 과정에서, 의문이 들었던 점은 주어진 환경에서 &lt;strong&gt;&quot;과연 다운타임을 0.000초로 완벽히 만들 수 있을까?&quot;&lt;/strong&gt; 였다. 이에 대해 &lt;a href=&quot;https://velog.io/@msung99/Nginx-HTTP1.1-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-reload-%EC%8B%9C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EB%8B%A4%EC%9A%B4%ED%83%80%EC%9E%84-%EC%9D%B4%EC%8A%88%EB%A5%BC-%EC%99%84%EC%A0%84%ED%9E%88-%EC%A0%9C%EA%B1%B0%ED%95%A0-%EC%88%98-%EC%9E%88%EC%9D%84%EA%B9%8C-feat.-zero-downtime&quot;&gt;결론내렸던 것&lt;/a&gt;은, &lt;strong&gt;&quot;다운타임은 완벽히 제거할 수 없다&quot;&lt;/strong&gt; 는 것이다. 더 자세히 결론 내렸던 내용을 요약하면 아래와 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Nginx Graceful Shutdown 은 표현히 모호하다. 우아한 종료라는 표현은 충분히 오해할 소지가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;무중단 배포에 &quot;가까워지기&quot; 위해선, 워커 프로세스나 기타 옵션에 대한 튜닝을 통해 가능할 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위와 같은 결론을 증명해내기 위해서 온갖 몸부림이 있었다. 진짜 머리가 너무 아팠다. 다행히도 포기하지 않는 굳은 각오를 통해서 증명 가능했었던 것 같다. 처음에는 막연하게 &lt;a href=&quot;https://velog.io/@msung99/SpringBoot-Graceful-Shutdown-%EA%B3%BC-SIGTERM-%EC%8B%9C%EA%B7%B8%EB%84%90-%EA%B5%AC%EB%B2%84%EC%A0%84-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EB%A5%BC-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-%EC%A2%85%EB%A3%8C%EC%8B%9C%EC%BC%9C%EB%B3%B4%EC%9E%90-pe10wqzm&quot;&gt;SpringBoot Graceful Shutdown&lt;/a&gt; 를 적용하면 &quot;당연히도&quot; 다운타임이 제거될 줄 알았지만, 기대와 달리 다운타임의 제거는 불가능했다는 점에서 의구심을 품고 다운타임을 제거하기 위한 시행착오가 많았다. 여러 개선 과정을 통해, &lt;strong&gt;다운타임을 약 0.02초로 대폭 감소시키는데 성공했다.&lt;/strong&gt; 또한 &lt;strong&gt;TCP 커넥션 처리 문제로 인해 발생한다는 것을 증명&lt;/strong&gt;해냈었다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;n1-문제-batchupdate-통한-쿼리-성능&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#n1-%EB%AC%B8%EC%A0%9C-batchupdate-%ED%86%B5%ED%95%9C-%EC%BF%BC%EB%A6%AC-%EC%84%B1%EB%8A%A5&quot; aria-label=&quot;n1 문제 batchupdate 통한 쿼리 성능 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;N+1 문제, BatchUpdate 통한 쿼리 성능&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/72042122-97b4-4e70-b593-656dda8be3a1/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이 외에도 사이드 프로젝트를 진행하면서 JPA 의 N+1 문제를 직접 마주쳤다. 이를 해결하도록 JdbcTemplate 의 벌크연산을 통해 쿼리 성능을 개선했던 경험도 정말 재밌었다. 이 과정에서 근본적인 기초 지식인 &lt;strong&gt;영속성 컨텍스트, 지연로딩과 즉시로딩&lt;/strong&gt;의 중요성을 몸소 꺠달았다. 또한 아직 ORM 이 해결하지 못하는 SQL Mapper 의 필요성에 대해 다시금 되짚어볼 수 있었다.&lt;/p&gt;
&lt;p&gt;옛날에 인강을 들으면서 이들이 왜 중요하다는건지 도무지 이해가지가 않았었는데, 역시 직접 트러블슈팅을 만나서 이론을 보충하니 왜 중요하다는지 알 수 있었다. 이 과정에서 &lt;code class=&quot;language-text&quot;&gt;야생형 학습법&lt;/code&gt; 이 나에게 얼마나 잘맞는 방식인지를 알 수 있었다.&lt;/p&gt;
&lt;p&gt;지금까지의 학습 및 프로젝트 관심사를 살펴보면 난 확실히 최적화, 자동화에 관심이 많다. 성능을 개선하고, 자동화를 통해 불편함을 감소시키는 것이 정말 즐거운 키워드다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;감사하게도-나에게-컨텍을-주신-여러-회사들&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%90%EC%82%AC%ED%95%98%EA%B2%8C%EB%8F%84-%EB%82%98%EC%97%90%EA%B2%8C-%EC%BB%A8%ED%85%8D%EC%9D%84-%EC%A3%BC%EC%8B%A0-%EC%97%AC%EB%9F%AC-%ED%9A%8C%EC%82%AC%EB%93%A4&quot; aria-label=&quot;감사하게도 나에게 컨텍을 주신 여러 회사들 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;감사하게도, 나에게 컨텍을 주신 여러 회사들&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a7ab17ee-379e-4ccf-8562-002de0563566/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이렇게 다년간 꾸준히 블로그를 운영한 결과, 나에게 메일로 좋은 컨텍을 주신 회사들이 몇 있었다. 이렇게나 대단한 회사나 그룹에서 나에게 교육과 컨설팅을 받고자 연락을 주시다니, 독학으로 개발을 시작한지 (컨텍 메일을 받았던 당시 기준) 1년도 안된 학부생인 나로썬 당황스럽기도 했지만, 잊지 못할 소중한 경험이었다.&lt;/p&gt;
&lt;p&gt;성장이 지체되어 있다고 생각이 들때, 내 장점을 찾고 자신감을 되찾을 수 있는 가장 정량적인, 객관적인 지표라고 생각한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;앞으로-어떻게-학습할-것인가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%9E%EC%9C%BC%EB%A1%9C-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%99%EC%8A%B5%ED%95%A0-%EA%B2%83%EC%9D%B8%EA%B0%80&quot; aria-label=&quot;앞으로 어떻게 학습할 것인가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;앞으로 어떻게 학습할 것인가?&lt;/h2&gt;
&lt;p&gt;지금껏 내가 원하는 공부만을 학습하는 것에 집중해왔으며, 특히 올해의 경우 나만의 프로그래밍 공부법을 만드는데 집중했다. 앞으로 나는 어떤 방향성과 목표를 가지고 학습해 나가야할까? 이에대한 상세한 방안, 계획, 생각등은 지금은 생략한다. 지금에선 대략적인 방향성만 잡아두는것을 목표로 둔다.&lt;/p&gt;
&lt;p&gt;추후 깊은 회고에 따라 생각이 변할 수 있기에, 현 내용이 그대로 실천되지 않아도 괜찮다.&lt;/p&gt;
&lt;h3 id=&quot;고수준이-아닌-저수준을-깊이있게-학습해야-할때&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%A0%EC%88%98%EC%A4%80%EC%9D%B4-%EC%95%84%EB%8B%8C-%EC%A0%80%EC%88%98%EC%A4%80%EC%9D%84-%EA%B9%8A%EC%9D%B4%EC%9E%88%EA%B2%8C-%ED%95%99%EC%8A%B5%ED%95%B4%EC%95%BC-%ED%95%A0%EB%95%8C&quot; aria-label=&quot;고수준이 아닌 저수준을 깊이있게 학습해야 할때 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;고수준이 아닌, 저수준을 깊이있게 학습해야 할때&lt;/h3&gt;
&lt;p&gt;지금껏 고수준을 지양하고 다짐해왔지만, 근본적이고 핵심적인 기술에 막힐떄가 많았다. 이를 알면서도 항상 등한시했다. 이젠 깊이있는 학습이 정말 절실히 필요할 떄이다. 근본부터 시작하여, 깊이있는 학습을 이어가보자. 어떤 학습 경로던 좋다. 아직도 매우 중요한 근본 지식들에서 구멍이 난 것들이 많다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/9315659d-08e7-48ac-8799-ae3c92a48dfd/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그렇지만 사실 고수준에 대해서 학습하고 싶은 리스트와 욕심은 많고도 많다. 막연하게 적어보자면 &lt;strong&gt;MSA, Circuit Breaker 패턴, DDD, 클린 아키텍처, 헥사고날 아키텍처, Apache Kafka, K8S, Redis, Elastic Search, Kibana, LogStash Github Actions, Grafana, Prometheus, ... 등이 있다.&lt;/strong&gt; (사실상 이들 중에서 이미 학습한 것들이 대부분이긴 하지만, 아직 깊게 파고들며 학습 한것이라고는 못 느낀다.) 너무너무나도 공부하고 싶지만... 욕심을 잠시 접어둘때다. &lt;strong&gt;좁은 범위 내에서 깊이있는 학습을 통해, T자형 인재가 되는데 집중해보자.&lt;/strong&gt; 지금 내 상황에선 T자형 인재가 되기에 최고의 적기라고 생각한다.&lt;/p&gt;
&lt;h3 id=&quot;블로그&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%94%EB%A1%9C%EA%B7%B8&quot; aria-label=&quot;블로그 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;블로그&lt;/h3&gt;
&lt;p&gt;이에 관한 자세한 내용은 별도로 다루겠다.&lt;/p&gt;
&lt;h3 id=&quot;글쓰기-프로젝트의-비율&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%80%EC%93%B0%EA%B8%B0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%9D%98-%EB%B9%84%EC%9C%A8&quot; aria-label=&quot;글쓰기 프로젝트의 비율 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;글쓰기, 프로젝트의 비율&lt;/h3&gt;
&lt;p&gt;이 또한 추후 자세히 다루겠다.&lt;/p&gt;
&lt;h3 id=&quot;책을-최대한-많이-읽기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B1%85%EC%9D%84-%EC%B5%9C%EB%8C%80%ED%95%9C-%EB%A7%8E%EC%9D%B4-%EC%9D%BD%EA%B8%B0&quot; aria-label=&quot;책을 최대한 많이 읽기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;책을 최대한 많이 읽기&lt;/h3&gt;
&lt;p&gt;위 생각 및 계획들보다 훨씬, 이번 회고중에서 가장 중요하다고 생각하는 것이다. 막막하고, 무언가 풀리는것이 없고, 현실적인 조언을 구하고 싶다면 책을 통해 해답을 찾아보자. 책만큼 빠르게 답을 찾을 수 있는 수단은 결코 없을것이다.&lt;/p&gt;
&lt;p&gt;이 또한 자세히 회고하겠다.&lt;/p&gt;
&lt;h3 id=&quot;나-자신에게-더-솔직해지자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%82%98-%EC%9E%90%EC%8B%A0%EC%97%90%EA%B2%8C-%EB%8D%94-%EC%86%94%EC%A7%81%ED%95%B4%EC%A7%80%EC%9E%90&quot; aria-label=&quot;나 자신에게 더 솔직해지자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;나 자신에게 더 솔직해지자&lt;/h3&gt;
&lt;p&gt;내 감정에 대해 더 진솔해질 필요가 있다. 이에대한 내용은 따로 회고를 쓰도록 한다 🙂&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;앞으로도 내 장점이 무엇인지를 다시 되짚어보며, 항상 감사하고도 소중했던 것들을 기록해보자. 아직 자세한 회고 및 계획 내용중 정말 소수에 내용에 대해서만 다루었다.&lt;/p&gt;
&lt;p&gt;타이탄들이 말하듯, 나는 공격적인 성향의 작은 타이탄이 되고 싶다. 매 순간순간 작은 하나라도 감사하며 행복한 삶을 살아보고 싶다. 나만의 주체적이며 매력적인 길을 걸어보고 싶다. 또한 안테암불로의 길을 걸어봐야겠다. 모두가 인정받지 못하고 분노할 때, 나만큼은 안테암불로의 길을 갈 것이다. 미래의 큰 자산이 되는 호의와 신용의 잔고를 쌓아두어야 겠다. 🙂&lt;/p&gt;
&lt;p&gt;2024년엔 내가 정말 멋지게 성장해있기를 바란다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[전략 패턴(Strategy Pattern)이란?]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/java/strategy-pattern/</link><guid isPermaLink="false">https://haon.site/haon/java/strategy-pattern/</guid><pubDate>Mon, 06 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;전략패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%EB%9E%B5%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;전략패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전략패턴&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;전략 패런&lt;/strong&gt;이란, 유사한 행위를 수행하는 여러 전략들에 대해 공통의 인터페이스로 정의해두고, 각 구체적인 전략에 대한 클래스를 각각 클래스로 캡슐화하고, 언제든 동적으로 전략 구현체를 바꿀 수 있도록하는 전략입니다. 전략패턴으로 구현된 코드는 직접 행위에 대한 코드를 수정할 필요없이, 전략만 변경하여 유연하게 확장할 수 있습니다.&lt;/p&gt;
&lt;h2 id=&quot;전략패턴-미적용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%EB%9E%B5%ED%8C%A8%ED%84%B4-%EB%AF%B8%EC%A0%81%EC%9A%A9&quot; aria-label=&quot;전략패턴 미적용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전략패턴 미적용&lt;/h2&gt;
&lt;p&gt;난수를 생성하는 다양한 전략이 존재한다고 해봅시다. 클라이언트로 부터 어떤 전략으로 난수를 생성할지 전달받고, 그에 알맞는 범위의 난수값을 생성하는 난수 생성기입니다. 이 안의 &lt;code class=&quot;language-text&quot;&gt;generateRandomNumber()&lt;/code&gt; 는 적절한 전략에 따라 &lt;code class=&quot;language-text&quot;&gt;if-else&lt;/code&gt; 분기 처리에 의해 난수를 처리하게 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; strategy&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; strategy&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; strategy&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateRandomNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;big&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 0~100 사이의 난수&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;small&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 0~10 사이의 난수&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;medium&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 0 ~ 50 사이의 난수&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;클라이언트는 여러 전략을 상황에 알맞게 사용하고 싶다면, 아래처럼 생성자에 매번 다른 인자값을 전달하여 인스턴스를 생성하고 난수생성 메소드를 호출해야 합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Client&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt; numberGenerator1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;big&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        numberGenerator1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateRandomNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt; numberGenerator2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;medium&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        numberGenerator2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateRandomNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt; numberGenerator3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;small&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        numberGenerator3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 이러한 구조는 &lt;code class=&quot;language-text&quot;&gt;OCP(개방 폐쇄 원칙)&lt;/code&gt; 을 위반하게 됩니다. 예를들어 기존 3가지 난수생성 전략 이외에 새로운 전략으로 &quot;micro&quot; 라는 전략을 추가하고, 기존 전략인 &quot;big&quot; 의 전략을 0&lt;del&gt;100 사이의 값이 아닌 0&lt;/del&gt;1000 의 숫자를 생성하게 만들었다고 해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 기존 코드에 변동이 일어났다!&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateRandomNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;big&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 0~1000 사이의 난수&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;small&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 0~10 사이의 난수&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;medium&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 0 ~ 50 사이의 난수&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;micro&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
           &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;결국 기존 코드에 영향을 주게되고, 이는 심각한 경우 자칫 서비스 전체에 악영향을 끼칠 수 있습니다. 지금의 예제의 경우는 매우 간단하기 때문에 별 영향이 없을것처럼 보일 수 있어도, 코드가 조금만 복잡해져도 하나가 문제가 터지면 겉잡을 수 없이 그 영향력은 클 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;전략패턴을-적용해보자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%EB%9E%B5%ED%8C%A8%ED%84%B4%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%9E%90&quot; aria-label=&quot;전략패턴을 적용해보자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전략패턴을 적용해보자!&lt;/h2&gt;
&lt;p&gt;앞서 말했듯이, 전략패턴은 유사 행위(전략)을 수행하는 단위별로 클래스로 나누어 캡슐화하고, 공통적인 특징은 인터페이스로 그룹화합니다. 앞선 코드의 경우 &quot;난수를 생성하는 행위&quot; 가 공통 관심사가 될 것이며, 어떻게 난수를 생성할지의 세부 행위가 &quot;전략&quot; 이 될 것입니다.&lt;/p&gt;
&lt;p&gt;인터페이스는 아래와 같이 정의할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerateStrategy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 각 전략별로 클래스를 세분화하여 구현해볼 수 있을겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigRangeStrategy&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerateStrategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 0~100 사이의 난수 1개&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SmallRangeStrategy&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerateStrategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 0~10 사이의 난수 1개&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MediumRangeStrategy&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerateStrategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; nt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 0~50 사이의 난수 1개&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또 기존 난수생성기는 생성자 주입으로 인터페이스 타입의 전략을 수용하게 됩니다. 이 인터페이스를 구현한 3가지 전략 구현 클래스중에 어떤것이 주입되던간에 유연하게 대응할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerateStrategy&lt;/span&gt; strategy&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NumberGenerateStrategy&lt;/span&gt; strategy&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; strategy&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateRandomNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약에 위 전략에서, 아까처럼 &quot;Micro&quot; 라는 신규 전략이 추가된다면 기존 코드에 파급력, 즉 악영향이 있을까요? 아닙니다. 신규 전략에 알맞게 캡슐화된 클래스를 새롭게 정의해주고, 난수 생성기 &lt;code class=&quot;language-text&quot;&gt;NumberGenerator&lt;/code&gt; 는 적절히 생성자 주입을 받아 난수를 생성하는 역할을 수행하면 끝입니다. 즉, 기존 코드 NumberGenerator 에는 아무런 변화도 발생하지 않습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MicroRangeStrategy&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerateStrategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; nt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 0~50 사이의 난수 1개&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;추가적으로 클라이언트는 아래처럼 난수 생성기를 적절히 생성자 주입을 통해 전략 클래스 인스턴스를 주입해주면 될겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Client&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; strategys &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;big&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;medium&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;small&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; strategy &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; strategys&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;big&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt; numberGenerator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BigRangeStrategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                numberGenerator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateRandomNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;medium&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt; numberGenerator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SmallRangeStrategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                numberGenerator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateRandomNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;small&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt; numberGenerator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberGenerator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SmallRangeStrategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                numberGenerator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateRandomNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;간혹 코드를 짜다보면 매우 비슷한 형태 또는 플로우를 지닌 여러 기능들이 존재할겁니다. 즉, &lt;strong&gt;매우 비슷한 로직 또는 형태를 지닌 여러 기능 및 케이스가 존재할때 사용하면&lt;/strong&gt; 매우 유용한 전략이 될겁니다. 또는 기능이 완전히 동일한데 요구사항 및 세부정책에 따라 자그마한 분기처리만 처리해도 좋을 경우에도 활용하면 좋은 패턴이 될겁니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;커멘드 패턴&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[소트웍스 엔솔로지의 객체치향 생활체조 원칙의 후반부]]></title><description><![CDATA[소트웍스 앤솔러지 객체치향 원칙 (Object Calisthenics) 의 전반부 를 먼저 참고하자. 원칙6. 모든 엔티티를 작게 유지한다. 50줄 이상 되는 클래스 또는 10개 파일 이상의 패지키는 없어야한다는 것입니다. 5…]]></description><link>https://haon.site/haon/oop/principle-of-daily-gymnastics-back/</link><guid isPermaLink="false">https://haon.site/haon/oop/principle-of-daily-gymnastics-back/</guid><pubDate>Sat, 30 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/%EC%86%8C%ED%8A%B8%EC%9B%8D%EC%8A%A4-%EC%95%A4%EC%86%94%EB%9F%AC%EC%A7%80-%EA%B0%9D%EC%B2%B4%EC%B9%98%ED%96%A5-%EC%83%9D%ED%99%9C%EC%B2%B4%EC%A1%B0-%EC%9B%90%EC%B9%99-Object-Calisthenics-%EC%9D%98-%EC%A0%84%EB%B0%98%EB%B6%80&quot;&gt;소트웍스 앤솔러지 객체치향 원칙 (Object Calisthenics) 의 전반부&lt;/a&gt; 를 먼저 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;원칙6-모든-엔티티를-작게-유지한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%996-%EB%AA%A8%EB%93%A0-%EC%97%94%ED%8B%B0%ED%8B%B0%EB%A5%BC-%EC%9E%91%EA%B2%8C-%EC%9C%A0%EC%A7%80%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;원칙6 모든 엔티티를 작게 유지한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙6. 모든 엔티티를 작게 유지한다.&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;50줄 이상 되는 클래스 또는 10개 파일 이상의 패지키는 없어야한다는 것입니다.&lt;/strong&gt; 50줄 이상인 경우 보통 클래스가 1가지 일만 하지 않기때문에, 코드의 가독성이 떨어지게 됩니다. 이는 곧 SRP 원칙을 위반하는 행위기기도 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;원칙7-2개를-초과하는-인스턴스-변수를-가진-클래스를-쓰지-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%997-2%EA%B0%9C%EB%A5%BC-%EC%B4%88%EA%B3%BC%ED%95%98%EB%8A%94-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EB%B3%80%EC%88%98%EB%A5%BC-%EA%B0%80%EC%A7%84-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%A5%BC-%EC%93%B0%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;원칙7 2개를 초과하는 인스턴스 변수를 가진 클래스를 쓰지 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙7. 2개를 초과하는 인스턴스 변수를 가진 클래스를 쓰지 않는다.&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/81f692aa-5287-42d1-b646-a868a7d6a87f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;인스턴수 변수가 많아질수록 클래스의 응집도는 낮아지는 것을 의미합니다.&lt;/strong&gt; 여기서 말하는 인스턴스 변수란 기본 원시타입 또는 컬렉션등을 말하는 것으로 보입니다. 즉, 일급 컬렉션이나 Wrappper 객체는 해당되지 않는 것 같습니다.&lt;/p&gt;
&lt;p&gt;쉽게말해, 한 클래스엔 최대 2개 이하의 인스턴스 변수만 배치하자는 것으로, 이를 지켰다면 최대한 클래스를 강제로 많이 분리함으로써 높은 응집도를 유지할 수 있게됩니다. 예를들어 Name 의 경우는 FirstName, LastName 으로 가능하다면 최대한 분리하자는 것이죠.&lt;/p&gt;
&lt;p&gt;덩치가 큰 객체를 작은 크기의 여러 객체로 분리하면, 자연스래 인스턴스 변수들을 적절한 곳에 배치할 수 있게됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;원칙8-일급-컬렉션을-쓴다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%998-%EC%9D%BC%EA%B8%89-%EC%BB%AC%EB%A0%89%EC%85%98%EC%9D%84-%EC%93%B4%EB%8B%A4&quot; aria-label=&quot;원칙8 일급 컬렉션을 쓴다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙8. 일급 컬렉션을 쓴다.&lt;/h2&gt;
&lt;p&gt;이는 &lt;code class=&quot;language-text&quot;&gt;원칙3. 모든 원시값과 문자열을 감싼다(Wrap)&lt;/code&gt; 와도 매우 유사한 원칙입니다. 컬렉션도 클래스로 레핑(Wrapping) 하지 않으면, 의미없는 객체가 될 수 있습니다.&lt;/p&gt;
&lt;p&gt;또한 일급 컬렉션을 사용하면 여기저기 흩여진 비즈니스 로직이 하나의 일급컬렉션 내부에 응집되고, 중복 코드가 줄어들게 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;원칙9-getter-setter-property-를-사용하지-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%999-getter-setter-property-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;원칙9 getter setter property 를 사용하지 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙9. Getter, Setter, Property 를 사용하지 않는다.&lt;/h2&gt;
&lt;p&gt;객체를 더 객체지향으로, 객체답게 활용하려면 &lt;strong&gt;객체가 충분히 혼자서 할 수 있는 작업은 최대한 객체에게 믿고 맡겨야한다는 것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;예를들어 아래처럼 Person 이 있고, 그 안에 원시타입의 money 가 있다고해봅시다. (더 좋은 설계를 위해선 money 를 원시타입이 아닌 Money 라는 클래스로 감싸줘야겠지만, 편의상 원시타입으로 정의했다.)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;비즈니스 로직상 이 세상엔 0원 이하의 금액을 보유한 사람은 없다고 해봅시다. 그러면 money 값을 추출해내서 체킹하기 위해선 아래처럼 getter 를 사용하여 값을 가져올것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; money &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;money &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 이렇게 작성하면 비즈니스 로직이 코드 전반으로 흩어지게 되고, 중복 코드가 발생할 수 있게됩니다. 위 코드는 아래처럼 충분히 Person 도메인 객체 내부에서 역할을 수행할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isInvaliBalance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; money &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;////////////////////////////////////////&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isInvalidBalance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;객체지향이란 지금껏 중요하다는 키워드를 인터넷에서 공부할때 많이 보게 되었고, 지금도 항상 어렵다고 느낍니다. 이러한 9가지 원칙을 원활히 지키는데까지 아직 실력향상이 많이 필요한 것 같네요. 이들을 모두 준수하면서 코드를 짜는것이 꽤 어렵겠지만, 지금부터 스스로 실력 향상을 위해 오랜시간이 걸려도 연습을 해보고자 합니다 😎&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;널 객체 패턴(Null Object Pattern)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;클린코드, 로버트 C. 마틴&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/thoughtworks-anthology-object-calisthenics/&quot;&gt;https://hudi.blog/thoughtworks-anthology-object-calisthenics/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jamie95.tistory.com/99&quot;&gt;https://jamie95.tistory.com/99&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[소트웍스 엔솔로지의 객체치향 생활체조 원칙의 전반부]]></title><description><![CDATA[학습배경 마틴 파울러의 소트웍스 엔솔러지에는 객제치향을 위한…]]></description><link>https://haon.site/haon/oop/principle-of-daily-gymnastics-front/</link><guid isPermaLink="false">https://haon.site/haon/oop/principle-of-daily-gymnastics-front/</guid><pubDate>Tue, 26 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;마틴 파울러의 소트웍스 엔솔러지에는 객제치향을 위한 9가지 원칙을 정의해 놓았습니다. 객제치향에 대한 설계법을 혼자서 깊게 고민할 때 계속 등장했던 키워드인데, 우선 자바에 대한 깊은 이해가 부족하다고 생각이 들었기 때문에 우선순위에서 밀려났습니다.&lt;/p&gt;
&lt;p&gt;지금부터 객제치향 원칙에 대해 공부해보고자 합니다 아직 객제치향에 대한 &quot;깊은&quot; 이해는 부족하기 때문에, 일단 최대한 기초를 이해하는데만 집중해보고 추후 제대로 이해한 내용을 기반으로 깊은 것들을 다루어보겠습니다. 또한 이번에는 9가지 원칙중 전반부에 대해서만 다루도록 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;원칙1-한-메소드에-오직-한-단계의-들여쓰기만-한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%991-%ED%95%9C-%EB%A9%94%EC%86%8C%EB%93%9C%EC%97%90-%EC%98%A4%EC%A7%81-%ED%95%9C-%EB%8B%A8%EA%B3%84%EC%9D%98-%EB%93%A4%EC%97%AC%EC%93%B0%EA%B8%B0%EB%A7%8C-%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;원칙1 한 메소드에 오직 한 단계의 들여쓰기만 한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙1. 한 메소드에 오직 한 단계의 들여쓰기만 한다&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;함수는 한 가지 일을 수행해야한다. 그 한가지만을 잘 해야한다. - Clean Code&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;한 메소드에 들여쓰기가 여러개 존재한다면, 해당 메소드는 여러가지 일을 수행한다고 봐도 무방합니다. 메소드는 본인이 맡은 일이 적을수록(잘개 쪼갤수록) 재사용성이 높고 디버깅도 유용합니다. &lt;strong&gt;즉, 코드의 들여쓰기와 중첩 구조가 깊어질수록 가독성이 떨어지므로, 들여쓰기는 최소화하는 것이 좋습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getLines&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt; stringBuilder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; j&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; j&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;J&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;one&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;two&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;메소드-추출기법extract-method&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%94%EC%86%8C%EB%93%9C-%EC%B6%94%EC%B6%9C%EA%B8%B0%EB%B2%95extract-method&quot; aria-label=&quot;메소드 추출기법extract method permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메소드 추출기법(Extract Method)&lt;/h3&gt;
&lt;p&gt;위와 같은 함수의 구조는 아래와 같이 개선해볼 수 있습니다. 마틴 파울러의 책에서 소개한 &lt;strong&gt;&quot;메소드 추출(Extract Method)&quot;&lt;/strong&gt; 기법을 활용하면 가독성이 올라갑니다. 메소드 추출은 코드으 일부분을 메소드로 분리하여, 코드의 복잡도를 낮추는 기법입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getLines&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt; stringBuilder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;repeatPrint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;repeatRows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt; stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;repeatRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;two&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;repeatRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt; stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    stringBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;one&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;원칙2-else-키워드를-쓰지-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%992-else-%ED%82%A4%EC%9B%8C%EB%93%9C%EB%A5%BC-%EC%93%B0%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;원칙2 else 키워드를 쓰지 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙2. else 키워드를 쓰지 않는다.&lt;/h2&gt;
&lt;p&gt;프로그래밍을 처음 접할 때 else 문을 많이 사용해봤을 것이지만, 저희는 else 키워드를 사용하지 않고도 분기처리 코드를 원활히 작성할 수 있습니다. 이 원칙은 &lt;code class=&quot;language-text&quot;&gt;원칙1. 한 메소드에 오직 한 단계의 들여쓰기만 한다&lt;/code&gt; 내용과도 밀접하게 연관된 내용입니다. &lt;strong&gt;else 문을 남용하면 자칫 중첩된 여러 조건문이 작성되고, 인덴트(들여쓰기)의 깊이가 늘어날 수 있기 때문입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printPriceStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; price&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;price &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;저렴합니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;price &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;무난합니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;price &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;비쌉니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;논외적으로 너무 비쌉니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드는 중복되는 조건문이 많이 겹쳐있으니 읽기 힘든 코드가 되었습니다. 정확히는, if 절이 중첩되면서 동작흐름 파악이 힘든 &lt;code class=&quot;language-text&quot;&gt;Arrow Anti Pattern&lt;/code&gt; 이 전형적인 사례입니다.&lt;/p&gt;
&lt;h3 id=&quot;early-pattern&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#early-pattern&quot; aria-label=&quot;early pattern permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Early Pattern&lt;/h3&gt;
&lt;p&gt;이렇듯 else 키워드를 사용하면 자칫 else 문 안에서 또 else 문을 작성하게 될 가능성이 있으니, 사용을 지양합시다. 위 코드는 아래처럼 &lt;code class=&quot;language-text&quot;&gt;Early Pattern&lt;/code&gt; 을 적용하여 개선할 수 있습니다.&lt;code class=&quot;language-text&quot;&gt;Early Pattern&lt;/code&gt; 이란, 조건문내의 조건이 일치하면 그 즉시 반환하는 디자인 패턴이니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printPriceStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; price&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;price &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;저렴합니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;price &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;무난합니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;price &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;비쌉니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;논외적으로 너무 비쌉니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 보호절(Guard Clause)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드에서 유의할 부분은 &lt;code class=&quot;language-text&quot;&gt;보호 구문(Guard Clause)&lt;/code&gt; 로 일반 케이스를 벗어난 부분에 대한 예외처리를 감싸주고 있습니다. 보호 구문이란, 실제 처리할 일반적으로 처리되는 if 분기문에 케이스가 잡히지않고 유효하지 않은 상황으로 분기되는 기타 특이사항에 대하여 처리하는 것을 뜻합니다. 위 경우는 &quot;논외적으로 너무 비쌉니다&quot; 부분이 보호절입니다.&lt;/p&gt;
&lt;h3 id=&quot;다형성polymorphism-기반의-디자인패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%ED%98%95%EC%84%B1polymorphism-%EA%B8%B0%EB%B0%98%EC%9D%98-%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;다형성polymorphism 기반의 디자인패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다형성(polymorphism) 기반의 디자인패턴&lt;/h3&gt;
&lt;p&gt;위처럼 간단한 경우는 &lt;code class=&quot;language-text&quot;&gt;보호 구문&lt;/code&gt; 을 사용하면 충분하나, 객체지향의 특성상 주요 특징인 다형성을 활용하는 방법도 있습니다. 다형성의 대표 사례로 &lt;code class=&quot;language-text&quot;&gt;전략 패턴(Strategy Pattern)&lt;/code&gt; 이나 &lt;code class=&quot;language-text&quot;&gt;널 객체 패턴(Null Object Pattern)&lt;/code&gt; 을 사용할 수도 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;전략패턴에 대한 자세한 이론은 &lt;a href=&quot;https://velog.io/@msung99/%EC%A0%84%EB%9E%B5-%ED%8C%A8%ED%84%B4Strategy-Pattern-%EC%9C%BC%EB%A1%9C-%EC%BD%94%EB%93%9C%EC%9D%98-%ED%99%95%EC%9E%A5%EC%84%B1%EC%9D%84-%EA%B3%A0%EB%A0%A4%ED%95%B4%EB%B3%B4%EC%9E%90&quot;&gt;전략 패턴(Strategy Pattern) 으로 코드의 확장성을 고려해보자!&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;널 객체 패턴에 대한 이론은 추후 자세히 학습하도록 하겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;원칙3-모든-원시값과-문자열을-포장wrap-한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%993-%EB%AA%A8%EB%93%A0-%EC%9B%90%EC%8B%9C%EA%B0%92%EA%B3%BC-%EB%AC%B8%EC%9E%90%EC%97%B4%EC%9D%84-%ED%8F%AC%EC%9E%A5wrap-%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;원칙3 모든 원시값과 문자열을 포장wrap 한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙3. 모든 원시값과 문자열을 포장(Wrap) 한다.&lt;/h2&gt;
&lt;p&gt;원시타입 데이터는 그 자체만으로 아무런 의미를 가지고 있지 않습니다. 원시값에 대한 의미를 변수명으로 추론하는 것도 한계가 있으므로, 실수를 범할 가능성이 커집니다.&lt;/p&gt;
&lt;p&gt;예를들어 아래와 같은 주문 도메인 클래스가 있다고 해봅시다. money 는 주문 가격정보를 뜻하며, distance 는 주문 배달거리를 뜻합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;money &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;distance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 아래와 같이 주문정보 객체가 생성된다고 해봅시다. 주문가격은 30만원이며, 배달거리는 1km 를 뜻합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; order &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;300000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 개발자가 실수로 로직을 잘못 설계하여 파라미터의 순서를 잘못 고려하여 값을 주입헀다고 해봅시다. 아래 객체가 뜻하는것은 가격이 1원이며, 배달거리는무려 300,000km 를 뜻하게됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; order &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;원시타입에-대한-집착&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%8B%9C%ED%83%80%EC%9E%85%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A7%91%EC%B0%A9&quot; aria-label=&quot;원시타입에 대한 집착 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원시타입에 대한 집착&lt;/h3&gt;
&lt;p&gt;위와 같은 실수를 범하게되는 주 원인은 무엇일까요? 우선 money 와 distance 는 int 라는 동일한 원시타입으로 표현됩니다. 따라서 이 필드들을 구분할 수 있는것은 오로지 변수명에만 의존하게 됩니다. 비슷한 이유로, 생성자의 파라미터 순서에만 의존하여 필드주입을 시도합니다. 이렇게 &lt;strong&gt;도메인을 표현시 원시타입만을 사용하여 표현하는 것을 &quot;원시 타입에 대한 집착(Primitive Obession) 안티패턴&quot;&lt;/strong&gt; 이라고 합니다.&lt;/p&gt;
&lt;h3 id=&quot;원사타입을-레핑하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%82%AC%ED%83%80%EC%9E%85%EC%9D%84-%EB%A0%88%ED%95%91%ED%95%98%EA%B8%B0&quot; aria-label=&quot;원사타입을 레핑하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원사타입을 레핑하기&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/03fc0315-df39-4f4e-9661-a2dfde148b2f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위 코드를 개선하면 아래와 같습니다. 원시값을 도메인 클래스로 감싸서 &lt;code class=&quot;language-text&quot;&gt;VO(Value Object)&lt;/code&gt; 로써 표현했고, 이로써 원시값에 정확한 의미가 부여되었습니다. 기존의 Money, Distance 와 관련된 비즈니스 검증 및 간단한 로직을 모두 Order 에서 도맡에 수행했겠지만, 이 무거운 역할과 책임을 Money, Distance 에게 적절히 분배했습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;VO 에 대한 자세한 내용은 &lt;a href=&quot;https://velog.io/@msung99/VOValue-Object-%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%B4%EA%B3%A0-%EC%9B%90%EC%8B%9C%ED%83%80%EC%9E%85%EC%9D%84-%EC%BA%A1%EC%8A%90%ED%99%94-%ED%95%A8%EC%9C%BC%EB%A1%9C%EC%8D%A8-%EC%96%BB%EA%B2%8C%EB%90%98%EB%8A%94-%EC%9D%B4%EC%A0%90%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot;&gt;VO(Value Object) 는 무엇이고, 원시타입을 캡슐화 함으로써 얻게되는 이점은 무엇인가?&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Distance&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Distance&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;money &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;distance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;money&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;money &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Distance&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;distance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;distance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 원칙3 은 추후 설명할 &lt;code class=&quot;language-text&quot;&gt;원칙8. 일급 컬렉션을 사용하기&lt;/code&gt; 와도 매우 유사한 원리와 규칙입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;원칙4-한-줄에-점을-하나만-찍는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%994-%ED%95%9C-%EC%A4%84%EC%97%90-%EC%A0%90%EC%9D%84-%ED%95%98%EB%82%98%EB%A7%8C-%EC%B0%8D%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;원칙4 한 줄에 점을 하나만 찍는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙4. 한 줄에 점을 하나만 찍는다.&lt;/h2&gt;
&lt;p&gt;저도 자주 실수를 범했던 문제로, &lt;code class=&quot;language-text&quot;&gt;getter&lt;/code&gt; 메소드에 점을 2개이상 사용하여 객체 멤버에 접근했었습니다. 앞선 Order 클래스에 대한 금액 Money 를 조회하고 싶을때, 아래와 같이 조회할 수 있을겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;order&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;백만원 이상의 주문가격은 허용되지 않습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드의 문제점은 무엇일까요? 바로 &lt;strong&gt;한줄에 점이 2개이상 찍이면서 결함도가 높아졌다는 것입니다.&lt;/strong&gt; 위 Order 클래스에 대한 로직은 Order 뿐 아니라 Money 에 대한 의존성도 동시에 갖게 되었습니다.&lt;/p&gt;
&lt;h3 id=&quot;디미터-법칙demeter-law&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%94%94%EB%AF%B8%ED%84%B0-%EB%B2%95%EC%B9%99demeter-law&quot; aria-label=&quot;디미터 법칙demeter law permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;디미터 법칙(Demeter Law)&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;디미터 법칙(Demeter Law) : &quot;낯선 이와 대화하지 말고, 친구하고만 대화하라&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이 원칙4 는 디미터 법칙은 쉽게 풀어썼다고 보면 됩니다. &lt;strong&gt;즉, 자신 소유의 객체, 자신이 생성한 객체, 그리고 누군가 준(파라미터로) 객체에만 메시지를 보내야함을 의미합니다.&lt;/strong&gt; 그렇지 않을경우, 다른 객체에 너무 깊숙하게 관여하게 되는것이며(과한 의존성) 이는 캡슐화를 어기는것이기도 합니다.&lt;/p&gt;
&lt;p&gt;쉽게 말하자면 &lt;strong&gt;객체간의 의존성이 그래프 형태를 띄고있을때, 한 객체에서 객체 그래프를 따라가서 멀리 떨어진 객체에게 메세지를 보내는 설계를 피하라는 것입니다.&lt;/strong&gt; 이는 객체간의 결함도를 높이는 행위이기 때문이죠.&lt;/p&gt;
&lt;p&gt;위 코드는 아래와 같이 개선할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;order&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isOverFlowMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;백만원 이상의 주문가격은 허용되지 않습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isOverFlowMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;점을 하나만 사용하여 코드를 개선했습니다. Order 의 Money 의 메소드를 호출하는 것이 아니라, Order 에게 직접 질문을 던지는 방식으로 개선되었습니다. 이로써 Order 는 자신이 가지고 있는 객체를 적절히 활용하여 더 능동적으로 행위를 수행하게 되었습니다.&lt;/p&gt;
&lt;h3 id=&quot;디미터-법칙을-적용하지-않는-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%94%94%EB%AF%B8%ED%84%B0-%EB%B2%95%EC%B9%99%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;디미터 법칙을 적용하지 않는 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;디미터 법칙을 적용하지 않는 경우&lt;/h3&gt;
&lt;p&gt;여기서 &lt;code class=&quot;language-text&quot;&gt;스트림(stream)&lt;/code&gt; 처럼 메소드 체이닝(chaining) 하는 경우는 점을 여러번 사용해도 이미 디미터 법칙을 위반하지 않습니다. 또 &lt;code class=&quot;language-text&quot;&gt;DTO&lt;/code&gt; 의 경우 내부 구조를 외부에 유출하는 것을 목적으로 설계되기 떄문에 디미터 법칙을 특별히 적용하지 않습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;원칙5-줄여쓰지-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%995-%EC%A4%84%EC%97%AC%EC%93%B0%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;원칙5 줄여쓰지 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙5. 줄여쓰지 않는다.&lt;/h2&gt;
&lt;p&gt;메소드, 클래스등에 대한 이름을 과도하게 줄인다면 되려 코드의 가독성을 저해합니다. 무조건 짧다고 좋은것이 아니죠.&lt;/p&gt;
&lt;p&gt;축약하고 싶은 욕구가 생기는 이유는 이름이 길기 때문입니다. 이름이 긴 이유는 해당 클래스, 메소드가 혼자 너무 많은 일을 수행하기 때문입니다. 만약 그게 클래스라면 &lt;code class=&quot;language-text&quot;&gt;SRP(단일 책임 원칙)&lt;/code&gt; 을 위반하고 있을 가능성이 큽니다.&lt;/p&gt;
&lt;p&gt;1~2단어 정도로 구성된 경우엔 축약하는 것이 권장되며, 문맥이 중복되는 경우 과감히 이름을 축약합시다. 예를들어 Order 클래스의 orderInfo 라는 메소드가 있다면, info 로 이름을 개선할 수 있을겁니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금껏 9가지의 원칙중에 5가지에 대해 살펴봤습니다. 다음번엔 나머지 4가지 원칙에 대하여 학습해보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;널 객체 패턴(Null Object Pattern)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/thoughtworks-anthology-object-calisthenics/&quot;&gt;https://hudi.blog/thoughtworks-anthology-object-calisthenics/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jamie95.tistory.com/99&quot;&gt;https://jamie95.tistory.com/99&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blogshine.tistory.com/241&quot;&gt;https://blogshine.tistory.com/241&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[DTO 의 개념과 역할, 사용 계층]]></title><description><![CDATA[DTO   는 계층 간 데이터를 전송을 위해 도메인 모델 대신에 사용하는 객체입니다. Tecoble 기술블로그 에 따르면, DTO 는 순수하게 데이터를 저장하고, 데이터에 대한 getter 와 setter…]]></description><link>https://haon.site/haon/spring/dto/</link><guid isPermaLink="false">https://haon.site/haon/spring/dto/</guid><pubDate>Wed, 13 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;dto&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dto&quot; aria-label=&quot;dto permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DTO&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/91e3f0c6-60e1-4e9a-b9bd-c2574176c0fc/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DTO(Data Transfer Object)&lt;/code&gt; 는 &lt;strong&gt;계층 간 데이터를 전송을 위해 도메인 모델 대신에 사용하는 객체&lt;/strong&gt;입니다. &lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2020-08-31-dto-vs-entity/&quot;&gt;Tecoble 기술블로그&lt;/a&gt; 에 따르면, DTO 는 순수하게 데이터를 저장하고, 데이터에 대한 getter 와 setter 만을 가져야한다고 합니다. 그 어떠한 비즈니스 로직을 가져서도 안되면, 오로지 저장, 검색등의 로직만을 보유할 수 있다고합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveDto&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; upResult&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; downResult&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MovedResultDto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; upResult&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; downResult&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;upResult &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; upResult&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;downResult &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; downResult&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveDto&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MoveResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; results&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveDto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;mapToSignal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;results&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mapToSignal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;results&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DOWN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mapToSignal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MoveResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; moveResults&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; moveResults&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;moveResult &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; moveResult&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSignal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getUpResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; upResult&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getDownResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; downResult&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위처럼 보듯이 DTO 는 아무런 로직을 포함하고 있지 않으며, &lt;code class=&quot;language-text&quot;&gt;final&lt;/code&gt; 로 불변의 특성을 지니고 있고, &lt;code class=&quot;language-text&quot;&gt;getter&lt;/code&gt; 만을 포함하고 있습니다. 지금보니 &lt;code class=&quot;language-text&quot;&gt;setter&lt;/code&gt; 는 별도로 보유하고 있지않은데, 필요에 따라선 setter 를 추가하는것은 자유롭게 해도 됩니다. 다만 위 경우는 setter 를 활용해야하는 상황과 이유가 발생하지 않았기 떄문에 별도의 코드가 존재하지 않는것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;왜-dto-를-사용하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-dto-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;왜 dto 를 사용하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 DTO 를 사용하는가?&lt;/h2&gt;
&lt;h3 id=&quot;1-도메인-모델을-캡슐화하여-보호할-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EB%8F%84%EB%A9%94%EC%9D%B8-%EB%AA%A8%EB%8D%B8%EC%9D%84-%EC%BA%A1%EC%8A%90%ED%99%94%ED%95%98%EC%97%AC-%EB%B3%B4%ED%98%B8%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;1 도메인 모델을 캡슐화하여 보호할 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 도메인 모델을 캡슐화하여 보호할 수 있다.&lt;/h3&gt;
&lt;p&gt;DTO 는 어쩌면 도메인 모델을 보호하는 것이 주 목적이라고 볼 수 있겠습니다. &lt;strong&gt;활용용도는 순수히 계층간에 값을 전송하는데에만 초점이 맞춰져있습니다.&lt;/strong&gt; DTO 가 아닌 도메인 모델을 계층간의 데이터 송수신에 직접 활용한다면, 도메인과 뷰 계층간의 강한 결합 및 의존성이 발생하게 됩니다. 추후 요구사항으로 인한 뷰 계층에 전송해야하는 세부 데이터가 변경된다면, 매번 도메인 코드를 변경해야하는 상황이 발생할 수 있습니다 &lt;strong&gt;즉, DTO 를 활용하면 도메인 모델을 계층간의 전송에 직접 활용하지 않으므로 보호할 수 있게됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-도메인-설계를-외부에-직접-유출시키지-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%84%A4%EA%B3%84%EB%A5%BC-%EC%99%B8%EB%B6%80%EC%97%90-%EC%A7%81%EC%A0%91-%EC%9C%A0%EC%B6%9C%EC%8B%9C%ED%82%A4%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;2 도메인 설계를 외부에 직접 유출시키지 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 도메인 설계를 외부에 직접 유출시키지 않는다.&lt;/h3&gt;
&lt;p&gt;또, &lt;strong&gt;도메인 모델 대신 DTO 를 활용함으로써 보안 문제를 한층 해결할 수 있게됩니다.&lt;/strong&gt; 만약 DTO 이 아니라 도메인 모델을 계층간 전달에 사용하면, UI 계층에서 도메인 모델의 메소드를 호출하거나 상태를 변경시킬 수 있습니다. 불필요하게 도메인 속성을 모두 외부에 유출시키면 보안 문제가 발생할 수 있게됩니다. 곧 도메인 모델을 캡슐화하여 보호할 수 있게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;3-검증validation-코드를-도메인-코드와-분리-가능하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%EA%B2%80%EC%A6%9Dvalidation-%EC%BD%94%EB%93%9C%EB%A5%BC-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%BD%94%EB%93%9C%EC%99%80-%EB%B6%84%EB%A6%AC-%EA%B0%80%EB%8A%A5%ED%95%98%EB%8B%A4&quot; aria-label=&quot;3 검증validation 코드를 도메인 코드와 분리 가능하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 검증(Validation) 코드를 도메인 코드와 분리 가능하다.&lt;/h3&gt;
&lt;p&gt;도메인 코드는 비즈니스 로직과 정책, 그리고 각 테이블간의 매핑관계와 같은 유즈케이스 핵심 코드가 흐르고 있는 코드입니다. 만약 도메인 모델에서 Empty 인지, Null 한지등의 모든 검증코드를 포함한다면 불필요하게 가독성이 떨어지고 클래스가 더욱이 복잡해집니다.&lt;/p&gt;
&lt;p&gt;따라서 각 요청에대한 Validation 을 DTO 에서 정의한다면, 도메인 및 엔티티 클래스를 핵심 비즈니스 로직 및 정책에만 집중할 수 있게됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;dto-를-어떤-계층에서-변환하는-것이-좋은가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dto-%EB%A5%BC-%EC%96%B4%EB%96%A4-%EA%B3%84%EC%B8%B5%EC%97%90%EC%84%9C-%EB%B3%80%ED%99%98%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%B4-%EC%A2%8B%EC%9D%80%EA%B0%80&quot; aria-label=&quot;dto 를 어떤 계층에서 변환하는 것이 좋은가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DTO 를 어떤 계층에서 변환하는 것이 좋은가?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;A Service Layer defines an application’s boundary [Cockburn PloP] and its set of available operations from the perspective of interfacing client layers. It encapsulates the application’s business logic, controlling transactions and coor-dinating responses in the implementation of its operations.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;마틴 파울러 - &lt;a href=&quot;https://martinfowler.com/eaaCatalog/serviceLayer.html&quot;&gt;Service Layer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;많은 의견들을 찾아보니, DTO 가 영속성 계층까지 도달하는 것은 지양하는 것으로 보입니다. 마틴파울러는 Service 계층이란 도메인을 보호하는 계층이라고 말합니다. 따라서 이 사항을 준수하려면 프레젠테이션(Presentation) 계층까지 도메인 유출되선 안되며, &lt;strong&gt;도메인 서비스 계층에서 도메인 모델이 DTO 로 변환되어 컨트롤러로 전달되는것이 맞는 것 같습니다.&lt;/strong&gt; 반대로 컨트롤러에서 서비스 계층으로 전달될때도 요청 모델이 DTO 로 변환되는 것이 좋다고봅니다.&lt;/p&gt;
&lt;h3 id=&quot;계층간-dto-전송의-일반적인-구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%84%EC%B8%B5%EA%B0%84-dto-%EC%A0%84%EC%86%A1%EC%9D%98-%EC%9D%BC%EB%B0%98%EC%A0%81%EC%9D%B8-%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;계층간 dto 전송의 일반적인 구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;계층간 DTO 전송의 일반적인 구조&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/b31845e6-1f3c-4b6c-855e-bd5de310b94b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;일반적인 상황에선, 대부분 위처럼 컨트롤러는 DTO 형태의 데이터로 요청을 서비스 계층에 넘기고, 서비스는 DTO 를 엔티티(도메인 모델) 로 변환하는 구조를 지닙니다. 만약 View 계층으로 부터 전달받은 데이터가 DTO 타입이 아니라면, DTO 로 변환후 서비스 계층에 전달해야합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7983661b-a6f9-46d3-8c9f-acc28b3006b7/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;반면 서비스 요청/응답시 서비스 계층의 메소드가 사용하는 서비스 DTO 를 별도로 만들고, DTO 와 서비스 DTO 를 매핑하는 &lt;code class=&quot;language-text&quot;&gt;Mapper(매퍼)&lt;/code&gt; 를 중간에 끼워넣는 방식도 있다고합니다. Mapper 를 사용하면 컨트롤러와 서비스 계층이 완전히 분리됨으로써 결함도가 낮아지는 효과를 볼 수 있다고 합니다. 아직은 실제로 경험해보지 않았기떄문에 공감대가 없어서, 추후 매퍼를 직접 배치해보는 학습이 필요한 것 같습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;검증validation-은-도대체-누가-수행하는거야&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%80%EC%A6%9Dvalidation-%EC%9D%80-%EB%8F%84%EB%8C%80%EC%B2%B4-%EB%88%84%EA%B0%80-%EC%88%98%ED%96%89%ED%95%98%EB%8A%94%EA%B1%B0%EC%95%BC&quot; aria-label=&quot;검증validation 은 도대체 누가 수행하는거야 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;검증(Validation) 은 도대체 누가 수행하는거야!?&lt;/h2&gt;
&lt;p&gt;DTO 를 공부하면서 가장 혼동스러웠던 점은, 검증(Validation) 을 정확히 어디서 수행하는 것이 좋은지에 대한 것이였습니다. 많은 분들의 견해를 찾아보니 검증은 DTO 또는 컨트롤러에서 검증코드를 따로 구성하는 것에 대한 의견이 분분했습니다. 사실 앞서 제가 언급했기를 검증을 항상 DTO 에서 구성해야한다는 식으로 말했지만, 이에 대한 정답은 없는 것 같습니다.&lt;/p&gt;
&lt;p&gt;다만 제 견해를 정리해보자면, &lt;strong&gt;검증(Validation) 처리는 DTO 가 아닌 컨트롤러단에서 별도의 Validation 메소드를 추출하고 검증처리를 수행&lt;/strong&gt;하는 것이 더 좋을것이라는 판단이 듭니다. DTO 의 정의에 대해 다시 돌아가보면, DTO 는 마치 택배 상자와 같습니다. 값을 순수히 전달하는 목적이기 때문이죠. 그런데 이런 택배상자가 본인이 스스로 &quot;내가 불량 상품을 포함하고 있는데?&quot; 라는 검증을 수행하는것은 역할에 부적합하지 않나라는 생각이 듭니다.&lt;/p&gt;
&lt;p&gt;따라서 컨트롤러 내에서도 충분히 검증이 처리 가능한경우 검증 처리 메소드를 따로 추출하여 검증 후 DTO 를 생성하는 것이 올바르지 않나라는 생각입니다. 다만 컨트롤러 내에서 처리하는게 부적합한 특별한 상황인 경우에만 DTO 내에서 처리하는게 좋다라는 견해입니다.&lt;/p&gt;
&lt;p&gt;무엇보다 가장 중요한 것은, 검증 처리가 도메인 계층에서 수행되는 상황을 방지하는것입니다. 이를 준수한다면 도메인이 추후 요구사항이 있더라도 큰 변화가 없는 좋은 클린코드라고 생각합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;VO 와 DTO&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2020-08-31-dto-vs-entity/&quot;&gt;https://tecoble.techcourse.co.kr/post/2020-08-31-dto-vs-entity/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/&quot;&gt;https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/java-dto-pattern&quot;&gt;https://www.baeldung.com/java-dto-pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://creampuffy.tistory.com/188&quot;&gt;https://creampuffy.tistory.com/188&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sedangdang.tistory.com/296&quot;&gt;https://sedangdang.tistory.com/296&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[자바의 싱글톤(SingleTon) 패턴 구현 방법 6가지, Bill Pugh Solution]]></title><description><![CDATA[학습배경 싱글톤에 대해 선수 학습한 경험이 있다. 이에 대한 내용은 [JAVA] 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (feat…]]></description><link>https://haon.site/haon/java/singleton/</link><guid isPermaLink="false">https://haon.site/haon/java/singleton/</guid><pubDate>Tue, 12 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;싱글톤에 대해 선수 학습한 경험이 있다. 이에 대한 내용은 &lt;a href=&quot;https://velog.io/@msung99/JAVA-%EC%9E%90%EC%9B%90%EC%9D%84-%EC%A7%81%EC%A0%91-%EB%AA%85%EC%8B%9C%ED%95%98%EC%A7%80-%EB%A7%90%EA%B3%A0-%EC%9D%98%EC%A1%B4-%EA%B0%9D%EC%B2%B4-%EC%A3%BC%EC%9E%85%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC-feat.-%EC%8B%B1%EA%B8%80%ED%86%A4&quot;&gt;[JAVA] 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (feat. 싱글톤)&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이전에 싱글톤의 기본 지식을 다룬 이력이 있으며, 프리코스 문제 풀이에도 싱글톤을 구현한 경험이 있기 떄문에 기초적인 선수지식은 생략합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;싱글톤singleton&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%B1%EA%B8%80%ED%86%A4singleton&quot; aria-label=&quot;싱글톤singleton permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;싱글톤(SingleTon)&lt;/h2&gt;
&lt;p&gt;간단히만 복습겸 싱글톤을 사용하는 이유에 대해 되짚고 넘어가봅시다. 싱글톤을 적용한다면, &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 고정된 메모리 영역을 가지고 하나의 인스턴스만 사용하기 떄문에 메모리 낭비를 방지할 수 있습니다. &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 싱글톤 클래스의 인스턴스는 전역이기 떄문에, 다른 클래스의 인스턴스들이 데이터를 공유하기도 쉽습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;싱글톤-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%B1%EA%B8%80%ED%86%A4-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;싱글톤 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;싱글톤 구현&lt;/h2&gt;
&lt;p&gt;싱글톤의 구현 방법은 정말 다양합니다. 이전부터 싱글톤의 단점을 보완하고자 여러 구현방식에 걸쳐서 발전해왔습니다.&lt;/p&gt;
&lt;h3 id=&quot;eager-initalization&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#eager-initalization&quot; aria-label=&quot;eager initalization permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Eager initalization&lt;/h3&gt;
&lt;p&gt;가장 먼저 등장한 방식으로, &lt;code class=&quot;language-text&quot;&gt;즉시 초기화(Eager initalization)&lt;/code&gt; 방식이 있습니다. 싱글톤을 구현해본 사람이라면 다들 이 방식을 보편적으로 많이 사용해봤을 겁니다. 이 방식은 필드에 자기자신 타입의 인스턴스를 필드로써 하나 보유하고, 바로 초기화하는 방식입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;장점&lt;/h4&gt;
&lt;p&gt;장점은 인스턴스 생성이 굉장히 빠르다는 것입니다.&lt;/p&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;p&gt;생성한 인스턴스를 사용하는 일이 없더라도 프로그램을 실행하면 static 을 로딩하면서 바로 메모리 공간을 차지해버립니다. 즉, 클라이언트에서 이렇게 생성된 인스턴스를 사용하지 않더라도 인스턴스가 항상 무조건 생성되버리며, 무엇보다 &lt;strong&gt;예외 처리를 할 수 있는 방법이 없습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;static-block-initalization&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#static-block-initalization&quot; aria-label=&quot;static block initalization permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Static Block initalization&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RunTimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;싱글톤 객체를 생성하는데 실패했습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 단점중에 &quot;예외처리&quot; 를 보완하고자, &lt;code class=&quot;language-text&quot;&gt;static&lt;/code&gt; 블록을 추가하고, 그 안에서 인스턴스 생성을 시도하면서 동시에 예외 처리를 넣는 방식이 등장했습니다. 이로써 인스턴스 생성시 즉시 예외처리가 가능해진다는 점을 보완했습니다. 하지만 여전히 처음 시작할 때 바로 초기화 된다는 단점을 가지고 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;lazy-initalization&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lazy-initalization&quot; aria-label=&quot;lazy initalization permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Lazy initalization&lt;/h3&gt;
&lt;p&gt;따라서 컴파일 로딩 시점에 &quot;즉시&quot; 인스턴스를 생성하려는 행위를 방지하고자, 지연 초기화(Lazy initalization) 방식이 등장했습니다. 이는 처음에 필드를 생성할때는 초기화하지 않고, &lt;code class=&quot;language-text&quot;&gt;getInstance()&lt;/code&gt; 메소드를 호출하게 되면 그제서야 검사를 시도합니다. 아래처럼 인스턴스가 null 이라면, 해당 인스턴스를 초기화를 시도합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;instance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;장점&lt;/h4&gt;
&lt;p&gt;이로써 즉시 인스턴스가 생성되는 것이 아니라, 클라이언트가 인스턴스를 사용하려고 하는 시점에 천천히 초기화 및 인스턴스가 생성되므로 메모리를 절약할 수 있게됩니다.&lt;/p&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;p&gt;하지만 단점은 &lt;code class=&quot;language-text&quot;&gt;쓰레드 안전(Thread-Safe)&lt;/code&gt; 하지 않다는 것입니다. 이게 만약에 여러개의 쓰레드가 동시에 요청하게 되면 싱글톤의 &lt;strong&gt;&quot;유일성(Unique)&quot;&lt;/strong&gt; 을 보장한다는 특성을 어길 수 있게됩니다. 즉, 멀티쓰레드 환경에서 동시에 인스턴스 생성을 요청하는 상황에 발생하면 다중 인스턴스가 생길 수 있게 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;thread-safe-initalization&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#thread-safe-initalization&quot; aria-label=&quot;thread safe initalization permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Thread-safe initalization&lt;/h3&gt;
&lt;p&gt;위 지연 초기화(Lazy initalization) 는 &lt;code class=&quot;language-text&quot;&gt;쓰레드 안전(Thread-safe)&lt;/code&gt; 하지 못하다는 것이 단점이므로, 이를 보완할 수 있게 쓰레드 안전한 초기화 방법이 등장합니다. 자바에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;synchronized&lt;/code&gt; 키워드를 활용하는 방식으로, 쓰레드가 이 인스턴스를 생성하는 &lt;code class=&quot;language-text&quot;&gt;임계영역(Critical Section)&lt;/code&gt; 에 진입할 때 순차적으로 진입할 수 있도록 하는 방식입니다. 즉, 동시에 여러 쓰레드가 인스턴스 생성 메소드에 접근하지 못하므로 하나의 인스턴스만 생성될 수 있게 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;instance &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;장점&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;쓰레드 안전(Thread-safe)&lt;/code&gt; 하므로 &quot;유일성&quot; 이 보장됩니다.&lt;/p&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;p&gt;하지만 &lt;code class=&quot;language-text&quot;&gt;synchronized&lt;/code&gt; 키워드로 인해 성능이 저하될 수 있습니다. 하나의 쓰레드만 접근이 가능하므로, 성능이 느려질 수 있는 비효율적인 방법인 것이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;double-checked-locking&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#double-checked-locking&quot; aria-label=&quot;double checked locking permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Double-Checked Locking&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;instance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;instance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      	&lt;span class=&quot;token keyword&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;instance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            	instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그래서 등장한 것이 2번 체크를 하는 방식입니다. 첫번째로 인스턴스가 null 인지를 체크합니다. 조건문에서 인스턴스가 이미 생성되었는지 아닌지를 체크하고, 생성된게 아직 없는 경우에만 &lt;code class=&quot;language-text&quot;&gt;synchornized&lt;/code&gt; 키워드에 기반하여 &lt;code class=&quot;language-text&quot;&gt;쓰레드 안전(Thread Safe)&lt;/code&gt; 하게 인스턴스를 생성하는 방식입니다. 즉, if 문으로 앞단에서 먼저 필터링하여 &lt;code class=&quot;language-text&quot;&gt;synchornized&lt;/code&gt; 가 최소한으로 사용되게 하는 방식인 것으로, 이전 방식보다 성능이 더 좋아지게됩니다.&lt;/p&gt;
&lt;h4&gt;장점&lt;/h4&gt;
&lt;p&gt;synchornized 로 인한 성능 저하 비용이 최소화되었으며, 쓰레드 안전하게 &quot;유일성&quot; 의 특징을 지닌 인스턴스 생성이 가능하다.&lt;/p&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;p&gt;가독성이 떨어진다. 메소드 내부의 코드가 많아졌으며 인덴트도 늘어난다. (위 경우 인덴트가 3이다.)&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;bill-pugh-solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bill-pugh-solution&quot; aria-label=&quot;bill pugh solution permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Bill Pugh Solution&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SingleTonHelper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;INSTANCE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoveRepository&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SingleTonHelper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;INSTANCE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 가독성 저하 원인을 보완하고자, &lt;code class=&quot;language-text&quot;&gt;정적 내부 클래스(static inner class)&lt;/code&gt; 클래스를 만들어서 &lt;code class=&quot;language-text&quot;&gt;헬퍼 클래스(Helper Class)&lt;/code&gt; 로써 동작하게 만들었습니다. 이 헬퍼 클래스 안에 인스턴스를 &lt;code class=&quot;language-text&quot;&gt;static final&lt;/code&gt; 로 보유하고 있습니다. &lt;code class=&quot;language-text&quot;&gt;static&lt;/code&gt; 의 특징으로 인해 메모리에 미리 할당했으며, 내부의 인스턴스 변수도 &lt;code class=&quot;language-text&quot;&gt;final&lt;/code&gt; 로 붙여서 &lt;strong&gt;&quot;불변성(immutable)&quot;&lt;/strong&gt; 의 특징을 살릴 수 있게 되었습니다.&lt;/p&gt;
&lt;p&gt;이로써 인스턴스가 &lt;code class=&quot;language-text&quot;&gt;getInstance()&lt;/code&gt; 를 활용하여 호출될 때, 동시에 여러 쓰레드가 호출되더라도 메모리에 미리 올라간 동일한 인스턴스를 헬퍼 클래스를 통해 전달받기 떄문에, 동시성 문제도 깔끔히 해결되고 코드의 가독성도 올라갔습니다.&lt;/p&gt;
&lt;p&gt;따라서 여러 쓰레드로 인한 동시성 문제도 해결할 수 있게 됩니다. 또한 메소드 길이도 짧아지고 깔끔해졌습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;싱글톤은-정말-oop-일까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%B1%EA%B8%80%ED%86%A4%EC%9D%80-%EC%A0%95%EB%A7%90-oop-%EC%9D%BC%EA%B9%8C&quot; aria-label=&quot;싱글톤은 정말 oop 일까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;싱글톤은 정말 OOP 일까?&lt;/h2&gt;
&lt;p&gt;싱글톤에 대해서 조금이라도 학습해봤다면, &quot;싱글톤을 지양하라&quot; , &quot;안티패턴인가?&quot; 등의 논쟁거리를 자주 접해봤을겁니다.&lt;/p&gt;
&lt;h3 id=&quot;상태state-와-공유자원을-보유한-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%81%ED%83%9Cstate-%EC%99%80-%EA%B3%B5%EC%9C%A0%EC%9E%90%EC%9B%90%EC%9D%84-%EB%B3%B4%EC%9C%A0%ED%95%9C-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;상태state 와 공유자원을 보유한 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;상태(State) 와 공유자원을 보유한 경우&lt;/h3&gt;
&lt;p&gt;만약 싱글톤으로 구현된 클래스가 &lt;strong&gt;&quot;상태(state)&quot;&lt;/strong&gt; 를 보유했다면 문제가 발생할 수 있습니다. 전역적으로 모든 쓰레드가 공유하는 인스턴스이므로, 여러 쓰레드가 상태를 바꾸게되면 &lt;code class=&quot;language-text&quot;&gt;데이터의 무결성&lt;/code&gt; 을 해치게 됩니다. 예를들어 &quot;보드게임&quot; 을 진행한다고 했을때, 이 보드게임판 클래스를 모든 유저가 공유하는 싱글톤으로 구현했다고 해봅시다. 이 경우는 모든 유저가 하나의 상태값 및 공유자원을 동시에 변경하고 조회할 수 있기 떄문에, 데이터의 일관성을 해칠 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;생성자에서-의존성이-드러나지-않는다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%9D%EC%84%B1%EC%9E%90%EC%97%90%EC%84%9C-%EC%9D%98%EC%A1%B4%EC%84%B1%EC%9D%B4-%EB%93%9C%EB%9F%AC%EB%82%98%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-label=&quot;생성자에서 의존성이 드러나지 않는다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;생성자에서 의존성이 드러나지 않는다.&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;getInstance()&lt;/code&gt; 로만 하나를 생성하므로 의존성이 잘 드러나지 않습니다. 따라서 상태가 없는 객체가, 또는 설계상으로 유일성을 보장해야하는 대상에 대해서만 싱글톤으로 구현해야합니다.&lt;/p&gt;
&lt;h3 id=&quot;그래서-싱글톤은-안티패턴인가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%9E%98%EC%84%9C-%EC%8B%B1%EA%B8%80%ED%86%A4%EC%9D%80-%EC%95%88%ED%8B%B0%ED%8C%A8%ED%84%B4%EC%9D%B8%EA%B0%80&quot; aria-label=&quot;그래서 싱글톤은 안티패턴인가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그래서 싱글톤은 안티패턴인가?&lt;/h3&gt;
&lt;p&gt;싱글톤을 구현하는 대부분의 경우는 클래스의 객체를 미리 생성하고, &lt;strong&gt;정적 메소드&lt;/strong&gt;를 활용해서 싱글톤을 구현하게 됩니다. 따라서 싱글톤과 사용하는 클래스 사이에 강한 의존성, 높은 결합이 생기게되어서 수정, 단위테스트의 어려움이 발생합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=C6CczyrkYXU&quot;&gt;[10분 테코톡] 🧇 크로플의 싱글턴과 정적클래스&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2020-11-07-singleton/&quot;&gt;https://tecoble.techcourse.co.kr/post/2020-11-07-singleton/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kephilab.tistory.com/50&quot;&gt;https://kephilab.tistory.com/50&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bangu4.tistory.com/286&quot;&gt;https://bangu4.tistory.com/286&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL 데이터베이스 파티셔닝(Partitioning) 과 샤딩(Sharding)]]></title><description><![CDATA[현 데이터베이스 파티셔닝과 샤딩 기법은 MySQL 8.…]]></description><link>https://haon.site/haon/mysql/partitioning-sharding/</link><guid isPermaLink="false">https://haon.site/haon/mysql/partitioning-sharding/</guid><pubDate>Wed, 06 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;현 데이터베이스 파티셔닝과 샤딩 기법은 MySQL 8.0 을 기준으로 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;이전에 데이터베이스에 관해 최대한 열심히 깊게 공부해보고, &quot;파티셔닝&quot; 에 대해 공부해볼 키워드를 발견 했었지만 이에대해 학습한 경험이 없어서 제대로 이해하지 못한체 넘어가게 되었습니다. 따라서, 추후 데이터베이스 적재 방식을 원활히 고려할 수 있도록 파티셔닝에 대해 학습하고자 합니다.&lt;/p&gt;
&lt;p&gt;이번 포스팅에서는 파티셔닝에 대한 이론만 오로지 다루도록 하고, 구현방법은 다루지 않도록 합니다. 또한, 이전에 학습했던 샤딩(Sharding) 이 파시셔닝과 함께 묶이는 학습 키워드로 판단되므로, 샤딩을 복습 차원에서 아주 간단히만 언급하도록 합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;샤딩에 대한 자세한 설명은 &lt;a href=&quot;https://velog.io/@msung99/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81Clustering-%EA%B3%BC-%EC%83%A4%EB%94%A9Sharding-%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EA%B3%A0%EA%B0%80%EC%9A%A9%EC%84%B1%EA%B3%BC-%EC%8A%A4%EC%BC%80%EC%9D%BC%EC%95%84%EC%9B%83&quot;&gt;데이터베이스 클러스터링(Clustering) 과 샤딩(Sharding) 을 활용한 고가용성과 스케일아웃&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;파티셔닝partitioning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%ED%8B%B0%EC%85%94%EB%8B%9Dpartitioning&quot; aria-label=&quot;파티셔닝partitioning permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파티셔닝(Partitioning)&lt;/h2&gt;
&lt;h3 id=&quot;데이터베이스의-고가용성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EA%B3%A0%EA%B0%80%EC%9A%A9%EC%84%B1&quot; aria-label=&quot;데이터베이스의 고가용성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스의 고가용성&lt;/h3&gt;
&lt;p&gt;서비스의 크기가 점점 거대해지고 데이터베이스에 저장하는 데이터 수가 매우 커지면서, DB 시스템상 저장용량의 한계에 도달할 수 있게됩니다. 즉, 하나의 DB 에 너무 큰 테이블이 들어가면서 용량과 성능의 큰 저하가 발생할 수 있게됩니다. 이를 해결하도록 테이블을 &lt;strong&gt;&quot;파티션(Partition)&quot;&lt;/strong&gt; 이라는 작은 단위로 분할하여 관리하는 파티셔닝 기법이 등장하게 되었습니다. 즉, 데이터베이스를 분산 처리하여 성능이 저하되는 것을 방지하고 관리를 보다 수월하게 할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;파티셔닝이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%ED%8B%B0%EC%85%94%EB%8B%9D%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;파티셔닝이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파티셔닝이란?&lt;/h3&gt;
&lt;p&gt;파티셔닝이란 논리적인 데이터 원소들을 다수의 Entity 로 쪼개는 행위를 뜻삽니다. &lt;strong&gt;즉, 큰 테이블이나 인덱스(index) 를 관리하기 쉬운 파티션(Partition) 이라는 작은 단위로 물리적으로 분할하는 것입니다.&lt;/strong&gt; 물리적인 데이터 분할이 발생하더라도 데이터베이스에 접근하는 애플리케이션이 이를 인식하지 못합니다. &lt;strong&gt;즉, 사용자는 마치 하나의 테이블에 접근하는 것과 같이 사용할 수 있다는 것이 특징입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;파티셔닝의-종류&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%ED%8B%B0%EC%85%94%EB%8B%9D%EC%9D%98-%EC%A2%85%EB%A5%98&quot; aria-label=&quot;파티셔닝의 종류 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파티셔닝의 종류&lt;/h2&gt;
&lt;h3 id=&quot;수직horizontal-파티셔닝&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%98%EC%A7%81horizontal-%ED%8C%8C%ED%8B%B0%EC%85%94%EB%8B%9D&quot; aria-label=&quot;수직horizontal 파티셔닝 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;수직(horizontal) 파티셔닝&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c2c056e4-665d-42f0-aa58-0a025ae1fb0c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;하나의 테이블의 각 행을 다른 테이블에 분산시키는 방법입니다.&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;샤딩(Sharding)&lt;/code&gt; 과 동일한 개념으로, 스키마를 복제한 후 샤드키를 기준으로 데이터를 분할하는 방식입니다. 즉**, 스키마가 같은 두 데이터를 2개 이상의 테이블에 나누어 저장하는 것입니다.** 예를들어 User 테이블에서 userID 를 사드키로 활용하여 샤딩하기로 한다면, 0&lt;del&gt;1000 번 유저의 정보는 샤드1 에 저장하고, 1001&lt;/del&gt;2000 번 유저의 정보는 샤드2 에 저장합니다.&lt;/p&gt;
&lt;h4&gt;장점&lt;/h4&gt;
&lt;p&gt;한 테이블의 데이터들을 &quot;개수&quot; 를 기준으로 나누어 분할하므로 데이터의 개수가 작아지게 됩니다. 따라서 인덱스(index) 의 개수도 작아지게 되고, 자연스래 성능을 향상됩니다.&lt;/p&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;p&gt;데이터를 찾는 과정이 기본 방식보다 복잡하므로 &lt;code class=&quot;language-text&quot;&gt;레이턴시(latency)&lt;/code&gt; 가 증가하게 됨, 여러 데이터베이스 서버들중에 하나의 서버에 장애가 발생하게 되면 데이터 무결성이 깨질 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;수직vertical-파시셔닝&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%98%EC%A7%81vertical-%ED%8C%8C%EC%8B%9C%EC%85%94%EB%8B%9D&quot; aria-label=&quot;수직vertical 파시셔닝 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;수직(vertical) 파시셔닝&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a92ace73-830d-498d-a630-ce916e08d2ab/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;테이블 일부 컬럼(Column) 을 빼내는 형태로 분할합니다. 모든 컬럼들 중 특정 컬럼들을 쪼개서 따로 저장하는 형태로, 하나의 엔티티(스키마) 를 2개 이상으로 분할하는 작업입니다.&lt;/p&gt;
&lt;h4&gt;장점&lt;/h4&gt;
&lt;p&gt;자주 사용하는 컬럼들을 분리시켜서 성능을 향상 시킬 수 있으며, 한 테이블을 select 하면 모든 컬럼들을 메모리에 올리게 되므로, 필요없는 컬럼까지 올라가서 한번에 읽을 수 있는 ROW 수가 증가하게 됩니다. 이는 I/O 측면에서 봤을때 필요한 컬럼만 올리면 훨씬 많은 수의 ROW 를 메모리에 올릴 수 있으니 성능상의 장점이 됩니다. 또한 같은 타입의 데이터가 저장되기 때문에 그룹화가 되어서, 데이터의 응집성을 높일 수 있게됩니다.&lt;/p&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;p&gt;마찬가지로 데이터를 찾는 과정이 복잡해지므로 &lt;code class=&quot;language-text&quot;&gt;레이턴시(Latency)&lt;/code&gt; 가 증가합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;파티셔닝의-분할기준범위&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%ED%8B%B0%EC%85%94%EB%8B%9D%EC%9D%98-%EB%B6%84%ED%95%A0%EA%B8%B0%EC%A4%80%EB%B2%94%EC%9C%84&quot; aria-label=&quot;파티셔닝의 분할기준범위 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파티셔닝의 분할기준(범위)&lt;/h2&gt;
&lt;p&gt;DBMS 는 파티셔닝에 대해 각종 기준(분할 기법) 을 제공하고 있습니다. 분할은 &lt;strong&gt;&quot;분할 키(partitioning key)&quot;&lt;/strong&gt; 를 활용합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/98304c32-6aed-48c2-b3e3-d398cc46f91c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;범위-분할range-partitioning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B2%94%EC%9C%84-%EB%B6%84%ED%95%A0range-partitioning&quot; aria-label=&quot;범위 분할range partitioning permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;범위 분할(Range Partitioning)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;데이터를 특정 범위를 기준으로 분할할 때 사용하는 방식&lt;/strong&gt;으로, 연속적인 숫자나 날짜를 기준으로 파시셔닝 하는 방식입니다. 위처럼 1&lt;del&gt;2월, 3&lt;/del&gt;4월, 5~6월, ... 으로 데이터를 분리할 때 사용할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;목록-분할list-partitioning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A9%EB%A1%9D-%EB%B6%84%ED%95%A0list-partitioning&quot; aria-label=&quot;목록 분할list partitioning permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;목록 분할(List Partitioning)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;데이터 값이 특정 목록에 포함된 경우 데이터를 분리&lt;/strong&gt;합니다. 분포도가 비슷하며, 많은 SQL 에서 해당 컬럼에 조건이 많이 들어오는 경우에 유용합니다. 예를들어 위처럼 특정 지역별로 데이터를 분할할 때 사용할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;해시-분할hash-partitioning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B4%EC%8B%9C-%EB%B6%84%ED%95%A0hash-partitioning&quot; aria-label=&quot;해시 분할hash partitioning permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;해시 분할(Hash Partitioning)&lt;/h3&gt;
&lt;p&gt;해시 함수를 사용하여 데이터를 분할하는 방식으로, &lt;strong&gt;특정 컬럼의 값을 해싱하여 저장할 파티션을 선택합니다.&lt;/strong&gt; 데이터의 균등 분할을 통해 성능을 향상하고자 하는 경우에 효율적입니다.&lt;/p&gt;
&lt;p&gt;공식문서에 따르면 여러 컬럼으로 해싱하는것은 크게 권장하지 않는 방법이라고 합니다. 만약 활용한다면, 유저 ID 처럼 변별력이 좋고 데이터 분포가 고른 컬럼을 파티션 키 컬럼으로 선정해야 효과적입니다.&lt;/p&gt;
&lt;h3 id=&quot;합성-분할composite-partitioning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%A9%EC%84%B1-%EB%B6%84%ED%95%A0composite-partitioning&quot; aria-label=&quot;합성 분할composite partitioning permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;합성 분할(Composite Partitioning)&lt;/h3&gt;
&lt;p&gt;위 파티셔닝 종류들 중에 2개 이상의 방식을 혼합하여 사용하는 방식입니다. 단, 파티션을 나누는 기준이 너무 많아지면 파티션 개수가 너무 많아지게 될것이고, 인덱스의 경합이 너무 심해져서 잘 사용하지 않는 방법이라고 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;샤딩sharding&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%A4%EB%94%A9sharding&quot; aria-label=&quot;샤딩sharding permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;샤딩(Sharding)&lt;/h2&gt;
&lt;p&gt;이전에 언급했듯이, 샤딩은 이전에 다룬 내용이므로 아주 간단히만 복습 차원에서 키워드를 언급하도록 하겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;hash-sharding&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hash-sharding&quot; aria-label=&quot;hash sharding permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Hash Sharding&lt;/h3&gt;
&lt;p&gt;해시 샤딩은 해시함수를 활용하여 PK 값을 나머지 연산한 결과값으로 어떤 샤드에 들어갈지 결정하는 방식입니다. 샤드의 수만큼 해싱을 하면 되기 떄문에 간단한 방식입니다.&lt;/p&gt;
&lt;p&gt;대신 데이터베이스 개수가 줄어들거나 늘어나면 해시 함수도 변경해야하므로, 데이터의 정합성이 깨질 수 있다는 위험때문에 데이터의 재정렬 작업이 필요해집니다.&lt;/p&gt;
&lt;h3 id=&quot;range-sharding&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#range-sharding&quot; aria-label=&quot;range sharding permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Range Sharding&lt;/h3&gt;
&lt;p&gt;PK 값을 범위로 지정하여 샤드를 결정하는 방식입니다. 예를들어 PK 가 1&lt;del&gt;1000번인 데이터는 1번 샤드에, 1001&lt;/del&gt;2000번 데이터는 2번 샤드에 저장합니다.&lt;/p&gt;
&lt;p&gt;해시 샤딩에 비해 데이터베이스 증설 작업에 용이하므로, 떄문에 급증할 수 있는 데이터는 Range Sharding 을 사용하는것이 좋을겁니다. 다만 기껏 분산을 시켰는데 특정 데이터베이스에만 부하가 몰릴 수 있다는점을 유의해야합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/partitioning.html&quot;&gt;https://dev.mysql.com/doc/refman/8.0/en/partitioning.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/vldbg/partition-concepts.html#GUID-5AE7BBD6-02C1-4DB4-BB5B-B4E5B4C96FAD&quot;&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/vldbg/partition-concepts.html#GUID-5AE7BBD6-02C1-4DB4-BB5B-B4E5B4C96FAD&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gmlwjd9405.github.io/2018/09/24/db-partitioning.html&quot;&gt;https://gmlwjd9405.github.io/2018/09/24/db-partitioning.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://code-lab1.tistory.com/202&quot;&gt;https://code-lab1.tistory.com/202&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://coding-factory.tistory.com/840&quot;&gt;https://coding-factory.tistory.com/840&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/db-partitioning-and-sharding/&quot;&gt;https://hudi.blog/db-partitioning-and-sharding/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[ControllerAdvice 를 통한 통합 예외처리를 해보자!]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/spring/controller-adivce/</link><guid isPermaLink="false">https://haon.site/haon/spring/controller-adivce/</guid><pubDate>Sun, 13 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;도입배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%9E%85%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;도입배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도입배경&lt;/h2&gt;
&lt;p&gt;과거 무심코 예외처리 구현했던 방식과 비교해보면서, 왜 사용하는것인지를 정리해보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;다양한-애플리케이션-예외처리-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%96%91%ED%95%9C-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;다양한 애플리케이션 예외처리 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다양한 애플리케이션 예외처리 방식&lt;/h2&gt;
&lt;p&gt;자바 애플리케이션을 개발하다보면 예외는 어디서나 터질수가있고, 예상치도 못한 범위를 벗어나서도 예외는 얼마든지 발생할 수 있습니다. 때문에 상세하고 다양한 예외들을 얼마든지 처리해줄 수 있는 방법이 있다면 정말 편할겁니다.&lt;/p&gt;
&lt;h3 id=&quot;기본적인-예외처리-방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9D%B8-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC-%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;기본적인 예외처리 방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기본적인 예외처리 방법&lt;/h3&gt;
&lt;p&gt;스프링부트 애플리케이션을 개발하다보면, 저희는 일반적으로 메소드 내에서 &lt;code class=&quot;language-text&quot;&gt;try-catch&lt;/code&gt; 문으로 가장 기초적인 예외처리를 진행하는 예외처리 방식을 고려해볼 수 있을겁니다. 예를들어 아래와 같은 방식으로 처리를 하겠죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Request&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에서는 비즈니스 규칙에 따라 입력 유효성을 검증하는 로직이 포함될 수 있으며, (몰론 아키텍처에 따라 각기 다르다) 비즈니스 로직 수행중 유효하지 않은 예외상황이 발생한다면 예외를 던질 수 있을겁니다. &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에서 던져진 예외는 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 에서 처리될겁니다.&lt;/p&gt;
&lt;p&gt;위 코드를 더 구체화해보자면, 아래와 같습니다. 하지만 이 처리방식의 문제점은 뭘까요?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Request&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  	   &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
       	   &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RunTimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;잔액이 부족합니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

       &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
           &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RunTimeExcpetion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;이미 존재하는 회원입니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
       &lt;span class=&quot;token comment&quot;&gt;// ... (비즈니스 로직)&lt;/span&gt;

   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;token comment&quot;&gt;// ... (예외처리)&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;예외처리의-한계-전역--통합-예외관리의-필요성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC%EC%9D%98-%ED%95%9C%EA%B3%84-%EC%A0%84%EC%97%AD--%ED%86%B5%ED%95%A9-%EC%98%88%EC%99%B8%EA%B4%80%EB%A6%AC%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1&quot; aria-label=&quot;예외처리의 한계 전역  통합 예외관리의 필요성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;예외처리의 한계, 전역 &amp;#x26; 통합 예외관리의 필요성&lt;/h3&gt;
&lt;p&gt;이 방식은 세부적인 예외상황을 핸들링하는데 한계가 있습니다. 위 코드처럼 비즈니스 규칙을 검증하는데 있어서 if 문을 활용할 것이며, 이렇게 if 문을 남용하다보면 분명 가독성이 많이 떨어지게 됩니다.&lt;/p&gt;
&lt;p&gt;또한 모든 예외상황에 대한 처리가 상위 클래스인 &lt;code class=&quot;language-text&quot;&gt;Exception&lt;/code&gt; 으로 모아지는데, 각 예외상황에 대해 모두 동일한 예외처리가 수행되므로, 해당 메소드에서 예외가 터진다면 정확히 어떤 예외 상황이 터진것인지 식별하는것이 꽤나 힘듭니다.&lt;/p&gt;
&lt;p&gt;또한 try-catch 문이 각 메소드마다 처리된다면, 불필요하게 중복되는 코드가 발생하는 꼴입됩니다. 가능하다면 바로 이어서 설명할 &lt;code class=&quot;language-text&quot;&gt;@ControllerAdvice&lt;/code&gt; 를 활용하여 전역 예외처리를 구현하고, 애플리케이션 전역에서 발생하는 모든 예외 상황들에 대해 한곳에서 통합 관리하는 방식이 더 좋을겁니다.&lt;/p&gt;
&lt;p&gt;따라서 유지보수 및 비즈니스 로직에 집중하기 위해서, 또 비즈니스 로직과 관련한 예외 상세 케이스들을 통제하기 위해서 &lt;code class=&quot;language-text&quot;&gt;@ExceptionHandler&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;@ControllerAdvice&lt;/code&gt; 를 사용하지 않을 이유가 없습니다. 필요성을 깨달았으니, 지금부터 해당 방식을 알아봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;exceptionhandler&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#exceptionhandler&quot; aria-label=&quot;exceptionhandler permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@ExceptionHandler&lt;/h2&gt;
&lt;p&gt;먼저 &lt;code class=&quot;language-text&quot;&gt;@ExceptionHandler&lt;/code&gt; 는 스프링에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;@Controller&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;@RestController&lt;/code&gt; 가 적용된 Bean 에서 try-catch 를 이용한 예외처리를 어노테이션을 통해 간편한 처리를 도와주는 어노테이션입니다. 예외 발생시의 리턴 타입은 자유롭게 진행해도됩니다. &lt;strong&gt;즉, 프로젝트의 상황에 따라 예외 발생시에 반환할 커스텀 클래스를 하나 정의해두고, 그 형식에 알맞게 Response 를 보내줘도 됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;사용시 주의할점은, Controller 와 RestCOntroller 에만 적용가능하기 때문에 &lt;code class=&quot;language-text&quot;&gt;@Service&lt;/code&gt; 등에는 적용 불가능합니다.&lt;/p&gt;
&lt;h3 id=&quot;controller-에-적용하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#controller-%EC%97%90-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; aria-label=&quot;controller 에 적용하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Controller 에 적용하기&lt;/h3&gt;
&lt;p&gt;아마 가장 궁금해하셨을 적용 코드 예시입니다. &lt;code class=&quot;language-text&quot;&gt;(1)&lt;/code&gt; 에는 우리가 흔히 살펴보던 API 통신을 위한 메소드의 정의부입니다. 반면 &lt;code class=&quot;language-text&quot;&gt;(2)&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;(3)&lt;/code&gt; 에는 &lt;code class=&quot;language-text&quot;&gt;@ExceptionHandler&lt;/code&gt; 가 설정된 것을 볼 수 있는데, 현재 OrderController 에서 발생하는 예외중 RunTimeException, NullPointerExcpetion 가 발생했을때 어떻게 처리해줄지에 대한 로직이 정의되어 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderService&lt;/span&gt; orderService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@PostMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/order&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;OrderResponseDto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;token comment&quot;&gt;// (1)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ExceptionHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RunTimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;OrderIOExcpetionResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;orderIOHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; ex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;token comment&quot;&gt;// (2)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token annotation punctuation&quot;&gt;@ExcpetionHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NullPointerException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;OrderNullExcpetionResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;orderNullHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// (3)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;즉, &lt;code class=&quot;language-text&quot;&gt;value&lt;/code&gt; 에는 아떤 예외를 잡아서 핸들링할지를 명시해줘야 합니다. 이때 value 를 별도 지정하지 않으면 모든 예외를 잡기 떄문에, 필요한 세부 예외를 지정해주는게 좋습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;restcontrolleradvice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#restcontrolleradvice&quot; aria-label=&quot;restcontrolleradvice permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@RestControllerAdvice&lt;/h2&gt;
&lt;h3 id=&quot;순수-exceptionhandler-활용의-번거로움&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%9C%EC%88%98-exceptionhandler-%ED%99%9C%EC%9A%A9%EC%9D%98-%EB%B2%88%EA%B1%B0%EB%A1%9C%EC%9B%80&quot; aria-label=&quot;순수 exceptionhandler 활용의 번거로움 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;순수 @ExceptionHandler 활용의 번거로움&lt;/h3&gt;
&lt;p&gt;앞선 코드를 이해했다면, 뭔가 아쉬움을 느꼈을수도 있습니다. &lt;code class=&quot;language-text&quot;&gt;@ExceptionHandler&lt;/code&gt; 는 등록된 해당 컨트롤러에서만 적용이 되죠. 하지만 순수히 &lt;code class=&quot;language-text&quot;&gt;@ExceptionHandler&lt;/code&gt; 만 사용해서는 특정 하나의 컨트롤러 외에, 모든 여러 컨트롤러에서 발생하는 예외를 잡을 수 없습니다.&lt;/p&gt;
&lt;p&gt;이러할때 &lt;code class=&quot;language-text&quot;&gt;@RestControllerAdvice&lt;/code&gt; 를 도입하면 위 문제가 해결됩니다. &lt;strong&gt;즉, 같은 예외가 발생했다면 같은 처리를 해주고 싶을때 활용하면 됩니다.&lt;/strong&gt; 똑같은 기능을 똑같이 각 컨트롤러마다 반복해서&lt;code class=&quot;language-text&quot;&gt;@ExcpetionHandler&lt;/code&gt; 를 정의해서 중복 코드를 반복하는 일을 없앨 수 있게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;통합-예외처리기-도입&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%86%B5%ED%95%A9-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC%EA%B8%B0-%EB%8F%84%EC%9E%85&quot; aria-label=&quot;통합 예외처리기 도입 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;통합 예외처리기 도입&lt;/h3&gt;
&lt;p&gt;전역 예외처리는 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;@RestControllerAdvice&lt;/code&gt; 를 활용한 클래스를 하나 정의했습니다. 이로써 모든 컨트롤러에서 발생하는 예외상황은 공통 형식으로 처리될겁니다.&lt;/p&gt;
&lt;p&gt;이때 예외가 발생했을때 공통 형식으로 응답하기 위한 &quot;공통 응답객쳬&quot; 로 &lt;code class=&quot;language-text&quot;&gt;BaseExceptionResponse&lt;/code&gt; 를 정의했으며, 비즈니스 로직 수행중 조건에 어긋나는 상황에 대한 비즈니스 예외를 핸들링하기 위해 &lt;code class=&quot;language-text&quot;&gt;BusinessExceptionResponse&lt;/code&gt; 라는 커스터마이징 클래스를 정의한 것을 볼 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestControllerAdvice&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GlobalExceptionHandler&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ExceptionHandler&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 비즈니스 로직 예외처리&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BaseExceptionResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;serviceExcpetion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BusinessExceptionResponse&lt;/span&gt; serviceExceptionResponse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;BaseExceptionResponse&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseExceptionResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;makeErrorResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ErrorBaseResponseCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BUSINESS_EXCEPTION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BAD_REQUEST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ExceptionHandler&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Method 예외처리&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BaseExceptionResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;methodException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpRequestMethodNotSupportedException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;BaseExceptionResponse&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseExceptionResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;makeErrorResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ErrorBaseResponseCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;METHOD_NOT_ALLOWED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;METHOD_NOT_ALLOWED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;restcontrolleradvice-vs-controlleradivce&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#restcontrolleradvice-vs-controlleradivce&quot; aria-label=&quot;restcontrolleradvice vs controlleradivce permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@RestControllerAdvice VS @ControllerAdivce&lt;/h3&gt;
&lt;p&gt;현 포스팅과 관련해 더 키워드를 찾아보면 둘의 차이점이 등장할겁니다. 요약하면 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;@RestControllerAdvice = @ControllerAdvice + @ResponseBody&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;@RestControllerAdvice&lt;/strong&gt; 는 &lt;strong&gt;@ControllerAdvice&lt;/strong&gt; 와 동일한 역할을 수행하지만, 단지 객체를 반환할 수 있다는 차이점만이 있습니다. 즉, &lt;strong&gt;@ControllerAdvice&lt;/strong&gt; 와 달리 &lt;strong&gt;@RestControllerAdvice&lt;/strong&gt; 는 응답의 body 에 객체를 넣어 반환이 가능합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;공통 응답객체 정의 (super 상속)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-05-10-controller_advice_exception_handler/&quot;&gt;https://tecoble.techcourse.co.kr/post/2021-05-10-controller_advice_exception_handler/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zayson.tistory.com/entry/RestControllerAdvice-ExceptionHandler%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%A0%84%EC%97%AD-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC&quot;&gt;https://zayson.tistory.com/entry/RestControllerAdvice-ExceptionHandler를-이용한-전역-예외-처리&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jeong-pro.tistory.com/195&quot;&gt;https://jeong-pro.tistory.com/195&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[JPA OSIV 란 무엇이고, 왜 데이터베이스 커넥션 생명주기와 관련있을까?]]></title><description><![CDATA[OSIV(Open Session In View)  스프링부트 애플리케이션을 실행해보면, 누구나 WARN 경고문을 확인한 적이 있을겁니다. 이것은 OSIV…]]></description><link>https://haon.site/haon/jpa/osiv/</link><guid isPermaLink="false">https://haon.site/haon/jpa/osiv/</guid><pubDate>Sat, 05 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;osivopen-session-in-view&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#osivopen-session-in-view&quot; aria-label=&quot;osivopen session in view permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OSIV(Open Session In View)&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/64841f30-b43e-44c9-82a8-d37e82496322/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;스프링부트 애플리케이션을 실행해보면, 누구나 WARN 경고문을 확인한 적이 있을겁니다. 이것은 OSIV 와 관련하여 이슈가 충분히 발생할 수 있기 떄문에, 경고문이 기본으로 표시되는 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;과거-osiv&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%BC%EA%B1%B0-osiv&quot; aria-label=&quot;과거 osiv permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;과거 OSIV&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c591f3c1-c405-4f17-9c59-ff04d98adf4a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;과거 OSIV 을 먼저 이해할 필요가 있습니다. 클라이언트의 요청이 들어오면 서블릿 필터, 스프링 인터셉터에서 &lt;strong&gt;영속성 컨텍스트와 트랜잭션 및 커넥션을 생성하고, 요청을 최초로 받고나서 완전히 응답하기 전까지 끊지않고 계속해서 유지하는 방식&lt;/strong&gt;이였습니다. 이로써 한 요청에 대해 엔티티가 끝까지 영속성 상태로 살아있고, DB 와의 커넥션도 끊어지지 않습니다. 때문에 지연로딩(Lazy Loading) 도 사용이 가능해졌습니다.&lt;/p&gt;
&lt;p&gt;다시 해석해보면, 이는 곧 컨트롤러(Controller), 뷰(View) 에서도 엔티티의 영속상태가 유지된다는 것을 의미하게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;전-계층-레이어에서-활성화시-문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84-%EA%B3%84%EC%B8%B5-%EB%A0%88%EC%9D%B4%EC%96%B4%EC%97%90%EC%84%9C-%ED%99%9C%EC%84%B1%ED%99%94%EC%8B%9C-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;전 계층 레이어에서 활성화시 문제점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전 계층 레이어에서 활성화시 문제점&lt;/h3&gt;
&lt;p&gt;하지만 이렇게 모든 레이어 계층에서 활성화되는 것은 문제점이 존재했습니다. 바로 &lt;code class=&quot;language-text&quot;&gt;프레젠테이션(presentation) 계층&lt;/code&gt; 을 포함한 전 범위의 레이어 계층에서 영속성 컨텍스트가 살아있고 유지된다는 것입니다. 이는 곧 프레젠테이션 계층에서도 엔티티를 변경 및 다양한 조작이 가능하다는 말이 됩니다.&lt;/p&gt;
&lt;p&gt;과거 OSIV 는 여기서 문제가 발생합니다. &lt;strong&gt;뷰가 랜더링되면 영속성 컨텍스트가 플러시(flush) 되고 DB 에 트랜잭션이 커밋이 날라간다는 특성이 있습니다.&lt;/strong&gt; 따라서 JPA 의 &lt;code class=&quot;language-text&quot;&gt;더티체킹(Dirty Checking)&lt;/code&gt; 과 같은 속성으로 인해 setter 를 호출하면서 뷰가 랜더링되면, 무조건 DB 에 커밋이 날라가게 되는 문제가 발생하게 됩니다. &lt;strong&gt;즉, 한 요청당 하나의 트랜잭션 단위로 묶여서 처리된다는 특성으로 인해 문제가 발생합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;최신-osiv-동작방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B5%9C%EC%8B%A0-osiv-%EB%8F%99%EC%9E%91%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;최신 osiv 동작방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;최신 OSIV 동작방식&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0ce6a841-1cd5-4e17-8699-23299c10b4af/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위와 같은 문제점을 보완하도록, 최신 OSIV 는 프레젠테이션 계층에서 엔티티를 수정 못하도록 개선되었습니다. 즉, &lt;strong&gt;프레젠테이션 계층을 제외한 비즈니스 계층에서만 트랜잭션을 사용 가능하도록 개선된 것입니다.&lt;/strong&gt; 이러면 뷰가 렌더링 될때 잘못된 엔티티 변경사항에 대한 트랜잭션 커밋이 실제 DB 에 날라가는 상황을 방지할 수 있게됩니다.&lt;/p&gt;
&lt;p&gt;스프링부트에선 기본값으로 OSIV 가 활성화되어 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;spring&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jpa&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;in&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;view&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;// 디폴트 옵션:true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@Transactinoal&lt;/code&gt; 등으로 서비스단에 존재하는 메소드에 트랜잭션을 생성하면 그 즉시 데이터베이스와 커넥션을 생성하고, 해당 메소드를 벗어나도 커넥션을 계속 유지하게 됩니다. DB 트랜잭션을 시작될때, 곧바로 영속성 컨텍스트가 DB 커넥션을 가져오는 방식인것입니다.&lt;/p&gt;
&lt;h3 id=&quot;osiv-의-주요-특징&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#osiv-%EC%9D%98-%EC%A3%BC%EC%9A%94-%ED%8A%B9%EC%A7%95&quot; aria-label=&quot;osiv 의 주요 특징 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OSIV 의 주요 특징&lt;/h3&gt;
&lt;p&gt;스프링부트에서 제공하는 OSIV 의 특징을 정리해보면 다음과 같습니다. 우선 &lt;strong&gt;API가 호출되고 응답을 주면서 화면이 렌더링 되는 전체 과정에서 영속성 컨텍스트는 살아있습니다.&lt;/strong&gt; 이 때문에 비즈니스 계층외에 프레젠테이션 계층에서도 지연 로딩으로 데이터를 가져오는 것이 가능한 것입니다.&lt;/p&gt;
&lt;p&gt;또한 &lt;strong&gt;@Transactional 어노테이션 등으로 비즈니스 계층에서 트랜잭션을 시작하는 즉시 DB 커넥션을 생성&lt;/strong&gt;하고, 해당 트랜잭션이 끝나더라도 DB 커넥션은 끊어지지않고 클라이언트에게 완전히 Response 하기 전까지 하나로 계속 유지됩니다.&lt;/p&gt;
&lt;p&gt;이 외애도 다음과 같은 특성이 존재합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;osiv-를-비활성화-한다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#osiv-%EB%A5%BC-%EB%B9%84%ED%99%9C%EC%84%B1%ED%99%94-%ED%95%9C%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;osiv 를 비활성화 한다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OSIV 를 비활성화 한다면?&lt;/h2&gt;
&lt;p&gt;반대로 OSIV 를 비활성화 해야 하는 상황도 자주 발생합니다. 어떤 상황에서 비활성화 해야 하는지를 이해하기위해, 그 전에 먼저 OSIV 를 비활성화 했을때의 특징을 정리해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/06661b4b-ad2a-4831-adec-2e798aaed080/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;spring&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jpa&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;in&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;view&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;가장 큰 특징은, &lt;strong&gt;트랜잭션을 종료할 때 영속성 컨텍스트를 닫고 DB 커넥션을 반환한다는 것입니다.&lt;/strong&gt; 즉, 영속성 컨텍스트와 DB 커넥션의 생존범위는 비즈니스 계층에만 존재하게 됩니다. 떄문에 커넥션 리소스를 낭비할 일이 없으며, 동시간대에 여러 API 를 호출할때 마다 ( = 프레젠테이션을 접근할 때 마다) 매번 다른 커넥션을 획득하게 되는 것이죠.&lt;/p&gt;
&lt;p&gt;따라서 이 방식을 활용하는 상황에선, &lt;strong&gt;모든 지연로딩에 대한 처리를 하나의 트랜잭션 안에서 해결해야 합니다.&lt;/strong&gt; 영속성 컨텍스트의 생명주기는 비즈니스 계층의 하나의 트랜잭션에서만 살아있기 때문에, 컨트롤러가 여러번 호출되는 경우 각기 다른 영속성 컨텍스트와 엔티티를 보유한 상태이기 떄문입니다. 따라서 트랜잭션이 끝나기 전에 지연 로딩을 강제로 호출해두거나 fetch join을 사용해야 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;자바 ORM 표준 JPA 프로그래밍, 김영한&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dodeon.gitbook.io/study/kimyounghan-spring-boot-and-jpa-optimization/04-osiv&quot;&gt;https://dodeon.gitbook.io/study/kimyounghan-spring-boot-and-jpa-optimization/04-osiv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ykh6242.tistory.com/entry/JPA-OSIVOpen-Session-In-View%EC%99%80-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94&quot;&gt;https://ykh6242.tistory.com/entry/JPA-OSIVOpen-Session-In-View와-성능-최적화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@sangmin7648/OSIV%EB%9E%80&quot;&gt;https://velog.io/@sangmin7648/OSIV%EB%9E%80&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Bulk insert(벌크 연산)를 활용하여 쿼리 성능 개선해보기]]></title><description><![CDATA[문제 발생배경 이번에는 제가 맡은 도메인 서비스를 개발하다가 만난 "벌크 연산(Bulk Insert)" 관련 이슈에 대해 다루어보고자 합니다. JPA saveAll() 의 한계  save() 를 300번 수행한다. 기존에는 JPA 의 save…]]></description><link>https://haon.site/haon/jpa/bulk-insert/</link><guid isPermaLink="false">https://haon.site/haon/jpa/bulk-insert/</guid><pubDate>Tue, 01 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;문제-발생배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%A0%9C-%EB%B0%9C%EC%83%9D%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;문제 발생배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문제 발생배경&lt;/h2&gt;
&lt;p&gt;이번에는 제가 맡은 도메인 서비스를 개발하다가 만난 &quot;벌크 연산(Bulk Insert)&quot; 관련 이슈에 대해 다루어보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;jpa-saveall-의-한계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jpa-saveall-%EC%9D%98-%ED%95%9C%EA%B3%84&quot; aria-label=&quot;jpa saveall 의 한계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JPA saveAll() 의 한계&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6cf39e77-59b4-48d4-afe5-d3e54da4910e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;save-를-300번-수행한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#save-%EB%A5%BC-300%EB%B2%88-%EC%88%98%ED%96%89%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;save 를 300번 수행한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;save() 를 300번 수행한다.&lt;/h3&gt;
&lt;p&gt;기존에는 JPA 의 save() 를 활용하여, 데이터 리스트를 for-each 로 순환하며 저장하는 간단한 로직을 구현했습니다. 그러나, 약 300개의 데이터를 insert 했을때, 한번의 쿼리가 아니라 300번의 삽입 쿼리가 나가며 로직이 종료되지 않고 오래걸리는 것을 확인하게 되었습니다. 또한 당연히 응답속도가 약 &lt;code class=&quot;language-text&quot;&gt;4000ms&lt;/code&gt; 로 매우 느린 모습을 확인하게 되었습니다.&lt;/p&gt;
&lt;h3 id=&quot;saveall-로-개선하면-성능이-좋아질까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#saveall-%EB%A1%9C-%EA%B0%9C%EC%84%A0%ED%95%98%EB%A9%B4-%EC%84%B1%EB%8A%A5%EC%9D%B4-%EC%A2%8B%EC%95%84%EC%A7%88%EA%B9%8C&quot; aria-label=&quot;saveall 로 개선하면 성능이 좋아질까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;saveAll() 로 개선하면 성능이 좋아질까?&lt;/h3&gt;
&lt;p&gt;그렇다면 saveAll() 을 활용했을떄, 쿼리가 단 한방에 나갈것을 기대하며 for-each 순환마다 save() 를 호출하는 로직을 제거하고, 엔티티 리스트를 만들어서 saveAll() 의 인자로 넘겨줬습니다. 그러나, 이 방식도 쿼리가 300번이 나가는것은 여전했으며, 성능 개선에 실패했었습니다.&lt;/p&gt;
&lt;h3 id=&quot;쿼리-성능이-조금은-좋아졌네-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BF%BC%EB%A6%AC-%EC%84%B1%EB%8A%A5%EC%9D%B4-%EC%A1%B0%EA%B8%88%EC%9D%80-%EC%A2%8B%EC%95%84%EC%A1%8C%EB%84%A4-&quot; aria-label=&quot;쿼리 성능이 조금은 좋아졌네  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쿼리 성능이 조금은 좋아졌네? 🤔&lt;/h3&gt;
&lt;p&gt;saveAll() 은 그래도 save() 를 여러번 호출하는 것에 비해서는 성능 개선이 됩니다. saveAll() 은 save() 를 여러번 호출하기 때문에 얼핏보면 똑같은 생각이 들 수 있으나, 자세히 들여다보면 두 메소드에는 &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 기반의 트랜잭션이 발동됩니다. 즉, 300번 save() 를 호출했을때는 트랜잭션이 300번이나 실행되는 것이지만, 반면 saveAll() 은 단 한번의 트랜잭션으로 저장이 수행되는 방식입니다. 이로인해 성능 차이가 발생하는 것이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;jpa-hibernate-batch-insert&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jpa-hibernate-batch-insert&quot; aria-label=&quot;jpa hibernate batch insert permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JPA Hibernate Batch Insert&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Hibernate disables insert batching at the JDBC level transparently if you use an identity identifier generator.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;사실 JPA 환경에서 이 외에도 널리 알려진 방법은 하이버네이트 차원에서 제공하는 배치 삽입연산을 진행하는 것입니다. 그러나, &lt;code class=&quot;language-text&quot;&gt;IDENTITY&lt;/code&gt; 전략에서는 하이버네이트가 Batch Insert 를 비활성화 때문에 사용이 불가능합니다. 이는 &lt;code class=&quot;language-text&quot;&gt;영속성 컨텍스트&lt;/code&gt; 내부에서 엔티티를 식별할때는 엔티티 타입과 엔티티의 pk 값으로 엔티티를 식별하지만, IDENTITY 의 경우 DB 에 Insert 문을 실행해야만 pk 값을 확인가능하므로 비활성화하는 것입니다.&lt;/p&gt;
&lt;p&gt;결론적으로, 저희 서비스는 &lt;code class=&quot;language-text&quot;&gt;IDENTITY&lt;/code&gt; 전략을 활용하므로 이는 활용하지 않았지만, &lt;code class=&quot;language-text&quot;&gt;Table&lt;/code&gt; 전략등의 서비스를 활용한다면 아래와 같이 배치 기능을 활성화해도 좋습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;spring&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  jpa&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  	properties&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      hibernate&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
        jdbc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
          batch_size&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
          order_inserts&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
          order_upates&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;jdbctemplate-batchupdate&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jdbctemplate-batchupdate&quot; aria-label=&quot;jdbctemplate batchupdate permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JdbcTemplate BatchUpdate&lt;/h2&gt;
&lt;p&gt;결국 saveAll() 도 300번의 쿼리가 나가게 되므로, 성능 개선이 되지 못하는것은 사실입니다. 또한 앞서 설명한 JPA 의 Batch Insert 기능을 활용하자니, 저희 서비스의 &lt;code class=&quot;language-text&quot;&gt;IDENTITY&lt;/code&gt; 전략을 다른 전략으로 전환하는것도 꽤 부담스러운 상황이였습니다.&lt;/p&gt;
&lt;p&gt;이를위해, JDBC 의 &lt;code class=&quot;language-text&quot;&gt;batchUpdate()&lt;/code&gt; 를 활용하여 벌크연산을 단 한방의 쿼리로 처리하도록 했습니다. 이를 위해선 SQL Mapper 인 &lt;code class=&quot;language-text&quot;&gt;JdbcTemplate&lt;/code&gt; 를 활용하는것이 통상적으로 알려진 방법이며, 저 또한 이를 활용했습니다.&lt;/p&gt;
&lt;h3 id=&quot;applicationyml&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#applicationyml&quot; aria-label=&quot;applicationyml permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;application.yml&lt;/h3&gt;
&lt;p&gt;본격적인 배치 코드 작성 이전에, 저희처럼 MySQL 를 활용하는 서비스라면 application.yml 에서 &lt;code class=&quot;language-text&quot;&gt;rewriteBatchedStatement=true&lt;/code&gt; 를 활성화해줍시다. 이를 활성화해야 MySQL 데이터베이스에 쿼리가 전송시 배치 단위로 전송됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;spring&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  datasource&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; jdbc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;mysql&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;localhost&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3306&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;test&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;rewriteBatchedStatements&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    username&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; root
    password&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    driver&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mysql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jdbc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;Driver&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;배치삽입-코드-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%EC%B9%98%EC%82%BD%EC%9E%85-%EC%BD%94%EB%93%9C-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;배치삽입 코드 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배치삽입 코드 구현&lt;/h3&gt;
&lt;p&gt;배치 연산을 활용한 API 및 도메인 서비스는 정말 많았지만, 그 중 코드 단위 하나를 가져와봤습니다. 아래처럼 batchUpdate 의 첫번쨰 인자로는 쿼리를, 두번째 인자로는 &lt;code class=&quot;language-text&quot;&gt;BatchPreparedStatementSetter&lt;/code&gt; 를 선언해주면 됩니다.&lt;/p&gt;
&lt;p&gt;그리고 오버라이드를 수행한 &lt;code class=&quot;language-text&quot;&gt;setValues&lt;/code&gt; 에는 SQL 의 각 물음표 파라미터에 들어갈 데이터와 타입을 명시하면 되며, &lt;code class=&quot;language-text&quot;&gt;getBatchSize&lt;/code&gt; 에는 한번에 얼만큼의 배치 삽입연산을 수행할 것인지에 대한 것을 명시해주면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;saveAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Pair&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; taskHistorys&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; firstTaskId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Daily&lt;/span&gt; daily&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; taskHistoryInsertQuery &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;INSERT INTO task_history (start_date, end_date, daily_id, task_id) VALUES (?, ?, ?, ?)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; currentTaskId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;firstTaskId&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 래퍼 클래스를 사용하여 final 변수 선언&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Entry&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Pair&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; taskHistoryEntry &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; taskHistorys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entrySet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Pair&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; historyPairs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; taskHistoryEntry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        jdbcTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;batchUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;taskHistoryInsertQuery&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BatchPreparedStatementSetter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PreparedStatement&lt;/span&gt; ps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SQLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        	&lt;span class=&quot;token class-name&quot;&gt;Pair&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; dateTimePair &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; historyPairs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            	ps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTimestamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Timestamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dateTimePair&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                ps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTimestamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Timestamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dateTimePair&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;second&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                ps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setLong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; daily&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                ps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setLong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; currentTaskId&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 배열 요소로 접근하여 변경 가능&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBatchSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; historyPairs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
       &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

     currentTaskId&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 다음 그룹에 대해 task_id 증가&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;결론&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EB%A1%A0&quot; aria-label=&quot;결론 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결론&lt;/h2&gt;
&lt;p&gt;결과적으로 배치 삽입 연산을 적절히 고려하니, 약 3초의 수행시간이 걸리던 API 가 약 1700ms 정도로 개선되었습니다. 지금은 대용량의 데이터가 아니라 엄청난 차이는 아닐지 몰라도, 만일 1만개 이상의 대용량의 데이터를 삽입하게 된다면 2,3배를 넘어서 더욱이 큰 성능차이가 보일겁니다 😎&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;batchUpdate (이 연산을 더욱이 능숙하게 다루기 위해 추가적인 학습이 필요한 것 같습니다. 처음이라 조금 낯설기감이 있는 것 같네요 🥲&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;만일 IDENTITY 전략외에 다른 전략을 활용시 JPA 의 배치 기능을 활성화를 위한 부가적인 설정이 필요한가?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://techblog.woowahan.com/2695/&quot;&gt;https://techblog.woowahan.com/2695/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://digda.tistory.com/42&quot;&gt;https://digda.tistory.com/42&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pepperoni.netlify.app/batch-insert/&quot;&gt;https://pepperoni.netlify.app/batch-insert/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jaehun2841.github.io/2020/11/22/2020-11-22-spring-data-jpa-batch-insert/#rewrite-%EC%98%B5%EC%85%98&quot;&gt;https://jaehun2841.github.io/2020/11/22/2020-11-22-spring-data-jpa-batch-insert/#rewrite-%EC%98%B5%EC%85%98&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gksdudrb922.tistory.com/154&quot;&gt;https://gksdudrb922.tistory.com/154&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://datamoney.tistory.com/319&quot;&gt;https://datamoney.tistory.com/319&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@penrose_15/MySQL%EC%97%90%EC%84%9C-Bulk-Insert%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%EB%BB%98%EC%A7%93-%EB%AA%A8%EC%9D%8C&quot;&gt;https://velog.io/@penrose_15/MySQL에서-Bulk-Insert를-사용하기-위한-뻘짓-모음&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@choiyunh/Spring-Data-JPA-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%97%90%EC%84%9C-save%EC%99%80-saveall-%EB%B9%84%EA%B5%90&quot;&gt;https://velog.io/@choiyunh/Spring-Data-JPA-트랜잭션에서-save와-saveall-비교&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Entity N+1 문제에서 발생한 다중 패치조인 문제와 해결법]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/jpa/n+1-with-multi-fetch/</link><guid isPermaLink="false">https://haon.site/haon/jpa/n+1-with-multi-fetch/</guid><pubDate>Fri, 21 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;문제-발생배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%A0%9C-%EB%B0%9C%EC%83%9D%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;문제 발생배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문제 발생배경&lt;/h2&gt;
&lt;p&gt;현재 진행중인 사이드 프로젝트에서 개발을 진행하다가 &lt;code class=&quot;language-text&quot;&gt;Entity N+1 문제&lt;/code&gt; 를 발견했습니다. 이를 해결하기 위해, 통상적으로 알려진 &lt;code class=&quot;language-text&quot;&gt;Fetch Join&lt;/code&gt; 쿼리를 여럿 날렸습니다. 하지만, 깊게 이해하지 못한체 쿼리를 남용하며 날리다보니 &lt;code class=&quot;language-text&quot;&gt;MultipleBagFetchException&lt;/code&gt; 이슈를 직면하게 되었습니다. 이를 해결하기까지의 어떤 개선 과정이 있었으며, 왜 Fetch Join 을 남용하여 쿼리를 날리면 안되는지에 대해 다루어보고자 합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;N+1 문제, 지연로딩(Lazy Loading), 패치 조인(Fetch Join) 에 대한 이론적인 지식은 추후 포스팅에서 깊게 다루도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;실제 프로젝트 코드는 정책 및 로직 구현 내용이 꽤 복잡하므로, 기본 구현 코드로만 다루도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;엔티티-매핑관계-소개&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%94%ED%8B%B0%ED%8B%B0-%EB%A7%A4%ED%95%91%EA%B4%80%EA%B3%84-%EC%86%8C%EA%B0%9C&quot; aria-label=&quot;엔티티 매핑관계 소개 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;엔티티 매핑관계 소개&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/8f8fa63f-6a58-4f01-b82c-3e38e965e10c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이해를 돕기위해, 각 엔티티 클래스와 매핑 관계를 간략히 소개해보겠습니다. 현재 유저의 모든 Daily 데이터를 조회하는 것이 로직인데, 이를위해 Daily 와 매핑되어있는 TimeLine, DailyTask, TaskHistory 를 조회해야합니다. 또한 Task 엔티티에서 taskName 필드 값 리스트를 추출하기위해, Task 또한 조회해야하는 상황입니다.&lt;/p&gt;
&lt;h3 id=&quot;daily&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#daily&quot; aria-label=&quot;daily permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Daily&lt;/h3&gt;
&lt;p&gt;Daily 엔티티에 대한 클래스입니다. 앞서 설명한 매핑 관계가 그대로 담겨있으며, 특히 timeLine 의 경우 &lt;code class=&quot;language-text&quot;&gt;OneToOne&lt;/code&gt; 의 관계로 담겨있습니다. 또 &lt;code class=&quot;language-text&quot;&gt;ManyToOne&lt;/code&gt; 관계에 놓여있는 dailyTaskList 의 경우 &lt;code class=&quot;language-text&quot;&gt;지연로딩(Lazy Loading)&lt;/code&gt; 을 설정해주었는데, 기본값은 즉시로딩(Eager Loadin) 을 수행하므로, 필요한 경우에만 엔티티 조회를 수행할 수 있도록 지연로딩을 설정해줬습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@NoArgsConstructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;access &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AccessLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PROTECTED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Daily&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GeneratedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenerationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;daily_id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; daily_id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;max_time&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; maxTime&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;day&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalDateTime&lt;/span&gt; day&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@OneToMany&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mappedBy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;daily&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DailyTask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; dailyTaskList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ManyToOne&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fetch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FetchType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;LAZY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@JoinColumn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;user_id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@OneToMany&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mappedBy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;daily&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TaskHistory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; taskHistoryList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@OneToOne&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mappedBy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;daily&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TimeLine&lt;/span&gt; timeLine&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;dailyrepository-이슈-발생-쿼리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dailyrepository-%EC%9D%B4%EC%8A%88-%EB%B0%9C%EC%83%9D-%EC%BF%BC%EB%A6%AC&quot; aria-label=&quot;dailyrepository 이슈 발생 쿼리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DailyRepository (이슈 발생 쿼리)&lt;/h3&gt;
&lt;p&gt;이번의 핵심 코드라고 할 수 있는 JPQL 쿼리 코드입니다. &lt;code class=&quot;language-text&quot;&gt;Fetch join&lt;/code&gt; 이 무려 3개나 수행되는 모습을 볼 수 있는데, 여기서 문제가 발생했습니다. 실행 결과를 아래에서 확인해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DailyRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JpaRepository&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Daily&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;select m from Daily m join fetch m.timeLine join fetch m.taskHistoryList join fetch m.dailyTaskList d join fetch d.task where m.user = :user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Daily&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findDailyAllInfoByUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// join fetch m.taskHistoryList&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;실행결과&lt;/h4&gt;
&lt;p&gt;예상했듯이, &lt;code class=&quot;language-text&quot;&gt;MultipleBagException&lt;/code&gt; 이슈가 발생했습니다. fetch join 을 활용하여 N+1 문제를 해결하기는 커녕, 여러번 Fetch join 을 했더니 문제가 발생했는데, 왜 이렇게 fetch join 을 남발했다고 해서 문제가 발생하는걸까요?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/f3474cf5-32c5-4f32-a9ec-000aa8d64973/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;jpa-의-fetch-join-특징&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jpa-%EC%9D%98-fetch-join-%ED%8A%B9%EC%A7%95&quot; aria-label=&quot;jpa 의 fetch join 특징 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JPA 의 Fetch Join 특징&lt;/h2&gt;
&lt;p&gt;JPA 에서 Fetch Join 은 N+1 문제를 해결하기 위해서 가장 보편적으로 사용되는 방법입니다. 하지만 이 방식에는 몇가지 사용 조건사항이 따릅니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;OneToOne, ManyToOne (&quot;ToOne&quot;) 관계에 대해선 몇개든 사용 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;ManyToMany, OneToMany (&quot;ToMany&quot;) 는 단 1개만 사용 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;즉, &lt;code class=&quot;language-text&quot;&gt;MultipleBagException&lt;/code&gt; 은 2개 이상의 자식 엔티티에 대해 fetch join 을 수행했을때 발생하는 이슈입니다. 다시말해서, 본인의 부모 엔티티에 대해선 얼마든지 fetch join 을 마음껏 여러개를 수행해도 상관없습니다.&lt;/p&gt;
&lt;h3 id=&quot;multiplebagexception-이-왜-발생한거지-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#multiplebagexception-%EC%9D%B4-%EC%99%9C-%EB%B0%9C%EC%83%9D%ED%95%9C%EA%B1%B0%EC%A7%80-&quot; aria-label=&quot;multiplebagexception 이 왜 발생한거지  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MultipleBagException 이 왜 발생한거지? 🔥&lt;/h3&gt;
&lt;p&gt;정리하면, &lt;strong&gt;이 문제는 2개이상의 컬렉션을 Fetch Join으로 로드하려고 하는 경우에 발생합니다.&lt;/strong&gt; JPA 에서는 여러 개의 컬렉션을 동시에 fetchJoin으로 가져오는 것을 지원하지 않기 때문입니다. 그 이유는 컬렉션 자체가 JPA 에서는 별도의 테이블로 구현되기 때문입니다.&lt;/p&gt;
&lt;p&gt;컬렉션 중에서 &lt;strong&gt;List 같은 경우는 원소의 순서가 중요하고 중복된 원소를 허용합니다.&lt;/strong&gt; 이러한 특징 때문에 JPA는 이러한 컬렉션을 여러개를 Fetch Join 하는 경우에 문제가 발생 할 수 있습니다. 이렇게 되면 &lt;strong&gt;중복된 데이터가 많이 조회&lt;/strong&gt;될 수 있기 때문에 이러한 경우에 사전에 방지하고자 MultipleBagFetchException가 발생하는 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;원인-분석&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%9D%B8-%EB%B6%84%EC%84%9D&quot; aria-label=&quot;원인 분석 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원인 분석&lt;/h3&gt;
&lt;p&gt;다시 코드를 살펴봅시다. Daily 의 자식 엔티티인 TimeLine 에 대해선 얼마든지 fetch join 을 수행해도 상관없습니다. 그러나, 부모 엔티티인 TaskHistory 와 DailyTask 에 대해서 동시에 fetch join 을 수행하고 있기 때문에 문제가 당연히 발생할 수 밖에 없었던 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DailyRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JpaRepository&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Daily&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;select m from Daily m join fetch m.timeLine join fetch m.taskHistoryList join fetch m.dailyTaskList d join fetch d.task where m.user = :user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Daily&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findDailyAllInfoByUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// join fetch m.taskHistoryList&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;대안1--n1-문제-일부개선&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8C%80%EC%95%881--n1-%EB%AC%B8%EC%A0%9C-%EC%9D%BC%EB%B6%80%EA%B0%9C%EC%84%A0&quot; aria-label=&quot;대안1  n1 문제 일부개선 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;대안1 : N+1 문제 일부개선&lt;/h3&gt;
&lt;p&gt;이슈를 해결하기 위해 여러 접근 방법이 있었습니다. 그나마 괜찮은 해결법으로는, fetch join 을 한 하나의 자식 엔티티에 대해서만 적용하고, 나머지 모든 자식 엔티티에 대해선 지연로딩(Lazy Loading) 하는 방식입니다. 하지만 이는 자식 데이터가 매우 많다면 성능 이슈가 발생할것이 뻔했습니다. 무엇보다 아직 N+1 문제가 해결된 것이 아닙니다.&lt;/p&gt;
&lt;h3 id=&quot;대안2-set-컬렉션-활용하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8C%80%EC%95%882-set-%EC%BB%AC%EB%A0%89%EC%85%98-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0&quot; aria-label=&quot;대안2 set 컬렉션 활용하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;대안2: Set 컬렉션 활용하기&lt;/h3&gt;
&lt;p&gt;컬렉션 List 의 중복 문제를 피하기위해, Set 을 활용하여 중복 이슈를 해결할 수 있습니다. 단, Set은 자료구조 특성상 중복허용과 순서 보장이 되지 않는 문제를 잘 생각하고 사용해야 합니다. 또한 성능 이슈가 있다고하니, 이 방법은 여러 문제를 초래할 것으로 판단되어 사용하지 않았습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;batch-size-활성화하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#batch-size-%ED%99%9C%EC%84%B1%ED%99%94%ED%95%98%EA%B8%B0&quot; aria-label=&quot;batch size 활성화하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Batch Size 활성화하기&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d8471e5b-8190-470f-a626-f8b98156ea39/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;해결첵은 &lt;code class=&quot;language-text&quot;&gt;default_batch_fetch_size&lt;/code&gt; 옵션을 활성화 및 조절에 있었습니다. 그 전에 N+1 문제가 왜 발생하는지를 생각해보면, 결국 부모 엔티티와 연관있는 여러 자식 엔티티들에 대한 조회 쿼리가 문제인 것 입니다. &lt;strong&gt;부모 엔티티를 조회하며 그의 자식 엔티티들을 fetch join시, 부모 엔티티 Key 값 하나하나를 자식 엔티티를 조회해오는데 활용하기 떄문입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DailyRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JpaRepository&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Daily&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;select m from Daily m join fetch m.timeLine join fetch m.dailyTaskList d join fetch d.task where m.user = :user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Daily&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findDailyAllInfoByUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// join fetch m.taskHistoryList&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;MultipleBagException&lt;/code&gt; 를 해결하기 위해 기존 코드를 수정했습니다. TaskHistory 에 대한 Fetch Join 을 제거함으로써 에러가 발생하지 않게했죠. 다만, 이러면 TaskHistory 자식 엔티티에 대한 N+1 문제는 여전히 발생합니다. 이때 바로 해결할 수 있는것이 바로 &lt;code class=&quot;language-text&quot;&gt;default_batch_fetch_size&lt;/code&gt; 인 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;default_batch_fetch_size&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#default_batch_fetch_size&quot; aria-label=&quot;default_batch_fetch_size permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;default_batch_fetch_size&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6c5833d4-abb6-45fb-a48e-8256321c40e7/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;hibernate.default_batch_fetch_size&lt;/code&gt; 옵션은, 지정된 수만큼 &lt;code class=&quot;language-text&quot;&gt;in절&lt;/code&gt; 에 부모 key 를 사용하게 됩니다. 예를들어 옵션값으로 1000을 지정했다면, 한 쿼리에 최대 1000개 만큼의 부모 Key 값이 in 절에 실려서 넘어가서 최대 1000개의 자식 엔티티를 조회해오는 방식입니다.&lt;/p&gt;
&lt;p&gt;즉, 조회 결과가 N개일 때 N개에 대해 각각 1번씩 조회 쿼리를 날리는 것이 아니라 위와 같이 batch size만큼씩 한꺼번에 날리는 것입니다.&lt;/p&gt;
&lt;p&gt;예를들어 조회해야할 부모 엔티티인 daily 가 1500개 있다고 해봅시다. 이 옵션을 미적용하여 N+1 문제가 그대로 발생한다면, 1500개의 조회 쿼리문이 날라라기게 되는것입니다. 반면 옵션을 활성화하면, 단 2번만의 쿼리 (1000개, 500개로 daily_id 값이 분할되어 전송) 가 날라가므로, 성능이 대폭 향상됩니다. 즉, 쿼리가 전송수가 2/1500 으로 대폭 감소 및 향상된것입니다.&lt;/p&gt;
&lt;h3 id=&quot;applicationyml&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#applicationyml&quot; aria-label=&quot;applicationyml permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;application.yml&lt;/h3&gt;
&lt;p&gt;적용 방법은 간단합니다. 아래처럼 &lt;code class=&quot;language-text&quot;&gt;jpa.properties.hibernate.default_batch_fetch_size&lt;/code&gt; 에서 글로벌 옵션을 적용해주면 됩니다. 자식 엔티티 중에 &quot;ToMany&quot; 관계에 있는 엔티티 하나만 fetch join 을 걸고, 나머지는 fetch join 없이 지연로딩(Lazy Loading) 을 수행하면 &lt;code class=&quot;language-text&quot;&gt;in 절&lt;/code&gt; 쿼리가 실행됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;spring&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  datasource&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; jdbc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;mysql&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;111.111&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.11&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3306&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mydb
    driver&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mysql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jdbc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;Driver&lt;/span&gt;
    username&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; haon
    password&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; password

  jpa&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    properties&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      hibernate&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
        default_batch_fetch_size&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;
        format_sql&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
        show_sql&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;참고로 보통 옵션값을 1000 이상 주지는 않습니다. in절에 파라미터로 1000개 이상의 값을 실어서 쿼리를 보냈을떄, 너무 많은 in절 파라미터로 인해 문제가 발생할 수도 있기 때문입니다. 또 지금의 경우 Daily 가 1000개를 넘지 않으면 단일 쿼리 1개로 수행되기 떄문에, 옵션을 이 이상의 값으로 설정하지 않았습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;결론&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EB%A1%A0&quot; aria-label=&quot;결론 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결론&lt;/h2&gt;
&lt;h3 id=&quot;batch-size-를-언제-활성화할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#batch-size-%EB%A5%BC-%EC%96%B8%EC%A0%9C-%ED%99%9C%EC%84%B1%ED%99%94%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;batch size 를 언제 활성화할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Batch Size 를 언제 활성화할까?&lt;/h3&gt;
&lt;p&gt;이렇게까지 Batch Size 를 전역으로 설정한다면, fetch join 이 필요없을 수도 있다 생각이 들 수 있지만, 절대 그렇지 않습니다. &lt;code class=&quot;language-text&quot;&gt;default_batch_fetch_size&lt;/code&gt; 을 적용시 최소한의 성능이 보장되는 것이지, 최적화된 것이 아닙니다. 따라서 자식 엔티티가 개수가 많은 엔티티에 대해 &lt;code class=&quot;language-text&quot;&gt;fetch join&lt;/code&gt; 을 통해 성능을 최대한 보장하며, 나머지의 자식 엔티티에 대해선 옵션을 통해 최소한의 성능 개선을 보장받는다고 생각합시다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&quot;ToMany&quot; 자식 엔티티중에, 가장 데이터가 많은 자식 엔티티에 fetch join 을 수행하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;나머지 자식 엔티티들은 default_batch_fetch_size 으로 최소한의 성능을 보장받자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;쿼리-성능개선&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BF%BC%EB%A6%AC-%EC%84%B1%EB%8A%A5%EA%B0%9C%EC%84%A0&quot; aria-label=&quot;쿼리 성능개선 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쿼리 성능개선&lt;/h3&gt;
&lt;p&gt;300개의 Daily 로 테스트를 진행했을때, N+1 문제를 미고려시 약 7초정도의 쿼리 수행시간이 걸렸습니다. 그런데 위와 같이 N+1 문제를 해결하도록 fetch join 과 batch size 를 적절히 조절하니, 약 2초정도로 쿼리 성능이 &lt;code class=&quot;language-text&quot;&gt;3.5배&lt;/code&gt; 나 개선된 것을 확인할 수 있었습니다 🙂&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Batch insert 쿼리 성능 개선방법&lt;/li&gt;
&lt;li&gt;EntityGraph, Query Builder&lt;/li&gt;
&lt;li&gt;지연로딩(Lazy Loading) 과 즉시로딩(Eager Loading)&lt;/li&gt;
&lt;li&gt;JPA 프록시 객체의 특징&lt;/li&gt;
&lt;li&gt;Cascade&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://jojoldu.tistory.com/457&quot;&gt;https://jojoldu.tistory.com/457&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devlog-wjdrbs96.tistory.com/421&quot;&gt;https://devlog-wjdrbs96.tistory.com/421&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://breakcoding.tistory.com/407&quot;&gt;https://breakcoding.tistory.com/407&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wedul.site/714&quot;&gt;https://wedul.site/714&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://backtony.github.io/jpa/2021-08-12-jpa-springdatajpa-2/&quot;&gt;https://backtony.github.io/jpa/2021-08-12-jpa-springdatajpa-2/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://techblog.woowahan.com/2695/&quot;&gt;https://techblog.woowahan.com/2695/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pidgey.tistory.com/23&quot;&gt;https://pidgey.tistory.com/23&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[SpringBoot AOP 와 프록시 객체는 무엇이고, 왜 필요할까?]]></title><description><![CDATA[학습배경 AOP 과 프록시 객체를 확실히 학습해야겠다는 생각을 하게되어서, 이번에 AOP 에 대해 다루어보고자 합니다. AOP (Aspect Oriented Programming)  AOP 는 Aspect Oriented Programming…]]></description><link>https://haon.site/haon/spring/aop/</link><guid isPermaLink="false">https://haon.site/haon/spring/aop/</guid><pubDate>Tue, 11 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;AOP 과 프록시 객체를 확실히 학습해야겠다는 생각을 하게되어서, 이번에 AOP 에 대해 다루어보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;aop-aspect-oriented-programming&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#aop-aspect-oriented-programming&quot; aria-label=&quot;aop aspect oriented programming permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AOP (Aspect Oriented Programming)&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/49400a29-b92c-459f-a0b7-e5216f742267/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;AOP 는 Aspect Oriented Programming 의 약자로, &lt;code class=&quot;language-text&quot;&gt;관점 지향 프로그래밍&lt;/code&gt; 이라고 불립니다. 어떤 로직을 기준으로 &lt;strong&gt;핵심적인 관점, 부가적인 관점&lt;/strong&gt;으로 나누어서 보고 그 관점을 기준으로 &lt;code class=&quot;language-text&quot;&gt;모듈화&lt;/code&gt; 하겠다는 것입니다. 쉽게말해, 모든 클래스 및 메소드에서 자주 등장할 중복되는 관심사와 코드를 별도로 분리하고 모듈화를 진행함으로써 중복되는 코드를 줄이고 가독성을 높이는 방법이라고 보면됩니다.&lt;/p&gt;
&lt;h3 id=&quot;흩어진-관심사-cross-cutting-concerns&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%9D%A9%EC%96%B4%EC%A7%84-%EA%B4%80%EC%8B%AC%EC%82%AC-cross-cutting-concerns&quot; aria-label=&quot;흩어진 관심사 cross cutting concerns permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;흩어진 관심사 (Cross-Cutting Concerns)&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;AOP 는 Aspect 를 모듈화하고 핵심 비즈니스 로직에서 분리하여 재사용하는 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;가령 위와같이 3개의 서비스가 있고, 각 서비스에 담긴 여러 메소드들은 모두 &lt;code class=&quot;language-text&quot;&gt;로깅(Logging)&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;메소드 수행시간 측정&lt;/code&gt; 이라는 기능을 공통적으로 포함하고 있다고 해보죠. 이들의 의미는 각 클래스 및 메소드에서 중복되는, 필드, 로직 및 코드들이 나타난다는 것입니다. 이렇게 반복되는 코드를 &lt;code class=&quot;language-text&quot;&gt;흩어진 관심사&lt;/code&gt; 라고 부르는 것입니다.&lt;/p&gt;
&lt;p&gt;AOP 는 이렇게 흩어진 관심사를 &lt;code class=&quot;language-text&quot;&gt;Aspect&lt;/code&gt; 를 이용해서 해결합니다. &lt;code class=&quot;language-text&quot;&gt;Aspect&lt;/code&gt; 란 흩어진 관심사라는 공통 부분을 모듈화 시킨 것으로, 개발자는 모듈화 시킨 &lt;code class=&quot;language-text&quot;&gt;Aspect&lt;/code&gt; 를 어느곳에 사용하는지만 정의해주면 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;프록시-패턴proxy-pattern&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A1%9D%EC%8B%9C-%ED%8C%A8%ED%84%B4proxy-pattern&quot; aria-label=&quot;프록시 패턴proxy pattern permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프록시 패턴(Proxy Pattern)&lt;/h2&gt;
&lt;p&gt;아래와 같이 주문 관련 Order 서비스가 있다고해봅시다. 앞서봤던 &quot;실행시간 측정&quot; 이라는 중복 코드를 여러 메소드에 보유하는 상황을 가정할 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;orderservice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#orderservice&quot; aria-label=&quot;orderservice permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OrderService&lt;/h3&gt;
&lt;p&gt;우선 주문 관련 인터페이스를 정의했습니다. 클라이언트는 주문 서비스를 이 인터페이스로 소통하게 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getOrderInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;deleteOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;simpleorderservice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#simpleorderservice&quot; aria-label=&quot;simpleorderservice permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SimpleOrderService&lt;/h3&gt;
&lt;p&gt;OrdeService 를 구현한 SimpleOrderService 입니다. 각 메소드의 수행시간을 측정하도록 기능을 추가했는데, &lt;strong&gt;핵심 비즈니스 로직이 잘 보이지 않아서 가독성이 떨어지고&lt;/strong&gt;, &lt;strong&gt;모든 메소드에 적용할 경우 대량의 중복 코드가 발생&lt;/strong&gt;한다는 단점이 있습니다. 이를 해결하기 위해, AOP 는 기존 코드를 수정하지 않고 공통(중복) 코드를 모두 적용가능하게 하는 &lt;code class=&quot;language-text&quot;&gt;프록시 패턴&lt;/code&gt; 을 제공하고 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SimpleOrderService&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; begin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Completed createing an Order.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; begin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getOrderInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; begin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Completed getting an Order.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; begin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;deleteOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; begin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Completed deleting an Order.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; begin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;프록시-패턴-적용-클래스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A1%9D%EC%8B%9C-%ED%8C%A8%ED%84%B4-%EC%A0%81%EC%9A%A9-%ED%81%B4%EB%9E%98%EC%8A%A4&quot; aria-label=&quot;프록시 패턴 적용 클래스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프록시 패턴 적용 클래스&lt;/h3&gt;
&lt;p&gt;클라이언트는 인터페이스 타입으로 프록시 객체를 사용하게 되고, 프록시는 Real Object 인 SimpleOrderService 를 감싸서 수행시간 측정이라는 부가기능을 처리하게 됩니다. &lt;strong&gt;즉, 인터페이스 타입 기반의 클라이언트 요청은 프록시 객체로 먼저 들어오게 되고, 프록시 객체는 중복되는 코드에 대한&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;부가 기능&lt;/code&gt; &lt;strong&gt;을 수행합니다. 그러고 기존 핵심 비즈니스 로직만을 보유한 클래스가 호출되어 수행되는 방식입니다.&lt;/strong&gt; 이로써 기존 핵심 비즈니스 로직을 별도로 분리하고, 부가 기능은 별도로 분리하며 관심사가 분리된 방식이 되었습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Primary&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProxySimpleEventService&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SimpleOrderService&lt;/span&gt; simpleOrderService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProxySimpleEventService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;OrderService&lt;/span&gt; orderService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;simpleOrderService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; simpleOrderService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; begin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        simpleOrderService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 비즈니스 로직&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; begin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getOrderInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; begin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        simpleOrderService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOrderInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 비즈니스 로작&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; begin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;deleteOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; begin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        simpleOrderService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOrderInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 비즈니스 로직&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; begin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 이 방식도 아직은 중복되는 코드를 계속 처리해줘야한다는 점에선 해결되지 못했습니다. 이를위해 등장한것이 바로 Spring AOP 입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;spring-aop&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#spring-aop&quot; aria-label=&quot;spring aop permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Spring AOP&lt;/h2&gt;
&lt;p&gt;AOP 의 핵심기능은 핵심 비즈니스 로직을 수정하지 않으면서, 공통 부가기능 및 관심사의 구현을 추가하는 것이라고 했었습니다. 핵심 기능에다 공통 부가기능을 추가하는 방법은 아래 3가지 시점에 추가가능합니다.&lt;/p&gt;
&lt;h3 id=&quot;aop-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#aop-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;aop 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AOP 방식&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;컴파일 시점 : 컴파일 시점에 코드에 공통 기능을 삽입. 자바 파일( .java) 을 클래스 파일 (.class) 로 만들때 바이트 코드를 조작하여 적용된 바이트 코드를 생성하는 방식&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;클래스 로딩 시점 : 클래스 로딩 시점에 공통 기능을 삽입. 컴파일은 원래 클래스 그대로 하고, 클래스를 로딩하는 시점에 끼워서 넣는다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;런타임 시점 : 런타임 시점에 프록시 객체를 사용하여 공통 기능을 삽입. A 라는 클래스를 빈으로 만들때 A 라는 타입의 프록시 빈을 감싸서 만든후에, 프록시 빈이 클래스 중간에 코드를 추가해서 넣는다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;스프링은 위 방법중에 3번쨰 방법인 &lt;code class=&quot;language-text&quot;&gt;런타임 시점&lt;/code&gt; 에 공통 기능을 삽입하는 AOP 방법을 채택하고 있습니다. 따라서 &lt;strong&gt;스프링 AOP 는 메소드 실행시점에만 AOP 를 적용할 수 있고, 스프링 컨테이너가 관리하는 빈에만 적용할 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;참고로 알아두면 좋은것은, 컴파일과 클래스 로딩 시점 방식은 AOP 프레임워크는 AspectJ 가 제공하는 컴파일러나 클래스 로더 조작기 같은 새로운 것을 사용해야 합니다. 따라서 더 유연한 AOP 적용이 가능하겠지만, 부가적인 의존성을 추가해야 한다는 단점이 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;스프링-aop-적용하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81-aop-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; aria-label=&quot;스프링 aop 적용하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링 AOP 적용하기&lt;/h3&gt;
&lt;p&gt;먼저 Spring에서 AOP를 사용하기 위해서는 spring-starter-aop 의존성을 추가해주어야 합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;implementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;aop&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;시간측정-모듈-분리-리팩토링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EA%B0%84%EC%B8%A1%EC%A0%95-%EB%AA%A8%EB%93%88-%EB%B6%84%EB%A6%AC-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81&quot; aria-label=&quot;시간측정 모듈 분리 리팩토링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시간측정 모듈 분리 리팩토링&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Aspect&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PerfAspect&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Around&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;execution(* com.example.aop.test.create*(..))&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;logPerf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ProceedingJoinPoint&lt;/span&gt; proceedingJoinPoint&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Throwable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; begin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; reVal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; proceedingJoinPoint&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;proceed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 핵심 비즈니스 로직을 담고있는 타깃&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; begin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; reVal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Spring AOP 는 &lt;strong&gt;무조건 Bean 으로 등록된 대상에 대해서만 프록시 빈으로 감쌉니다.&lt;/strong&gt; 즉, 클래스 A가 스프링 빈으로 등록될때 새로운 프록시 빈이 감싸지고, 그 생성된 프록시 빈이 클래스 A 의 중간에 코드를 추가해서 넣는 방식입니다. 이를위해 &lt;code class=&quot;language-text&quot;&gt;@Component&lt;/code&gt; 어노테이션으로 클래스를 빈으로 등록해줍시다.&lt;/p&gt;
&lt;p&gt;또 &lt;code class=&quot;language-text&quot;&gt;@Aspect&lt;/code&gt; 어노테이션으로 시간측정 공통 코드를 Aspect 모듈로 분리시켜서 정의해었습니다. 메소드에 &lt;code class=&quot;language-text&quot;&gt;@Around&lt;/code&gt; 어노테이션이 있는걸 볼 수 있는데, 이는 스프링에서 구현가능한 Advice 모듈중에 한 종류입니다. &lt;strong&gt;메소드의 실행 전후 또는 Exception 발생 시점에 부가 기능을 추가한다는 것입니다.&lt;/strong&gt; 또한 &lt;code class=&quot;language-text&quot;&gt;execution()&lt;/code&gt; 안에 com.example.aop.test 패키지 밑에있 클래스들 중에서 이름이 create 로 시작하는 메소드에 대해서만 적용함을 명시하는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;ProceedingJoinPoint&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;proceed()&lt;/code&gt; 메소드도 호출한 것을 볼 수 있습니다. 이는 핵심 비즈니스를 들고있는 대상입니다. 즉, 위와같이 정의한 시간측정 모듈을 호출할 대상을 호출하는 것입니다.&lt;/p&gt;
&lt;p&gt;추가적으로 &lt;code class=&quot;language-text&quot;&gt;@Around&lt;/code&gt; 외에도 &lt;code class=&quot;language-text&quot;&gt;@Before&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@AfterRunning&lt;/code&gt; 등으로 어떤 타이밍 및 조건에 부합할때 메소드에 모듈을 적용할지 정할 수 있으니, 찾아보길 바랍니다.&lt;/p&gt;
&lt;h3 id=&quot;aop-주요-개념&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#aop-%EC%A3%BC%EC%9A%94-%EA%B0%9C%EB%85%90&quot; aria-label=&quot;aop 주요 개념 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AOP 주요 개념&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Aspect : 위의 사진에서 처럼 Aspect 안에 모듈화 시킨 것을 의미한다.&lt;/li&gt;
&lt;li&gt;Advice : 실질적으로 어떤 일을 해야하는지를 담고 있다.&lt;/li&gt;
&lt;li&gt;Pointcut : 어디에 적용해야 하는지에 대한 정보를 담고 있다.&lt;/li&gt;
&lt;li&gt;Target : Aspect에 적용이 되는 대상&lt;/li&gt;
&lt;li&gt;Join point : Advice가 적용될 위치, 끼어들 수 있는 지점. 메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능(여러가지 합류 지점임)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/2.5.5/reference/aop.html&quot;&gt;https://docs.spring.io/spring-framework/docs/2.5.5/reference/aop.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devlog-wjdrbs96.tistory.com/398&quot;&gt;https://devlog-wjdrbs96.tistory.com/398&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://code-lab1.tistory.com/193&quot;&gt;https://code-lab1.tistory.com/193&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://atoz-develop.tistory.com/entry/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-AOP-%EA%B0%9C%EB%85%90-%EC%9D%B4%ED%95%B4-%EB%B0%8F-%EC%A0%81%EC%9A%A9-%EB%B0%A9%EB%B2%95&quot;&gt;https://atoz-develop.tistory.com/entry/Spring-스프링-AOP-개념-이해-및-적용-방법&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL 레플리케이션과 스프링부트 DataSource 라우팅을 통한 쿼리 성능개선]]></title><description><![CDATA[데이터베이스 레플리케이션 관련 이론적인 내용은 MySQL 의 Master/Slave 레플리케이션(Replication) 아키텍처와 토폴로지 구성 방식 을 참고하자. 레플리케이션 구성 지난 [MySQL 8.0] Master/Slave…]]></description><link>https://haon.site/haon/mysql/replication-improvement/</link><guid isPermaLink="false">https://haon.site/haon/mysql/replication-improvement/</guid><pubDate>Sun, 09 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;데이터베이스 레플리케이션 관련 이론적인 내용은 &lt;a href=&quot;https://velog.io/@msung99/MySQL-%EC%9D%98-MasterSlave-%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98Replication-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%99%80-%ED%86%A0%ED%8F%B4%EB%A1%9C%EC%A7%80-%EA%B5%AC%EC%84%B1-%EB%B0%A9%EC%8B%9D&quot;&gt;MySQL 의 Master/Slave 레플리케이션(Replication) 아키텍처와 토폴로지 구성 방식&lt;/a&gt; 을 참고하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;레플리케이션-구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;레플리케이션 구성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레플리케이션 구성&lt;/h2&gt;
&lt;p&gt;지난 &lt;a href=&quot;https://velog.io/@msung99/MySQL-8.0-MasterSlave-%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EA%B5%AC%EC%A1%B0%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B6%84%EC%82%B0-%ED%99%98%EA%B2%BD%EC%9D%84-%EA%B5%AC%EC%B6%95%ED%95%B4%EB%B3%B4%EC%9E%90&quot;&gt;[MySQL 8.0] Master/Slave 레플리케이션 구조로 데이터베이스 분산 환경을 구축해보자!&lt;/a&gt; 에서는 MySQL 서버 2대를 띄워놓고, 각 서버의 데이터베이스 2대를 Master-Slave 구조로 구축해서 실시간으로 동기화가 이루어지도록 환경 구축을 마쳤습니다. 이번에는 스프링부트에서도 이 환경에 알맞게 DataSource 를 분기할 수 있도록 개발을 진행했는데, 이를 다루어보고자 합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5b955bf8-deb8-46f6-8210-f996b6097a6e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;레플리케이션 환경에 대한 구성은 위와 같습니다. MySQL 서버를 2대 띄운것은 지난번과 동일하며, 트랜잭션의 &lt;code class=&quot;language-text&quot;&gt;readOnly&lt;/code&gt; 설정값에 따라서 Master/Slave 중에 요청이 분산됩니다. Master 서버는 쓰기전용 서버로 배치했기 떄문에 쓰기 요청은 모두 마스터 서버로 분기되며, Slave 서버는 읽기전용 서버로 배치했기 때문에 읽기 연산은 슬레이브 서버로 분기됩니다.&lt;/p&gt;
&lt;p&gt;데이터베이스에 요청되는 &lt;strong&gt;대부분의 요청은 조회 쿼리문(Select)&lt;/strong&gt; 이므로, 이렇게 &lt;strong&gt;조회 전용 데이터베이스 서버를 따로 배치&lt;/strong&gt;해둔다면 조회 쿼리 성능이 꽤나 향상될것을 기대해볼 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;applicationyml&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#applicationyml&quot; aria-label=&quot;applicationyml permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;application.yml&lt;/h2&gt;
&lt;p&gt;이번에 구축할 환경이 &lt;code class=&quot;language-text&quot;&gt;Multi DataSource&lt;/code&gt; 임을 감안하여, 스프링부트 application.yml 에 DataSource 에 대한 설정을 여러개로 구분하여 구성해줬습니다. &lt;code class=&quot;language-text&quot;&gt;spring.datasource.master&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;spring.datasource.slave&lt;/code&gt; 로 이중 구성을 진행해줬으며, 추후 아래에서 살펴볼 Config 파일들에서 &lt;code class=&quot;language-text&quot;&gt;readOnly&lt;/code&gt; 옵션값에 따라서 각 DataSource 에 대한 분기 요청을 설정해 줄 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;spring&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  datasource&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    master&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      username&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; haon
      password&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; password
      driver&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mysql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jdbc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;Driver&lt;/span&gt;
      jdbc&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; jdbc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;mysql&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;111.111&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.11&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3306&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;replication
    slave&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      username&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; haon
      password&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; password
      driver&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mysql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jdbc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;Driver&lt;/span&gt;
      jdbc&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; jdbc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;mysql&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;222.222&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.22&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3306&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;replication

  mvc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    pathmatch&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      matching&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;strategy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ant_path_matcher

  jpa&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    database&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;platform&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hibernate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dialect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;MySQLDialect&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;in&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;view&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    show&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;sql&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  sql&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    init&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      platform&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; mysql&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;datasourceconfig&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#datasourceconfig&quot; aria-label=&quot;datasourceconfig permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DataSourceConfig&lt;/h2&gt;
&lt;p&gt;가장 먼저 DataSource 에 대한 Config 파일인데, 설정정보 구성 전체 코드는 아래와 같습니다. 한 단위씩 끊어서 자세히 설명해보겠습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; master &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; slave &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;slave&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;master&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ConfigurationProperties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prefix &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;spring.datasource.master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;masterDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;slave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ConfigurationProperties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prefix &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;spring.datasource.slave&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slaveDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;master&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; masterDataSource&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;slave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; slaveDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt; routingDataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 쿼리 요청을 적절한 서버로 분기할 때 활용됨&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; targetDataSourceMap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// targetDataSourceMap 객체에 분기할 서버들의 DataSource 빈을 저장&lt;/span&gt;
        targetDataSourceMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; masterDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        targetDataSourceMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;slave&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; slaveDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTargetDataSources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;targetDataSourceMap&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// DataSource 타깃을 설정한다.&lt;/span&gt;
        routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDefaultTargetDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;masterDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Primary&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; determinedDataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;masterDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slaveDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LazyConnectionDataSourceProxy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;determinedDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;mastersource-slavedatasource-빈-등록&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mastersource-slavedatasource-%EB%B9%88-%EB%93%B1%EB%A1%9D&quot; aria-label=&quot;mastersource slavedatasource 빈 등록 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;masterSource, slaveDataSource 빈 등록&lt;/h3&gt;
&lt;p&gt;우선 DataSourceConfig 에 새롭게 추가한 MySQL 서버에 대한 &lt;code class=&quot;language-text&quot;&gt;DataSource 빈&lt;/code&gt; 을 등록해야합니다. 이를위해 아래와 같이 &lt;code class=&quot;language-text&quot;&gt;Master&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;Slave&lt;/code&gt; 에 대한 두개의 DataSource 타입 빈을 등록해줬습니다.&lt;/p&gt;
&lt;p&gt;이때 &lt;code class=&quot;language-text&quot;&gt;@ConfigurationProperties&lt;/code&gt; 을 사용하면 &lt;code class=&quot;language-text&quot;&gt;application.yml&lt;/code&gt; 에서 특정 &lt;code class=&quot;language-text&quot;&gt;prefix&lt;/code&gt; 에 해당하는 설정 값만을 자바 Bean 에다 매핑할 수 있습니다. 즉 2개의 DataSource 타입의 빈에 대해 서로 다른 prefix 설정정보 값을 기반으로 빈이 구성되도록 해줬습니다.&lt;/p&gt;
&lt;p&gt;또한 &lt;code class=&quot;language-text&quot;&gt;@Quaifier&lt;/code&gt; 를 사용한 것을 볼 수 있습니다. &lt;a href=&quot;https://velog.io/@msung99/%EC%95%8C%EB%A9%B4-%EB%8F%84%EC%9B%80%EB%90%A0-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%EC%BA%94%EC%9D%98-%EB%8B%A4%EC%96%91%ED%95%9C-%EB%8C%80%EC%83%81%EB%93%A4%EA%B3%BC-DI-%EC%97%90-%EB%8C%80%ED%95%9C-%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95&quot;&gt;[ loc/DI ] 컴포넌트 스캔의 다양한 대상들과 DI 에 대한 해결방법&lt;/a&gt; 에서도 다루었듯이, 기본적으로 의존관계 주입은 &lt;code class=&quot;language-text&quot;&gt;타입&lt;/code&gt; 을 기준으로 빈을 등록하고 매핑되기 떄문에 동일한 타입이 있을때 별도의 처리가 없다면 &lt;code class=&quot;language-text&quot;&gt;NoUniqueBeanDefinitionException&lt;/code&gt; 이 발생한다고 했었습니다. 이를 해결하도록 동일한 &lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 타입을 가지는 2개의 빈에 대해 &lt;code class=&quot;language-text&quot;&gt;@Quaifier&lt;/code&gt; 로 추가 구분자명을 부여해서 2개의 빈이 구분되어 등록되도록 해줬습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; master &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; slave &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;slave&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;master&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@ConfigurationProperties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prefix &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;spring.datasource.master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;masterDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;slave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@ConfigurationProperties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prefix &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;spring.datasource.slave&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slaveDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSourceBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;abstractroutingdatasource-상속&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#abstractroutingdatasource-%EC%83%81%EC%86%8D&quot; aria-label=&quot;abstractroutingdatasource 상속 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AbstractRoutingDataSource 상속&lt;/h2&gt;
&lt;p&gt;DataSourceConfig 의 나머지 메소드들은 잠깐 제치고, &lt;code class=&quot;language-text&quot;&gt;RoutingDataSource()&lt;/code&gt; 클래스를 봅시다. 스프링에서는 &lt;strong&gt;여러개의 DataSource 를 하나로 묶고 자동으로 분기&lt;/strong&gt;해주는 &lt;code class=&quot;language-text&quot;&gt;AbstractRoutingDataSource&lt;/code&gt; 클래스를 제공합니다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;determineCurrentLookupKey()&lt;/code&gt; 를 오버라이드 했는데, 여러개의 DataSource 중에서 실제로 사용될 DataSource 를 결정하는 역할을 합니다. 즉, Master 와 Slave 서버중에서 어떤것을 사용할지를 결정하는데, &lt;code class=&quot;language-text&quot;&gt;TransactionSynchronizationManager&lt;/code&gt; 로 현재 요청에 대한 트랜잭션 속성이 &lt;code class=&quot;language-text&quot;&gt;읽기전용&lt;/code&gt; 인지, 아니면 &lt;code class=&quot;language-text&quot;&gt;쓰기전용&lt;/code&gt; 인지에 따라서 마스터 또는 슬레이브 서버중에 어떤것을 사용할지를 결정하는 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//  AbstractRoutingDataSource : Multi DataSource 환경에서 여러 DataSource 를 묶고 분기해줄 때 사용한다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AbstractRoutingDataSource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// determineCurrentLookupKey 메소드 : 여러 datasource 중에서 실제로 사용될 DataSource 를 결정하는 역할&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 현재 트랜잭션의 속성에 따라 targetDataSourceMap 의 조회 Key 를 결정하기위해 AbstractRoutingDataSource 를 상속받아서 determineCurrentLookupKey 를 구현했다.&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;determineCurrentLookupKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; isReadOnly &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionSynchronizationManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isCurrentTransactionReadOnly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;현재 트랜잭션 속성이 ReadOnly인가?:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; isReadOnly&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; isReadOnly &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;slave&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;routingdatasource&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#routingdatasource&quot; aria-label=&quot;routingdatasource permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;routingDataSource&lt;/h3&gt;
&lt;p&gt;다시 &lt;code class=&quot;language-text&quot;&gt;DataSourceConfig&lt;/code&gt; 로 돌아와서, &lt;code class=&quot;language-text&quot;&gt;routingDataSource()&lt;/code&gt; 에 대해 봅시다. 앞서 정의한 &lt;code class=&quot;language-text&quot;&gt;RoutingDataSource&lt;/code&gt; 객체를 사용해서, 트랜잭션 요청을 적절한 DataSource 서버로 분기하기 위해 선언해줬습니다.&lt;/p&gt;
&lt;p&gt;또한 &lt;code class=&quot;language-text&quot;&gt;HashMap&lt;/code&gt; 을 생성하고, 앞서 정의한 &lt;code class=&quot;language-text&quot;&gt;masterDataSource&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;sourceDataSource&lt;/code&gt; 에 대한 스프링 빈을 각각 &lt;code class=&quot;language-text&quot;&gt;&quot;master&quot;&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;&quot;slave&quot;&lt;/code&gt; 로라는 Key 값과 매핑시켰습니다.&lt;/p&gt;
&lt;p&gt;HashMap 에 저장된 이 2개의 DataSource 를 &lt;code class=&quot;language-text&quot;&gt;RoutingDataSource&lt;/code&gt; (정확히는 AbstractRoutingDataSource) 의 &lt;code class=&quot;language-text&quot;&gt;setTargetDataSources()&lt;/code&gt; 로 현 스프링부트 서버의 DataSource 타깃을 설정해줬으며, &lt;code class=&quot;language-text&quot;&gt;setDefaultTargetDataSource()&lt;/code&gt; 로 기본 데이터 소스를 마스터 서버로 지정해줬습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;master&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; masterDataSource&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token annotation punctuation&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;slave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; slaveDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

   &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt; routingDataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RoutingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 쿼리 요청을 적절한 서버로 분기할 때 활용됨&lt;/span&gt;
   &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; targetDataSourceMap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

   &lt;span class=&quot;token comment&quot;&gt;// targetDataSourceMap 객체에 분기할 서버들의 DataSource 빈을 저장&lt;/span&gt;
   targetDataSourceMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;master&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; masterDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   targetDataSourceMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;slave&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; slaveDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

   routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTargetDataSources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;targetDataSourceMap&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// DataSource 타깃을 설정한다.&lt;/span&gt;
   routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDefaultTargetDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;masterDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; routingDataSource&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;datasource-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#datasource-&quot; aria-label=&quot;datasource  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;dataSource( )&lt;/h3&gt;
&lt;p&gt;마지막으로 &lt;code class=&quot;language-text&quot;&gt;DataSourceConfig&lt;/code&gt; 에 대한 마지막 스프링 빈입니다. 현 스프링부트 전반에서 사용될 최종적인 &lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 빈을 등록해주는 것입니다.&lt;/p&gt;
&lt;p&gt;이때 &lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourceProxy&lt;/code&gt; 를 사용하는 모습을 볼 수 있습니다. 스프링부트는 트랜잭션에 진입하는 순간부터 설정된 DataSource 의 커넥션을 가져옵니다. 이것이 Multi Source 환경에서 문제가 될 수 있습니다. 트랜잭션에 진입한 이후에 DataSource 를 결정해야하는데, 예전에 이미 트랜잭션 진입시점에 DataSource 를 결정하고 커넥션을 획득했기 때문에 분기가 불가능합니다. 이를 해결가능하게 하는 것이 바로 &lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourceProxy&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourceProxy&lt;/code&gt; 를 사용하면 실제로 커넥션이 필요한 경우가 아니라면, 데이터베이스 풀에서 커넥션을 점유하지 않고 필요한 시점에만 커넥션을 점유할 수 있게 됩니다. 즉, 실제로 쿼리가 시작되고 &lt;code class=&quot;language-text&quot;&gt;Repository&lt;/code&gt; 계층을 활용하게 될때 &lt;code class=&quot;language-text&quot;&gt;DataSource&lt;/code&gt; 가 결정되고 커넥션을 맺는 방식으로 동작합니다. 요약하자면, &lt;code class=&quot;language-text&quot;&gt;Datasource&lt;/code&gt; 를 결정하고 커넥션을 획득하는 시점을 지연(Lazy) 시켜서 정상적으로 DataSource 를 결정하도록 하는 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Primary&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;DataSource&lt;/span&gt; determinedDataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routingDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;masterDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slaveDataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LazyConnectionDataSourceProxy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;determinedDataSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourceProxy&lt;/code&gt; 와 관련된 더욱이 깊은 내용은 추후 별도의 포스팅으로 자세히 다루도록 하겠습니다. 이와 관련해 더 깊은 학습해봐야겠다고 느껴지네요!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;osiv-이슈--아직-완전히-해결된게-아니라고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#osiv-%EC%9D%B4%EC%8A%88--%EC%95%84%EC%A7%81-%EC%99%84%EC%A0%84%ED%9E%88-%ED%95%B4%EA%B2%B0%EB%90%9C%EA%B2%8C-%EC%95%84%EB%8B%88%EB%9D%BC%EA%B3%A0&quot; aria-label=&quot;osiv 이슈  아직 완전히 해결된게 아니라고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OSIV 이슈 🔥 (아직 완전히 해결된게 아니라고?)&lt;/h2&gt;
&lt;p&gt;사실 이렇게만 진행했다면 &lt;code class=&quot;language-text&quot;&gt;OSIV 문제&lt;/code&gt; 가 발생합니다. 실제로 제가 이렇게 까지만 DataSource 환경을 구축했을떄, DataSource 분기가 이루어지지 않는 문제가 발생했었습니다. 이와 관련해서는 조만간 매우 깊은 내용과 함께 새로운 포스팅에서 다루어보고자 합니다.&lt;/p&gt;
&lt;p&gt;따라서 지금으로써는, OSIV 이슈를 해결했음을 가정하고 쿼리 성능 측정을 진행했다고 보시면 될 것 같습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;실제-쿼리성능-측정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%EC%A0%9C-%EC%BF%BC%EB%A6%AC%EC%84%B1%EB%8A%A5-%EC%B8%A1%EC%A0%95&quot; aria-label=&quot;실제 쿼리성능 측정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실제 쿼리성능 측정&lt;/h2&gt;
&lt;p&gt;실제로 분기가 잘 되는지를 직접 확인해보기 위해, 간단히 스키마를 하나 정의하고 API 를 정의했습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h4&gt;성능 측정환경&lt;/h4&gt;
&lt;p&gt;(몰론 제가 현재 운영하는 실제 프로젝트와는 환경이 많이 다릅니다. 레플리케이션 학습 차원에서 확인하도록 이렇게 구축한 것입니다 🙂)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;MySQL 데이터베이스를 2대 띄웠다. (지난번과 동일)&lt;/li&gt;
&lt;li&gt;데이터베이스에 &lt;code class=&quot;language-text&quot;&gt;약 16300개의 더미데이터&lt;/code&gt; 를 삽입했다.&lt;/li&gt;
&lt;li&gt;스프링부트 API 서버를 한대 띄웠다.&lt;/li&gt;
&lt;li&gt;API 를 간단히 정의하고, Jmeter 를 활용하여 200개의 쓰레드를 생성하고 읽기/쓰기 동시 요청을 보낸다.&lt;/li&gt;
&lt;li&gt;레플리케이션을 미적용했을때의 API 서버로 따로 성능 측정을 진행했다. 적용과 미적용했을때의 성능을 비교해볼 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;user&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#user&quot; aria-label=&quot;user permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;User&lt;/h3&gt;
&lt;p&gt;엔티티에 대한 간단히 클래스를 정의해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@AllArgsConstructor&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Setter&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GeneratedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenerationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; age&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; age&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;age &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; age&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;usercontroller&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#usercontroller&quot; aria-label=&quot;usercontroller permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UserController&lt;/h3&gt;
&lt;p&gt;API 2개를 정의했습니다. GET 요청의 경우는, 특정 나이에 해당하는 모든 유저를 데이터베이스에서 조회하는 로직입니다. POST 요청의 경우는, 간단히 유저를 생성하는 로직입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/user/{age}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getUserInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@PathVariable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; age&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;age&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@PostMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        userService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;회원가입에 성공했습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;userservice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#userservice&quot; aria-label=&quot;userservice permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UserService&lt;/h3&gt;
&lt;p&gt;getUserInfo 와 createUser 메소드를 간단히 정의했습니다. 이때 createUser 의 경우는 유의미한 더미데이터 값을 삽입하기위해 UUID 와 random 값을 기반으로 컬럼값을 삽입하도록 구현했습니다. 이때 age 값의 경우 1~99 사이의 값이 되도록 해줬습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRepository&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserRepository&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;readOnly &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getUserInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; age&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; user_list &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findAllByAge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;age&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; user_list&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; random_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;randomUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Random&lt;/span&gt; random &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; random_age &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;random&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;nextLong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;99&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;random_name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; random_age&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        userRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;userrepository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#userrepository&quot; aria-label=&quot;userrepository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UserRepository&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Repository&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JpaRepository&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findAllByAge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; age&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;response-성능-측정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#response-%EC%84%B1%EB%8A%A5-%EC%B8%A1%EC%A0%95&quot; aria-label=&quot;response 성능 측정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Response 성능 측정&lt;/h2&gt;
&lt;p&gt;이렇게 모든 작업이 끝난후, 실제로 성능 테스트를 진행했습니다. 앞서 말했듯이 &lt;code class=&quot;language-text&quot;&gt;약 16300 개&lt;/code&gt; 의 더미데이터를 삽입해줬습니다. 또한 Master/Slave 미적용 환경에서 동일한 API 를 개발했을때와 비교를 진행하고, 레플리케이션을 적용했을때 실제로 얼마나 쿼리 성능이 개선되었는지를 확인했습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/889a47b8-5990-4361-95ef-dfeac4d6c81f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;쿼리-성능-개선결과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BF%BC%EB%A6%AC-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%EA%B2%B0%EA%B3%BC&quot; aria-label=&quot;쿼리 성능 개선결과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쿼리 성능 개선결과&lt;/h3&gt;
&lt;p&gt;Jmeter 를 활용하여 200개의 쓰레드를 POST 요청으로 보내고, 나머지 소수의 GET 요청들로 조회 쿼리를 실행하도록 진행했습니다.&lt;/p&gt;
&lt;p&gt;레플리케이션을 미적용했을때는, &lt;code class=&quot;language-text&quot;&gt;약 1400~1700ms&lt;/code&gt; 가량의 조회 응답시간을 확인할 수 있었으며, 반대로 레플리케이션 적용시에는 &lt;code class=&quot;language-text&quot;&gt;약 500~900ms&lt;/code&gt; 가량의 조회 응답시간이라는 결과를 얻어낼 수 있었습니다. 이렇듯 조회 쿼리의 경우 대략 &lt;code class=&quot;language-text&quot;&gt;2배&lt;/code&gt; 가량의 성능이 향상된 결과를 도출할 수 있게 되었습니다 🙂&lt;/p&gt;
&lt;h3 id=&quot;쿼리-성능-향상원인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BF%BC%EB%A6%AC-%EC%84%B1%EB%8A%A5-%ED%96%A5%EC%83%81%EC%9B%90%EC%9D%B8&quot; aria-label=&quot;쿼리 성능 향상원인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쿼리 성능 향상원인&lt;/h3&gt;
&lt;p&gt;이미 이에 대한 정답은 알고있지만, 마무리하면서 다시 확실히 정리도 해볼겸 쿼리가 어떤 이유로 향상되었는지를 요약해보고자 합니다.&lt;/p&gt;
&lt;p&gt;우선 레플리케이션의 주 목적은 &lt;code class=&quot;language-text&quot;&gt;부하 분산&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;고가용성&lt;/code&gt; 을 위해서라고 계속 얘기했었습니다. Master 데이터베이스의 읽기작업을 Slave 로 분산시킴으로써 당연히 읽기 부하가 분산되고 부하가 감소해서 응답속도가 향상될것입니다. 또한 이렇게 분산 환경을 구축함으로써 &lt;code class=&quot;language-text&quot;&gt;병렬 처리&lt;/code&gt; 가 가능해지는 것이므로 많은 트래픽이 유입되었을때 당연히 작업 속도가 전반적으로 향상될것입니다.&lt;/p&gt;
&lt;p&gt;읽기(조회) 쿼리 성능만 향상되는 것처럼 언급했는데, 쓰기 쿼리 성능도 당연히 향상될것입니다. 몰론 읽기 요청만큼 가벼운 요청은 아니라서 엄청난 성능 향상의 기대는 힘들겠지만, 슬레이브 서버에게 읽기 요청을 분산시킨다는 점에서 부하가 줄어드는 것은 당연한 사실입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해봐야할-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B4%90%EC%95%BC%ED%95%A0-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해봐야할 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해봐야할 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;OSIV&lt;/li&gt;
&lt;li&gt;LazyConnectionDataSourceProxy&lt;/li&gt;
&lt;li&gt;DataSource&lt;/li&gt;
&lt;li&gt;프록시 객체&lt;/li&gt;
&lt;li&gt;트랜잭션 동기화&lt;/li&gt;
&lt;li&gt;TransactionSynchronizationManager&lt;/li&gt;
&lt;li&gt;글로벌 트랜잭션&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;특히 초기에 &lt;code class=&quot;language-text&quot;&gt;LazyConnectionDataSourceProxy&lt;/code&gt; 를 미적용했을때 트랝개션 ReadOnly 속성값을 제대로 못 읽어오는 이슈가 발생했었는데, 이에 관련해 조만간 깊게 학습해보고자 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@ch4570/MySQL-Master-Slave-%EA%B5%AC%EC%A1%B0%EC%97%90%EC%84%9C-Slave%EB%A5%BC-Scale-Out-%ED%95%B4%EB%B3%B4%EA%B8%B0&quot;&gt;https://velog.io/@ch4570/MySQL-Master-Slave-구조에서-Slave를-Scale-Out-해보기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chagokx2.tistory.com/100&quot;&gt;https://chagokx2.tistory.com/100&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kwonnam.pe.kr/wiki/springframework/lazyconnectiondatasourceproxy&quot;&gt;https://kwonnam.pe.kr/wiki/springframework/lazyconnectiondatasourceproxy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sup2is.github.io/2021/07/08/lazy-connection-datasource-proxy.html&quot;&gt;https://sup2is.github.io/2021/07/08/lazy-connection-datasource-proxy.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL Master/Slave 레플리케이션 구조로 DB 라우팅을 구축해보자!]]></title><description><![CDATA[레플리케이션 도입배경 데이터베이스 클러스터링(Clustering) 과 샤딩(Sharding…]]></description><link>https://haon.site/haon/mysql/replication-env/</link><guid isPermaLink="false">https://haon.site/haon/mysql/replication-env/</guid><pubDate>Sat, 08 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;레플리케이션-도입배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EB%8F%84%EC%9E%85%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;레플리케이션 도입배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레플리케이션 도입배경&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81Clustering-%EA%B3%BC-%EC%83%A4%EB%94%A9Sharding-%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EA%B3%A0%EA%B0%80%EC%9A%A9%EC%84%B1%EA%B3%BC-%EC%8A%A4%EC%BC%80%EC%9D%BC%EC%95%84%EC%9B%83&quot;&gt;데이터베이스 클러스터링(Clustering) 과 샤딩(Sharding) 을 활용한 고가용성과 스케일아웃&lt;/a&gt; 에서도 다루었듯이, 어떤 방식으로 데이터베이스 분산 환경을 구축할지에 대해 학습 했었습니다. 결국 레플리케이션을 도입해야겠다는 결론을 내리게되었고, 지금부터 그 구축 환경에 대해 다루어보고자 합니다. 또한 어떻게 스프링부트 애플리케이션에서 DataSource 로 분기처리를 진행했는지는 곧 바로 다음 포스팅에서 다루어보고자 합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;레플리케이션에 대한 자세한 설명은 &lt;a href=&quot;https://velog.io/@msung99/MySQL-%EC%9D%98-MasterSlave-%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98Replication-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%99%80-%ED%86%A0%ED%8F%B4%EB%A1%9C%EC%A7%80-%EA%B5%AC%EC%84%B1-%EB%B0%A9%EC%8B%9D&quot;&gt;MySQL 의 Master/Slave 레플리케이션(Replication) 아키텍처와 토폴로지 구성 방식&lt;/a&gt; 에서 다루었으므로 생략한다. 이번에는 오로지 어떻게 구현했는지에 대해서만 다루겠다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;상세 도입배경 및 WAS 서버 구축은 곧 바로 이어질 다음 포스팅에서 다루겠다. 이번에는 MySQL 레플리케이션 환경 구축 그 자체에 대해서만 다룬다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;mysql-환경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mysql-%ED%99%98%EA%B2%BD&quot; aria-label=&quot;mysql 환경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MySQL 환경&lt;/h2&gt;
&lt;p&gt;전반적인 개발 환경은 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Spring Data JPA&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;인프라에 EC2 로 MySQL 서버를 2대 띄웠다.&lt;/li&gt;
&lt;li&gt;Ubuntu 22.04 LTS 를 기준으로 진행했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;mysql-설치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mysql-%EC%84%A4%EC%B9%98&quot; aria-label=&quot;mysql 설치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MySQL 설치&lt;/h3&gt;
&lt;p&gt;우선 Ubuntu 서버에 아래와 같이 MySQL 을 설치해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;$ apt &lt;span class=&quot;token keyword&quot;&gt;update&lt;/span&gt;
$ apt install mysql&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;server&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;외부-접속허용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%B8%EB%B6%80-%EC%A0%91%EC%86%8D%ED%97%88%EC%9A%A9&quot; aria-label=&quot;외부 접속허용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;외부 접속허용&lt;/h3&gt;
&lt;p&gt;MySQL 은 기본적으로 localhost 에서만 접속이 가능한데, 당연히 외부에서도 접속이 가능해야 할것입니다. Master, Slave 서버 모두 외부에서 접속이 가능하도록 변경해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;vi &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysql&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;d&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysqld&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cnf

&lt;span class=&quot;token comment&quot;&gt;# bind-address = 127.0.0.1&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# mysqlx-bind-address = 127.0.0.1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;master-서버&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#master-%EC%84%9C%EB%B2%84&quot; aria-label=&quot;master 서버 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Master 서버&lt;/h2&gt;
&lt;p&gt;우선 마스터 서버부터 작업을 시작해봅시다. &quot;replication&quot; 이라는 데이터베이스를 MySQL 안에 하나 생성해주고, 해당 데이터베이스의 작업내용들이 Slave 서버에 복제되도록 할 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;$ sudo mysql &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;u root &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p    &lt;span class=&quot;token comment&quot;&gt;// 또는 &quot;sudo mysql&quot;&lt;/span&gt;
$ &lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;database&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;replication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;replication&quot; 데이베이스 생성&lt;/span&gt;
$ &lt;span class=&quot;token keyword&quot;&gt;show&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;databases&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;replication&quot; 데이터베이스 잘 생성되었는지 확인&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또한, 마스터 서버에서 변경사항(이벤트) 가 발행했을때 슬레이브 서버가 마스터 서버에 접근하여 &lt;code class=&quot;language-text&quot;&gt;바이너리 로그 파일&lt;/code&gt; 의 변경사항을 읽어와야 하는데, 이렇게 마스터 서버에 접근하고 복제를 가능케하는 &quot;레플리케이션 전용 계정&quot; 을 하나 생성해줘야 합니다. 즉, 이 계정은 슬레이브 서버에서 마스터 서버에 접근할 때 사용합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;$ &lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;USER&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;replication&apos;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@&apos;%&apos;&lt;/span&gt; IDENTIFIED &lt;span class=&quot;token keyword&quot;&gt;WITH&lt;/span&gt; mysql_native_password &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;password&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
$ &lt;span class=&quot;token keyword&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;REPLICATION&lt;/span&gt; SLAVE &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;replication&apos;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@&apos;%&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;master-기능-비활성화-상태에서-더미데이터-삽입&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#master-%EA%B8%B0%EB%8A%A5-%EB%B9%84%ED%99%9C%EC%84%B1%ED%99%94-%EC%83%81%ED%83%9C%EC%97%90%EC%84%9C-%EB%8D%94%EB%AF%B8%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%82%BD%EC%9E%85&quot; aria-label=&quot;master 기능 비활성화 상태에서 더미데이터 삽입 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Master 기능 비활성화 상태에서 더미데이터 삽입&lt;/h3&gt;
&lt;p&gt;생성한 &quot;replication&quot; 데이터베이스에 접속하여, &quot;user&quot; 라는 테이블을 하나 생성해주고 그 안에 더미데이터를 넣어줍시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4246b912-688e-437c-8ac9-bdd5deb566f2/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이전에 설명했듯이, 모든 변경사항(이벤트) 는 마스터 서버의 &lt;code class=&quot;language-text&quot;&gt;바이너리 로그 파일&lt;/code&gt; 에 기록됩니다. 마스터 서버의 &lt;code class=&quot;language-text&quot;&gt;바이너리 로깅&lt;/code&gt; 기능이 활성화되고나서 모든 이벤트는 파일에 기록된다는 것입니다. 즉, 아래와 같이 user 테이블을 생성하고 더미데이터를 넣는다고 한들, 아직 바이너리 로깅이 비활성화 상태이기 때문에 (아직 애초에 마스터 서버로 활성화된 상태가 아니기 때문에) 파일에는 아직 아무런 이벤트가 기록되지 않을겁니다.&lt;/p&gt;
&lt;p&gt;왜 이런 과정을 넣었냐면, 실제로 운영중인 서비스의 DB 에 대해 레플리케이션을 도입하려는 상황을 가정한것입니다. Master 서버 기능을 활성화 한다면 기존 데이터들은 새롭게 발생한 &quot;변경사항&quot; 이 아니므로 바이너리 로그 파일에 기록된 상태가 아닐것이고, 결국 레플리케이션 대상에서 제외될 것입니다. 이런 상황에서 기존 DB 의 데이터들을 어떻게 복제할지가 관건인데, 이는 &lt;code class=&quot;language-text&quot;&gt;&quot;dump(백업)&quot;&lt;/code&gt; 을 통해 해결 가능합니다. 이에 대한건 아래에서 자세히 다루겠습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;$ &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;replication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
	id &lt;span class=&quot;token keyword&quot;&gt;BIGINT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	name &lt;span class=&quot;token keyword&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;insert&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;into&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;name1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;insert&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;into&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;name2&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;insert&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;into&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;name3&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;mysqldcnf&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mysqldcnf&quot; aria-label=&quot;mysqldcnf permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;mysqld.cnf&lt;/h3&gt;
&lt;p&gt;앞서 수정했던 &lt;code class=&quot;language-text&quot;&gt;mysqld.cnf&lt;/code&gt; 를 다시 열고, 그 안에 아래와 같이 작성해줍시다. 마스터 서버의 복제가 발생할때 어떻게 어떤 방식으로 처리할지에 대한 옵션을 부여한다고 보면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;$ vi &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysql&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;d&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysqld&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cnf

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;mysqld&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
max_allowed_packet&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;M
server&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
log&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;bin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mysql&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;bin
binlog_format &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ROW&lt;/span&gt;
max_binlog_size &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;M
sync_binlog &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
expire&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;logs&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;days &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;
binlog_do_db &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;replication&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;[mysqld]&lt;/code&gt; : MySQL 서버의 전역설정 정보를 나타내는 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;max_allowed_packet&lt;/code&gt; : 클라이언트와 서버 간에 교환되는 최대 네트워크 패킷 크기로, 여기서는 패킷의 최대 크기가 1000MB 로 설정된 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;server-id&lt;/code&gt; : 레플리케이션을 위해 서버에 할당된 고유한 식별자입니다. 이전에 다루었듯이, 레플리케이션 토폴리지 내의 서버는 각각 고유한 서버 ID 값을 가져아하므로, 이 값은슬레이브 서버와 달라야합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;log-bin&lt;/code&gt; : 바이너리 로그 파일의 위치(경로)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;binlog_format&lt;/code&gt; : 바이너리 로그 파일에 저장되는 데이터의 저장 형식을 지정하는 것입니다. &lt;code class=&quot;language-text&quot;&gt;STATEMENT&lt;/code&gt; , &lt;code class=&quot;language-text&quot;&gt;ROW&lt;/code&gt; , &lt;code class=&quot;language-text&quot;&gt;MIXED&lt;/code&gt; 이 3가지 중 하나를 선택할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;max_binlog_size&lt;/code&gt; : 바이너리 로그 파일의 최대 크기로, 지정된 크기에 도달하면 새로운파일이 생성됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;sync_binlog&lt;/code&gt; : 바이너리 로그 파일이 디스크에 동기적으로 기록되도록 지정하는 것으로, 1은 가장 안정적으로 기록되지만, 가장 느린 설정입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;expire-logs-days&lt;/code&gt; : 바이너리 로그 파일의 만료기간으로, 위처럼 7일을 설정하면 이 이후로는 파일이 삭제됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;binlog_do_db&lt;/code&gt; : 레플리케이션을 적용할 데이터베이스의 이름을 지정하는 것으로, 앞서 만든 &quot;replication&quot; 데이터베이스의 이름을 지정해줬습니다. 별도의 설정이 없다면, 모든 데이터베이스를 대상으로 복제가 진행됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;master-서버-상태값-조회&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#master-%EC%84%9C%EB%B2%84-%EC%83%81%ED%83%9C%EA%B0%92-%EC%A1%B0%ED%9A%8C&quot; aria-label=&quot;master 서버 상태값 조회 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Master 서버 상태값 조회&lt;/h3&gt;
&lt;p&gt;모든 작성이 끝났다면, MySQL 서버를 다시 재가동해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ service mysql restart&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러고 해당 Master 서버의 상태를 조회해봅시다. 레플리케이션을 실행시 &lt;code class=&quot;language-text&quot;&gt;File&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;Position&lt;/code&gt; 값을 기반으로 동기화 진행되니, 이 값을 기억해둡시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;mysql&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;SHOW&lt;/span&gt; MASTER &lt;span class=&quot;token keyword&quot;&gt;STATUS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;------------------+----------+--------------+------------------+-------------------+&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;File&lt;/span&gt;             &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Position &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Binlog_Do_DB &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Binlog_Ignore_DB &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Executed_Gtid_Set &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;------------------+----------+--------------+------------------+-------------------+&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; mysql&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;bin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;000001&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;      &lt;span class=&quot;token number&quot;&gt;157&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;replication&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;                  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;                   &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;------------------+----------+--------------+------------------+---------------&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;position&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#position&quot; aria-label=&quot;position permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Position&lt;/h3&gt;
&lt;p&gt;이때 &lt;code class=&quot;language-text&quot;&gt;Postition&lt;/code&gt; 값을 무엇을 의미할까요?&lt;/p&gt;
&lt;p&gt;이전에 다루었듯이, 마스터 서버는 &lt;code class=&quot;language-text&quot;&gt;Binary Logging&lt;/code&gt; 이 활성화되면 마스터의 모든 데이터 구문이 &lt;code class=&quot;language-text&quot;&gt;바이너리 로그 파일&lt;/code&gt; 에 저장되며, 이 로그 내용들을 슬레이브 서버가 읽어오는 방식으로 복제가 된다고 했었습니다. 따라서 slave는 log 파일내의 position을 유지할 필요가 있습니다. 그래야 로그파일 전체를 처음부터 읽지 않고 효과적으로 로그 파일을 운영할 수 있기 때문입니다. 여기서 &lt;strong&gt;position 은 바이너리 로그 파일 내의 위치를 의미하며, 어느 부분부터 읽겠다는 것을 의미합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이어서 슬레이브 서버를 셋팅할때, 이 Position 값을 부여해주면 Slave 서버는 해당 위치값에서 부터 계속 변경 위치값을 계속 추적해가면서 최근에 변경사항이 기록된 필요한 위치에서부터 레플리케이션을 수행하는 방식입니다. 그래야 변경사항이 생긴 부분만 읽을것이며, 불필요하게 바이너리 로그 파일의 맨 처음부터 읽을 필요없이 변경사항의 기록된 필요한 부분만 읽어오면 되기 때문입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;slave-서버&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#slave-%EC%84%9C%EB%B2%84&quot; aria-label=&quot;slave 서버 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Slave 서버&lt;/h2&gt;
&lt;p&gt;마스터 서버와 마찬가지로 &quot;replication&quot; 이라는 데이터베이스를 생성해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;$ &lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DATABASE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;replication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// 슬레이브 서버도 동일한 명으로 DB 를 생성해준다&lt;/span&gt;
$ &lt;span class=&quot;token keyword&quot;&gt;show&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;databases&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;                &lt;span class=&quot;token comment&quot;&gt;// 잘 생성되었는지 확인&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;백업파일-생성-및-동기화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B1%EC%97%85%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EB%8F%99%EA%B8%B0%ED%99%94&quot; aria-label=&quot;백업파일 생성 및 동기화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;백업파일 생성 및 동기화&lt;/h3&gt;
&lt;p&gt;앞서 언급했듯이, 마스터와 슬레이브 서버간의 &lt;code class=&quot;language-text&quot;&gt;정합성 문제&lt;/code&gt; 가 발생할 수 있으므로 &quot;백업&quot; 을 진행해야 한다고 했었습니다. 아래와 같이 진행해줍시다.&lt;/p&gt;
&lt;p&gt;우선 마스터 서버에서 백업 파일을 생성해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 1. 마스터 서버에서의 작업내용&lt;/span&gt;
mysql&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; FLUSH &lt;span class=&quot;token keyword&quot;&gt;TABLES&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;READ&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;LOCK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// 테이블 락&lt;/span&gt;
mysql&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;exit&lt;/span&gt;

mysqldump &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;u root &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;B &lt;span class=&quot;token keyword&quot;&gt;replication&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;backup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;sql&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 마스터 서버의 &quot;replcation&quot; 데이터베이스에 기반한 백업 파일생성&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;UNLOCK&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLES&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 테이블 락 해제&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러고 &lt;code class=&quot;language-text&quot;&gt;scp&lt;/code&gt; 와 같은 다양한 방법으로 마스터 서버의 백업 파일을 슬레이브 서버에 전송해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 2. scp 전송&lt;/span&gt;
scp dump_file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;sql&lt;/span&gt; root@&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;슬레이브 서버 IP 또는 호스트명&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;:&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;home&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ubuntu&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;dump_file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;sql&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;마지막으로 전달받은 백업 파일을 슬레이브 서버의 &quot;replication&quot; 데이터베이스에다 내용을 복원해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 3. 슬레이브 서버에서의 작업내용&lt;/span&gt;
mysql &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;u &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token keyword&quot;&gt;replication&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; dump_file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;sql&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 진행했다면, 마스터 서버의 &quot;replication&quot; DB 의 내용이 슬레이브 서버에서 새롭게 생성한 &quot;replication&quot; DB 에 동기화 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;mysqldcnf-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mysqldcnf-1&quot; aria-label=&quot;mysqldcnf 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;mysqld.cnf&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;mysqld.cnf&lt;/code&gt; 에 접속하여 마스터 서버와 마찬가지로 속성을 부여해줍시다. 동일한 레플리케이션 토폴로지 내에서는 각 서버가 유일한 id 값을 지녀야한다고 했기 때문에 &lt;code class=&quot;language-text&quot;&gt;server-id = 2&lt;/code&gt; 로 별도의 값을 부여했습니다. 또한 슬레이브 서버를 읽기전용 으로 구축하기 위해 &lt;code class=&quot;language-text&quot;&gt;read_only=1&lt;/code&gt; 을 부여했습니다. 추후에 다룰 포스팅에서 자세히 설명하겠지만, &lt;code class=&quot;language-text&quot;&gt;@Transactional(readOnly = true)&lt;/code&gt; 가 부여된 API, 즉 읽기전용 API 에 대한 요청들은 슬레이브 서버로 유입되게 할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;replicate-do-db&lt;/code&gt; 로 마스터 서버의 어떤 데이버베이스를 복제할 것인지 지정할 수 있는데, 앞서 마스터 서버에서 생성한 &quot;replication&quot; 데이터베이스를 지정했습니다. 결과적으로 방금전에 슬레이브 서버에서 만든 replication 데이터베이스에 원활히 복제가 수행됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;$ vi &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysql&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;d&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mysqld&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cnf

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;mysqld&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
server&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
replicate&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;db &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;replication&lt;/span&gt;
read_only &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;설정이 끝났다면 재실행시켜줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ service mysql restart&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;master-서버-접속설정-및-slave-기능레플리케이션-활성화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#master-%EC%84%9C%EB%B2%84-%EC%A0%91%EC%86%8D%EC%84%A4%EC%A0%95-%EB%B0%8F-slave-%EA%B8%B0%EB%8A%A5%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%ED%99%9C%EC%84%B1%ED%99%94&quot; aria-label=&quot;master 서버 접속설정 및 slave 기능레플리케이션 활성화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Master 서버 접속설정 및 Slave 기능(레플리케이션) 활성화&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;MASTER_USER&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;MASTER_PASSWORD&lt;/code&gt; 에는 앞서 생성한 레플리케이션 전용 계정에 대한 정보를 넣어줍시다. 또 &lt;code class=&quot;language-text&quot;&gt;MASTER_LOG_FILE&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;MASTER_LOG_POS&lt;/code&gt; 에는 앞서 기억해둔 마스터 서버의 상태값들로 설정해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;RESET SLAVE&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

CHANGE MASTER &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt;
MASTER_HOST&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;xx.xxx.xxx&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 마스터 서버 IP 주소&lt;/span&gt;
MASTER_USER&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;replication&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 레플리케이션 전용 계정 기입&lt;/span&gt;
MASTER_PASSWORD&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;password&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
MASTER_LOG_FILE&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;mysql-bin.000001&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
MASTER_LOG_POS&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;157&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;START&lt;/span&gt; REPLICA&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;레플리케이션-동작-확인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EB%8F%99%EC%9E%91-%ED%99%95%EC%9D%B8&quot; aria-label=&quot;레플리케이션 동작 확인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레플리케이션 동작 확인&lt;/h3&gt;
&lt;p&gt;슬레이브 서버에서 레플리케이션이 잘 동작하는지 조회해봅시다. &lt;code class=&quot;language-text&quot;&gt;Slave_IO_Running&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;Slave_SQL_Running&lt;/code&gt; 이 둘다 &quot;Yes&quot; 가 되어야 정상 동작하는 것이며, 만약 하나라도 &quot;No&quot; 인 경우라면 에러 로그를 조회해보고 적절히 처리해줘야합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SHOW&lt;/span&gt; SLAVE &lt;span class=&quot;token keyword&quot;&gt;STATUS&lt;/span&gt;\G&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 아래 명령어는 레플리케이션이 동작하지 않을때만 활용하면 됩니다.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// STOP REPLICA;  // =&gt; 에러를 해결하고나서 &quot;RESET SLAVE;&quot; 부터 다시 활성화시킬때, 레플리케이션을 먼저 종료해야한다.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;실제로 조회했을때, 아래와 같이 YES 가 보이면 레플리케이션이 정상 동작하는 것입니다.
&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/23c187f7-7272-4bdf-a24c-e88326364597/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;레플리케이션-직접-확인해보기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%A7%81%EC%A0%91-%ED%99%95%EC%9D%B8%ED%95%B4%EB%B3%B4%EA%B8%B0&quot; aria-label=&quot;레플리케이션 직접 확인해보기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레플리케이션 직접 확인해보기&lt;/h3&gt;
&lt;p&gt;이렇게까지 잘 설정했다면, 실제로 레플리케이션이 동작하는지 직접 확인해봅시다. 마스터 서버에서 &lt;code class=&quot;language-text&quot;&gt;insert&lt;/code&gt; 로 새로운 데이터를 넣어도 좋고, &lt;code class=&quot;language-text&quot;&gt;create table&lt;/code&gt; 로 새로운 스키마를 생성해도 좋습니다. 어떤 내용이던 정상 반영될것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;결론&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EB%A1%A0&quot; aria-label=&quot;결론 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결론&lt;/h2&gt;
&lt;p&gt;이렇게 MySQL Replication 을 활용하여 Master-Slave 방식으로 분산 환경을 구축해봤습니다. 내용이 꽤 어려워서 꽤나 애먹고 삽집을 많이 했던것같네요 😓
다음 포스팅에서는 스프링부트 애플리케이션에서 DataSource 빈 등록을 통해, 현재 구축한 레플리케이션 환경과 연동하여 실제 API 서버를 구축해 볼 예정입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.naver.com/ggaibi1004/10163013682&quot;&gt;https://blog.naver.com/ggaibi1004/10163013682&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://server-engineer.tistory.com/252&quot;&gt;https://server-engineer.tistory.com/252&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://trandent.com/article/etc/detail/320833&quot;&gt;https://trandent.com/article/etc/detail/320833&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cheese10yun.github.io/spring-transaction/&quot;&gt;https://cheese10yun.github.io/spring-transaction/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[데이터베이스 클러스터링과 샤딩을 통한 고가용성과 스케일아웃]]></title><description><![CDATA[학습배경 MySQL 의 Master/Slave 레플리케이션(Replication) 아키텍처와 토폴로지 구성 방식 에서도 다루었듯이, 데이터베이스 레플리케이션(Replication…]]></description><link>https://haon.site/haon/mysql/scale-out/</link><guid isPermaLink="false">https://haon.site/haon/mysql/scale-out/</guid><pubDate>Wed, 05 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/MySQL-%EC%9D%98-MasterSlave-%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98Replication-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%99%80-%ED%86%A0%ED%8F%B4%EB%A1%9C%EC%A7%80-%EA%B5%AC%EC%84%B1-%EB%B0%A9%EC%8B%9D&quot;&gt;MySQL 의 Master/Slave 레플리케이션(Replication) 아키텍처와 토폴로지 구성 방식&lt;/a&gt; 에서도 다루었듯이, 데이터베이스 레플리케이션(Replication) 에 대해 학습한 바가 있습니다. 레플리케이션과 함께 자주 언급되는 개념에는 &lt;code class=&quot;language-text&quot;&gt;클러스터링&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;파티셔닝&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;샤딩&lt;/code&gt; 등의 다양한 기법이 존재하는 것으로 보입니다. 이번에는 클러스터링과 샤딩에 대해 호기심이 생겨서 학습해보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;데이터베이스-클러스터링clustering&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81clustering&quot; aria-label=&quot;데이터베이스 클러스터링clustering permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 클러스터링(Clustering)&lt;/h2&gt;
&lt;h3 id=&quot;데이터베이스-서버와-스토리지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%84%9C%EB%B2%84%EC%99%80-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80&quot; aria-label=&quot;데이터베이스 서버와 스토리지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 서버와 스토리지&lt;/h3&gt;
&lt;p&gt;사전에 알아야 할점이 있습니다. 바로 &lt;code class=&quot;language-text&quot;&gt;데이터베이스 저장소(스토리지)&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;데이터베이스 서버&lt;/code&gt; 는 다른 개념이라는 것입니다. 데이터베이스 서버는 SQL 을 실행하고, 스토리지는 실제로 데이터를 보존하는 저장소입니다. 가령 MySQL 을 서비스에서 사용하고 있다면, MySQL 이라는 것은 데이터베이스 서버이며, 실제로 데이터가 저장되는 곳은 따로 저장소가 존재합니다. 즉, MySQL 서버는 스토리지에 쿼리문을 날려서 저장소의 데이터에 대해 읽기-쓰기 작업을 수행하는 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;정합성-이슈-해결&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%ED%95%A9%EC%84%B1-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0&quot; aria-label=&quot;정합성 이슈 해결 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정합성 이슈 해결&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/63c781cf-2813-40d0-9b10-b63ce04a662a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;데이터베이스 스토리지를 다중화할 경우 &lt;code class=&quot;language-text&quot;&gt;정합성&lt;/code&gt; 문제는 절대 무시할 수 없을겁니다. 여러 저장소로 확장했을때 어떤 데이터가 어디에는 저장되고 어떤곳에는 저장이 안되는 현상이 발생한다면 서비스에 장애가 발생할 수 있기 때문에, 이를 사전에 방지하고 해결하는 것은 꽤나 번거롭습니다.&lt;/p&gt;
&lt;p&gt;이 때문에 등장한것이 데이터베이스 &lt;code class=&quot;language-text&quot;&gt;클러스터링(Clustering)&lt;/code&gt; 입니다. 간단히 데이터베이스 스토리지는 한대만 두고, 여러 데이터베이스 서버가 공유하는 방식입니다. &lt;strong&gt;모든 DB 서버가 하나의 저장소를 공유하기 때문에 정합성 문제를 걱정하지 않아도됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;장점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90&quot; aria-label=&quot;장점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h3&gt;
&lt;p&gt;특징을 정리해봅시다. 우선 장점으로는 DB 서버중 한대에 장애가 발생하더라도, 다른 서버가 역할을 대신할 수 있기 때문에 서비스가 중단되는 현상을 방지할 수 있습니다. 또한 스토리지 하나만 배치하고 서버 &quot;여러대&quot; 로 운영하기 때문에, 원활한 &lt;code class=&quot;language-text&quot;&gt;부하 분산&lt;/code&gt; 이 가능해집니다.&lt;/p&gt;
&lt;h3 id=&quot;단점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90&quot; aria-label=&quot;단점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;p&gt;하지만 단점은, 여러 DB 서버가 하나의 저장소를 공유하기 때문에 &lt;code class=&quot;language-text&quot;&gt;병목현상&lt;/code&gt;이 발생할 수 있습니다. 클러스터 서버를 여럿 늘리면 서버 개수에 정비례해서 처리 성능이 증가할 것 같지만, 막상 데이터베이스 저장소는 하나이기 때문에 저장소에서 병목이 발생할 수 있는것입니다. 이는 곧 성능저하의 원인이 될것입니다. 또한 여러 서버를 돌리다보면 &lt;code class=&quot;language-text&quot;&gt;비용 문제&lt;/code&gt; 도 무시못합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;클러스터링-종류&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EC%A2%85%EB%A5%98&quot; aria-label=&quot;클러스터링 종류 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클러스터링 종류&lt;/h2&gt;
&lt;h3 id=&quot;active---active&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#active---active&quot; aria-label=&quot;active   active permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Active - Active&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0e183582-77de-4507-bf6b-da631c54ee7f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;클러스터 내부의 &lt;strong&gt;모든 DB 서버를&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;Active(활성화)&lt;/code&gt; &lt;strong&gt;상태로 만들어서, 모든 DB 서버가 실제로 동작하는 상태로 만드는 것&lt;/strong&gt; 입니다. 이렇게 되면 한 서버에 장애가 발생해도 나머지 서버가 작업을 그대로 진행할 수 있게되서 &lt;code class=&quot;language-text&quot;&gt;다운타임&lt;/code&gt; 이 발생하지 않습니다. 또한 부하가 골구로 분산되므로 성능 측면에서도 기대해볼 수 있을겁니다.&lt;/p&gt;
&lt;p&gt;하지만 여러 데이터베이스 서버가 하나의 스토리지를 공유하기 때문에, 저장소에서 &lt;code class=&quot;language-text&quot;&gt;병목 현상&lt;/code&gt; 이 발생하고 성능 저하의 원인이 될 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;active---standby&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#active---standby&quot; aria-label=&quot;active   standby permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Active - Standby&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0b20a32c-dafc-440c-b8fe-438282d53414/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Active - Standby 방식은 일부 서버는 &lt;code class=&quot;language-text&quot;&gt;Active(활성화)&lt;/code&gt; 상태로 두고, 나머지 일부 서버들은 &lt;code class=&quot;language-text&quot;&gt;Standby(대기상태)&lt;/code&gt; 로 두는 방식입니다. Standby 서버는 평소에는 일을하지 않고, Active 서버에 장애가 발생했을때 본인이 Active 상태로 전환되어서 일을 대신하도록 주기적으로 기존 Active 서버를 &lt;code class=&quot;language-text&quot;&gt;모니터링&lt;/code&gt; 하는 방식으로 동작합니다.&lt;/p&gt;
&lt;p&gt;장점은, Active-Active 구성 방식보다는 비용 절감이 될것입니다. 하지만 Standby 서버가 Active 서버로 전환되아야 할때 시간이 좀 오래걸려서&lt;code class=&quot;language-text&quot;&gt;다운타임이 발생&lt;/code&gt; 하게 되고, 결국 가용성이 떨어진다는 단점이 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;데이터베이스-샤딩sharding&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%83%A4%EB%94%A9sharding&quot; aria-label=&quot;데이터베이스 샤딩sharding permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 샤딩(Sharding)&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/b872f0d6-2a0d-4d0e-aea5-fce9388ca2e7/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;데이터가 쌓이고 개수가 많아지면 연산이 느려질겁니다. 어떻게 더 빠른 연산을 제공할지에 대한 고민에서 탄생한 방식이 샤딩(Sharding) 으로, &quot;테이블을 나눠서 빠르게 연산을 진행하는 방식&quot; 입니다. 정리하면, 샤딩은 동일한 스키마(테이블) 을 가지고 있는 데이터들에 대해 여러대의 데이터베이스 서버에 여러 작은 단위로 끊어서 분산 저장하는 기법입니다. 이때 작은 단위를 &lt;code class=&quot;language-text&quot;&gt;샤드(Shared)&lt;/code&gt; 라고 합니다.&lt;/p&gt;
&lt;h3 id=&quot;샤드-키shared-key&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%A4%EB%93%9C-%ED%82%A4shared-key&quot; aria-label=&quot;샤드 키shared key permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;샤드 키(Shared Key)&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;샤드 키(Shared Key)&lt;/code&gt; 란 분할된 여러 샤드중 어떤 샤드를 선택할지 결정할때 사용되는 Key 값입니다. 또한 이 샤드키를 어떤 방식으로 생성할지에 따라서 샤딩의 방법이 나뉩니다. 샤딩의 방법으론 &lt;code class=&quot;language-text&quot;&gt;해시 샤딩(Hash Sharding)&lt;/code&gt; , &lt;code class=&quot;language-text&quot;&gt;동적 샤딩(Dynamic Sharding)&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;엔티티 그룹(Entity Group)&lt;/code&gt; 이 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;hash-sharding&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hash-sharding&quot; aria-label=&quot;hash sharding permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Hash Sharding&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/8de91ceb-2799-4f5d-9893-32e18d81946b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;해시 샤딩은 &lt;code class=&quot;language-text&quot;&gt;해시함수&lt;/code&gt; 를 활용한 방식으로, PK 값을 &lt;code class=&quot;language-text&quot;&gt;나머지 연산&lt;/code&gt; 을 진행한 결과값으로 어떤 샤드에 들어갈지 결정하는 방식입니다. 이 방식은 구현자체가 샤드의 수만큼 해싱을 하면 되기 때문에 간단합니다.&lt;/p&gt;
&lt;p&gt;대신에 &lt;strong&gt;샤드가 늘어나면 해시함수 자체가 달라져야 하기 때문에,&lt;/strong&gt; 기존에 저장되면 데이터들에 대한 정합성이 깨지게됩니다. 따라서 확장성이 안좋다는 단점이 있습니다. 또한 단순히 key 값을 기준으로해서 해시 데이터를 나누기 때문에, **공간에 대한 효율성은 고려하지않고 단순 해시로만 처리한다는 단점이 있습니다. **&lt;/p&gt;
&lt;h3 id=&quot;range-sharding-dynamic-sharding&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#range-sharding-dynamic-sharding&quot; aria-label=&quot;range sharding dynamic sharding permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Range Sharding (Dynamic Sharding)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/62367131-da64-49b2-af59-fccd1932fb9c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;다이나믹 샤딩(Dynamic Sharding)&lt;/code&gt; 이라고도 불리는 &lt;code class=&quot;language-text&quot;&gt;레인지 샤딩(Range Sharding)&lt;/code&gt; 은 확장성을 해결한 방식입니다. &lt;strong&gt;PK 값을 범위로 지정하여 샤드를 결정하는 방식이죠.&lt;/strong&gt; 예를들어 PK 가 1&lt;del&gt;100번인 데이터에 대해서는 1번 샤드에, 101&lt;/del&gt;200번 데이터에 대해서는 2번 샤드에 저장하는 방식입니다.&lt;/p&gt;
&lt;p&gt;대신 단점은, 특정 샤드에만 트래픽이 몰려서 &lt;code class=&quot;language-text&quot;&gt;부하 문제&lt;/code&gt; 가 충분히 발생할 수 있습니다. 예를들어 &quot;최근 게시물&quot; 조회 API 가 있고, 이 API 를 메인으로 사용하는 서비스가 있다고 해봅시다. 최근에 작성된 게시물은 마지막 샤딩에만 위치하게 될텐데, 최근 게시물들을 조회하기 위해 여러 트래픽에 마지막 샤딩에만 몰리게 될 것입니다. 결국 특정 샤딩에만 트래픽이 몰릴 가능성이 높고, 다른 샤딩에는 비교적 유입될 트래픽이 적게되는 현상이 발생합니다.&lt;/p&gt;
&lt;p&gt;따라서 특정 상황에서는 레인지 샤딩보다 해시 샤딩이 더 좋을수도 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;entity-group&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#entity-group&quot; aria-label=&quot;entity group permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Entity Group&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0deb7e94-0080-43ab-b1b3-3ace5b472ae0/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;앞선 방식들이 key-value 형식의 &lt;code class=&quot;language-text&quot;&gt;NoSQL&lt;/code&gt; 에 더 작합한 샤딩 방식이라면, 이 방식은 &lt;code class=&quot;language-text&quot;&gt;RDBMS&lt;/code&gt; 에서 더 유리한 방식입니다. &lt;strong&gt;관계가 매핑되어 있는 엔티티끼리를 같은 샤드내에 위치하도록 만든 방식입니다.&lt;/strong&gt; 그래서 유저가 작성한 게시글이나 댓글등을 같은 샤드 내에서 가지고 있도록 작성한 방식입니다.&lt;/p&gt;
&lt;p&gt;즉, 이 방식은 단일 샤드 내에서 강한 응집도를 가지고, 동일한 그룹에 대한 SQL 문에 대해선 매우 빠른 응답속도를 받을 수 있을겁니다. 하지만 다른 그룹에 대한 SQL 문에 대해선 매우 비효율적인 방식이 되겠죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;데이터베이스 파티셔닝&lt;/li&gt;
&lt;li&gt;스프링부트에서 레플리케이션 구현방법&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=y42TXZKFfqQ&amp;#x26;t=333s&quot;&gt;https://www.youtube.com/watch?v=y42TXZKFfqQ&amp;#x26;t=333s&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://betterprogramming.pub/an-introduction-to-database-sharding-b6abde73d04f&quot;&gt;https://betterprogramming.pub/an-introduction-to-database-sharding-b6abde73d04f&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://eminentstar.tistory.com/54&quot;&gt;http://eminentstar.tistory.com/54&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL 레플리케이션 토폴로지 구성 방식]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/mysql/topology/</link><guid isPermaLink="false">https://haon.site/haon/mysql/topology/</guid><pubDate>Wed, 05 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습-및-도입배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5-%EB%B0%8F-%EB%8F%84%EC%9E%85%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습 및 도입배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습 및 도입배경&lt;/h2&gt;
&lt;p&gt;현재 운영중인 서비스가 아직 데이터베이스 구축하기 전 단계에 있습니다. 최근들어 신규 유입 유저가 &lt;code class=&quot;language-text&quot;&gt;약 5000명&lt;/code&gt; 가량이 늘었는데, 리전이 대한민국 외에도 중국, 미국등 다양한 국가의 유저들이 유입되었습니다. 데이터베이스 서버의 부하를 줄이는 방법으로는 인덱스(Index) 를 비롯한 각종 개선요소가 있겠지만, 그 중 하나로 데이터베이스 레플리케이션(Replication) 을 떠올리게 되었고, 서비스에 도입해보고자 이렇게 학습을 진행합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;레플리케이션-replication&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-replication&quot; aria-label=&quot;레플리케이션 replication permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레플리케이션 (Replication)&lt;/h2&gt;
&lt;p&gt;데이터베이스의 레플리케이션 (replication) 이란 말그래도 &quot;복재&quot; 하는 해위를 의미합니다. 가용성과 확장성 측면에서는 빠질 수 없는 요소라고 볼 수 있습니다. 트래픽이 증가했을때 인프라에서 수직확장(Scale Up) 만으로는 한계가 있으므로 &lt;code class=&quot;language-text&quot;&gt;로드밸런싱&lt;/code&gt; 환경으로 &lt;code class=&quot;language-text&quot;&gt;수평확장(Scale Out)&lt;/code&gt; 환경을 구축해야 하듯이, 부하를 감당하고 가용성과 확장성을 위해선 데이터베이스도 복제가 반드시 필요합니다.&lt;/p&gt;
&lt;h3 id=&quot;source-replica-구조-masterslave-구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-replica-%EA%B5%AC%EC%A1%B0-masterslave-%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;source replica 구조 masterslave 구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;source-replica 구조 (Master/Slave 구조)&lt;/h3&gt;
&lt;p&gt;기본적으로 DB 레플레키이션 구조에서 원본 데이터를 가진 서버를 &lt;code class=&quot;language-text&quot;&gt;source(Master)&lt;/code&gt; 라고하며, 복제된 서버를 &lt;code class=&quot;language-text&quot;&gt;replica(Slave)&lt;/code&gt; 서버라고 합니다. source 서버에 문제가 발생하면 replica 서버를 source 서버로 전환하여 사용할 수도 있고, source 서버를 쓰기전용 DB 로, replica 서버를 읽기전용 DB 로 분리해서 부하를 해결하는등의 다양한 전략으로 구현 가능합니다.&lt;/p&gt;
&lt;p&gt;참고로 &lt;code class=&quot;language-text&quot;&gt;마스터(Master)&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;슬래이브(Slave)&lt;/code&gt; 라는 표현으로도 사용되고 있으나, 최근에는 잘 사용되지는 않는 표현이라고 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;바이너리-로그-기반-복제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%94%EC%9D%B4%EB%84%88%EB%A6%AC-%EB%A1%9C%EA%B7%B8-%EA%B8%B0%EB%B0%98-%EB%B3%B5%EC%A0%9C&quot; aria-label=&quot;바이너리 로그 기반 복제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;바이너리 로그 기반 복제&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/880e2a81-8869-4ad4-862c-be42aed07eba/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;MySQL 의 복제 방식은 &lt;code class=&quot;language-text&quot;&gt;바이너리 로그 기반 복제&lt;/code&gt; 방식으로 이루어집니다. WAS 서버에서 source 서버로 보낸 변경사항(이벤트) 들은 바이너리 로그 파일에 담기게 되는데, 소스(Source) 서버에 변경사항이 일어나서 바이너리 로그가 기록이 되면 그 이벤트는 레플리카(Replica) 서버가 자신의 로컬 파일에 저장하는 방식입니다.&lt;/p&gt;
&lt;p&gt;더 자세히 설명해겠습니다. 우선 트랜잭션 처리 쓰레드를 제외했을떄, 복제 과정에는 3가지 종류의 쓰레드가 참여합니다. source 서버의 바이너리 로그에 이벤트 변경사항이 일어아면 &lt;code class=&quot;language-text&quot;&gt;바이너리 로그덤프 쓰레드&lt;/code&gt; 가 이 이벤트를 읽어서 레플리카 서버로 전송합니다. 레플리카 서버의 &lt;code class=&quot;language-text&quot;&gt;I/O 쓰레드&lt;/code&gt; 는 이 변경사항을 자신으 로컬 파일인 &lt;code class=&quot;language-text&quot;&gt;릴레이 로그&lt;/code&gt; 에 저장합니다. 아직까지는 변경사항이 레플리카 서버에는 반영되지 않은 상태인데, 이를 반영하기 위해 &lt;code class=&quot;language-text&quot;&gt;SQL 쓰레드&lt;/code&gt; 가 변경내용을 데이터 파일에 저장합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;글로벌-트랜잭션-아이디gtid-기반-복제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%80%EB%A1%9C%EB%B2%8C-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%95%84%EC%9D%B4%EB%94%94gtid-%EA%B8%B0%EB%B0%98-%EB%B3%B5%EC%A0%9C&quot; aria-label=&quot;글로벌 트랜잭션 아이디gtid 기반 복제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;글로벌 트랜잭션 아이디(GTID) 기반 복제&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7d2a8753-f2fd-49ab-9794-fc7516d5c01d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그렇다면 레플리카 서버는 서버의 바이너리 로그에 기록된 변경내역(이벤트) 들을 어떻게 식별해서 반영할지를 살펴보면, MySQL 5.0 이상부터는 &lt;code class=&quot;language-text&quot;&gt;글로벌 트랜잭션 아이디 기반 복제(GTID)&lt;/code&gt; 방식을 기본으로 채택하고 있습니다.&lt;/p&gt;
&lt;p&gt;모든 데이터베이스들은 고유한 식별값을 가지고 있습니다. 그 &quot;식별값&quot; 이란 바로 글로벌 트랜잭션 아이디라는 것인데, &lt;strong&gt;같은 레플리케이션에 참여한 모든 데이터베이스들의 &quot;식별자&quot; 값은 모두 동일합니다.&lt;/strong&gt; 이 값들이 모두 동일하기 때문에, 동일한 이벤트(변경사항) 에 대해서 같은 값을가진 DB 들만 읽어오면 같은 복제 그룹 대상들에게 변경사항을 동일하게 반영시킬 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;복제-데이터-포맷&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B3%B5%EC%A0%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%8F%AC%EB%A7%B7&quot; aria-label=&quot;복제 데이터 포맷 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;복제 데이터 포맷&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e85a7f3e-c4eb-4d22-bb22-9b9252bc59d5/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;앞서 언급했듯이, WAS 서버에서 보낸 데이터는 &lt;code class=&quot;language-text&quot;&gt;트랜잭션 처리 쓰레드&lt;/code&gt; 에 의해 바이너리 로그에 이벤트(변경사항) 이 저장됩니다. 이때 어떤 타입으로, 즉 어떤 바이너리 로그 타입의 이벤트가 바이너리 로그 파일에 담기게될까요? MySQL 5.7.7 이전까지는 실행된 SQL 문을 그대로 저장했으나, 이후로는 &lt;code class=&quot;language-text&quot;&gt;row 기반 바이너리 로그 타입&lt;/code&gt; 을 기본적으로 저장하고 있습니다. 이 방식은 변경값 자체가 바이너리 로그에 그대로 저장되는 방식입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;복제-토폴로지-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B3%B5%EC%A0%9C-%ED%86%A0%ED%8F%B4%EB%A1%9C%EC%A7%80-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;복제 토폴로지 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;복제 토폴로지 방식&lt;/h2&gt;
&lt;p&gt;그렇다면 소스 서버와 레플리카 서버를 어떻게 구성할 수 있을지에 대해서도 생각해봅시다.&lt;/p&gt;
&lt;h3 id=&quot;single-replica-싱글-레플리카&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#single-replica-%EC%8B%B1%EA%B8%80-%EB%A0%88%ED%94%8C%EB%A6%AC%EC%B9%B4&quot; aria-label=&quot;single replica 싱글 레플리카 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Single Replica (싱글 레플리카)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/422201c8-5f53-4cea-ab41-cd74aacd488e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;먼저 소스서버 1대와 레플리카 서버 1대를 두는 싱글 레플리카 방식이 있습니다. &lt;strong&gt;레플리카 서버는 예비서버 및 데이터 백업용으로&lt;/strong&gt; 활용됩니다.&lt;/p&gt;
&lt;h3 id=&quot;multi-replica-멀티-레플리카&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#multi-replica-%EB%A9%80%ED%8B%B0-%EB%A0%88%ED%94%8C%EB%A6%AC%EC%B9%B4&quot; aria-label=&quot;multi replica 멀티 레플리카 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Multi Replica (멀티 레플리카)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/cfabdec1-d72e-4d30-8ce1-88d05fc2af4e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;레플리카 서버를 여러대 배치한 방식인 멀티 레플리카 방식도 있습니다. 즉 소스서버 1대와 레플리카 여러대로 배치하는 방식이죠. 레플리카를 2대를 배치한다면, 한 서버는 부하 분산을 위한 용도로 사용하고, 다른 서버는 백업용을 위한 서버로 활용할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;chain-replication-체인-복제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#chain-replication-%EC%B2%B4%EC%9D%B8-%EB%B3%B5%EC%A0%9C&quot; aria-label=&quot;chain replication 체인 복제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Chain Replication (체인 복제)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a8f9e5e4-d6a6-4f8d-b0af-0fc23b5d21c2/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;소스서버에 연결된 레플리카 서버가 많은 경우, 소스 서버에 복제로 인한 부하가 커지게 됩니다. 이런 경우 다른 레플리카 서버를 소스 서버로 활용해서 복제 부하를 분산시키는 사용할 수도 있습니다. 이 방식은 MySQL 서버를 업데이트하거나 장비 교채 하는 시기에도 이런 구성방식을 활용할 수 있을겁니다.&lt;/p&gt;
&lt;h3 id=&quot;dual-source-replication-듀얼-소스-복제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dual-source-replication-%EB%93%80%EC%96%BC-%EC%86%8C%EC%8A%A4-%EB%B3%B5%EC%A0%9C&quot; aria-label=&quot;dual source replication 듀얼 소스 복제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dual Source Replication (듀얼 소스 복제)&lt;/h3&gt;
&lt;p&gt;또한 듀얼 소스 복제방식도 있는데, 두 서버가 모두 읽기와 쓰기가 모두 가능한 형태입니다. 두 서버 모두 서로 동일한 형태의 데이터를 가지고있고, 트랜잭션 충돌이 일어날 경우 롤백, 즉 복제 멈춤 현상이 일어나기 때문에 잘 사용되지 않는 토폴리지입니다.&lt;/p&gt;
&lt;h3 id=&quot;multi-source-replication-멀티-소스-복제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#multi-source-replication-%EB%A9%80%ED%8B%B0-%EC%86%8C%EC%8A%A4-%EB%B3%B5%EC%A0%9C&quot; aria-label=&quot;multi source replication 멀티 소스 복제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Multi Source Replication (멀티 소스 복제)&lt;/h3&gt;
&lt;p&gt;마지막으로 멀티 소스 복제 방식이 있습니다. 레플리카 서버 한대에 소스 서버가 여러대 연결된 형태로, 소스서버에 흩어져 있는 데이터들은 한대 모아서 데이터를 분석할 때 사용할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Real MySQL 8.0 (백은빈, 이성욱 지음)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=NPVJQz_YF2A&quot;&gt;https://www.youtube.com/watch?v=NPVJQz_YF2A&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[분산 환경에서 Redis Pub/Sub 메시지브로커를 활용한 로컬캐시 동기화]]></title><description><![CDATA[글로벌 캐싱(Global Caching) 의 한계 [Redis] RedisCacheManager 를 활용한 글로벌 캐싱(Global Caching) 에서 다루었듯이, 분산 환경에서 Redis…]]></description><link>https://haon.site/haon/redis/pub-sub/</link><guid isPermaLink="false">https://haon.site/haon/redis/pub-sub/</guid><pubDate>Sun, 18 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;글로벌-캐싱global-caching-의-한계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%80%EB%A1%9C%EB%B2%8C-%EC%BA%90%EC%8B%B1global-caching-%EC%9D%98-%ED%95%9C%EA%B3%84&quot; aria-label=&quot;글로벌 캐싱global caching 의 한계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;글로벌 캐싱(Global Caching) 의 한계&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Redis-RedisCacheManager-%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EA%B8%80%EB%A1%9C%EB%B2%8C-%EC%BA%90%EC%8B%B1Global-Caching-favahclm&quot;&gt;[Redis] RedisCacheManager 를 활용한 글로벌 캐싱(Global Caching)&lt;/a&gt; 에서 다루었듯이, 분산 환경에서 Redis 의 글로벌 캐싱을 활용하면 로컬 캐시에 대한 데이터 &lt;code class=&quot;language-text&quot;&gt;일관성 문제&lt;/code&gt; 를 해결 가능합니다. 그러나, Redis 와 같은 사설 저장소를 활용한다면 글로벌 캐싱도 문제점을 지닙니다. 우선 &lt;code class=&quot;language-text&quot;&gt;네트워크 I/O 비용&lt;/code&gt;은 절대 무시할 수 없을것입니다.&lt;/p&gt;
&lt;p&gt;또한 캐시에 데이터를 저장하고 꺼내오는 I/O 과정에서 &lt;code class=&quot;language-text&quot;&gt;직렬화(Serialization)&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;역직렬화(Deserialization)&lt;/code&gt; 비용도 발생할 것입니다. 참고로 직렬화란 객체를 Redis 와 같은 메모리나 저장소에 객체를 저장하기 위해, 객체를 일렬로 나열된 바이트 형태로 변환하는 과정을 의미합니다. 반대로 역직렬화는 직렬화된 데이터를 다시 꺼내올때 객체로 변환하는 과정을 의미합니다.&lt;/p&gt;
&lt;p&gt;이어서 최적화된 Response 를 제공하기 위해선 글로벌 캐시보다는 로컬 캐시를 사용하는 것이 훨씬 더 빠를겁니다. 로컬 캐시는 네트워크 I/O 비용 발생없이 해당 서버의 &lt;code class=&quot;language-text&quot;&gt;지역성(locality)&lt;/code&gt; 특성을 살려서 매우 빠른 속도로 데이터를 읽어오면 되기 때문입니다. 이 외에도 중앙회된 글로벌 캐시 서버를 사용시 발생하는 &lt;code class=&quot;language-text&quot;&gt;단일 지점 문제(SPOF)&lt;/code&gt; 와 같은 문제를 걱정하지 않아도 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;redis-의-pubsub&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-%EC%9D%98-pubsub&quot; aria-label=&quot;redis 의 pubsub permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis 의 Pub/Sub&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/8e71bc83-a38e-4794-9409-e3cd6da2478b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Redis 에는 다양한 메시징 방법을 제공하는데, 그 중 대표적으로 &lt;code class=&quot;language-text&quot;&gt;메시지 큐잉(Message Queueing)&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;Pub/Sub(Publisher-Subscriber)&lt;/code&gt; 메시지브로커를 제공합니다. 메시지 큐잉은 Point-to-Point Channel 방식으로, 오로지 한 소비자만 메세지를 수신받는 방식으로 송신자와 수신자가 1:1 로 대응되어서 데이터를 수신받는 구조이죠.&lt;/p&gt;
&lt;p&gt;반면 Pub/Sub 구조는 이벤트(메시지) 를 발생하는 Publisher 와 Subscriber 가 1:다 의 구조를 지닙니다. 이벤트(메시지)를 발생시켜서 특정 Channel(채널)에 브로드캐스트하는 &lt;code class=&quot;language-text&quot;&gt;발행자(Pubisher)&lt;/code&gt; 가 존재하며, 해당 &lt;code class=&quot;language-text&quot;&gt;Channel(채널)&lt;/code&gt; 을 구독하고 이벤트를 발급받는 여러 &lt;code class=&quot;language-text&quot;&gt;구독자(Subscriber)&lt;/code&gt; 가 존재합니다.&lt;/p&gt;
&lt;p&gt;이러한 Pub/Sub 구조에서 Redis 의 채널은 이벤트를 저장하지 않는다는 비휘발성의 특징을 가지고 있습니다. 만약 Channel 에 이벤트가 도착했을때, 해당 채널의 구독자(Subscriber) 가 존재하지 않거나 그 구독자에 이상이 생겨서 이벤트를 발급받지 못하는 상황이라면, 이벤트는 자연스럽게 사라진다는 비휘발성의 특징이 있습니다. 또 구독자는 동시에 여러 Channel을 구독할 수있으며, 특정한 채널 이름을 명명하지 않고 &apos;패턴&apos;으로도 채널을 구독할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;애플리케이션-서버의-scale-out-상황에서-pubsub-적용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%84%9C%EB%B2%84%EC%9D%98-scale-out-%EC%83%81%ED%99%A9%EC%97%90%EC%84%9C-pubsub-%EC%A0%81%EC%9A%A9&quot; aria-label=&quot;애플리케이션 서버의 scale out 상황에서 pubsub 적용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;애플리케이션 서버의 Scale-Out 상황에서 Pub/Sub 적용&lt;/h3&gt;
&lt;p&gt;이렇게 Redis 의 Pub/Sub 기능을 Scale-Out(수평확장) 구조에서 활용해봅시다. &lt;strong&gt;특정 애플리케이션 서버의 로컬 캐시가 갱신되었을 때, 해당 메시지(이벤트)를 다른 모든 서버에 브로드캐스트 하는 방식으로 여러 로컬 캐시를 동기화하는 방법입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이때 캐시가 갱신되었음을 알리고 이벤트를 발급하는 발행자(Publisher) 는 로컬캐시가 갱신된 서버입니다. 또, 그 로컬 캐시가 갱신된 사실을 브로드캐스트받는 구독자(Subscriber) 는 나머지 모든 애플리케이션 서버가 되는 구조인 것입니다. 브로드캐스트가 되면, 갱신된 캐시의 키를 메시지로 전달하는 방식입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;로컬캐시-동기화-구현하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EC%BB%AC%EC%BA%90%EC%8B%9C-%EB%8F%99%EA%B8%B0%ED%99%94-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0&quot; aria-label=&quot;로컬캐시 동기화 구현하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로컬캐시 동기화 구현하기&lt;/h2&gt;
&lt;p&gt;지금부터 스프링부트 애플리케이션에서 로컬캐시 동기화를 구현하는 코드를 직접 보여보겠습니다. 이 방식은 로컬 캐시에서 변경된 데이터만을 채널에 브로트캐스트 하는 방식이며, &quot;Book&quot; 이라는 클래스 객체를 캐시에 저장함을 가정했습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;기본-로컬캐싱-셋팅&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EB%B3%B8-%EB%A1%9C%EC%BB%AC%EC%BA%90%EC%8B%B1-%EC%85%8B%ED%8C%85&quot; aria-label=&quot;기본 로컬캐싱 셋팅 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기본 로컬캐싱 셋팅&lt;/h2&gt;
&lt;h3 id=&quot;book-entity&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#book-entity&quot; aria-label=&quot;book entity permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Book Entity&lt;/h3&gt;
&lt;p&gt;Book 은 아래와 같이 정말 간단하게만 구현했습니다. PK 와 책 이름 name 만을 저장합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@AllArgsConstructor&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Setter&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GeneratedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenerationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;localcacheconfig&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#localcacheconfig&quot; aria-label=&quot;localcacheconfig permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LocalCacheConfig&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@EnableCaching&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalCacheConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CacheManager&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cacheManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConcurrentMapCacheManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;지난번에는 글로벌 캐싱을 위해 RedisCacheManager 를 빈으로 등록해줬다면, 이번에는 간단히 캐시매니저로 간단히 &lt;code class=&quot;language-text&quot;&gt;ConcurrentMapCacheManager&lt;/code&gt; 를 사용했습니다. 각 서버의 로컬캐시에는 이 자료구조에 기반하여 Book 객체가 직렬화되어 캐싱될 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;redisconfig&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redisconfig&quot; aria-label=&quot;redisconfig permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedisConfig&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;${spring.data.redis.host}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; redisHost&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;${spring.data.redis.port}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; redisport&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LettuceConnectionFactory&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;connectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LettuceConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;redisHost&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; redisport&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisMessageListenerContainer&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisMessageListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LettuceConnectionFactory&lt;/span&gt; lettuceConnectionFactory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RedisMessageListenerContainer&lt;/span&gt; container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisMessageListenerContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lettuceConnectionFactory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; container&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;redisTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setKeySerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringRedisSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setValueSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Jackson2JsonRedisSerializer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;다음으로 Redis 와의 연결 및 &lt;code class=&quot;language-text&quot;&gt;RedisMessageContainer&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;RedisTemplate&lt;/code&gt; 을 활용하기 위한 설정정보를 담고있는 RedisConfig 클래스입니다. 기본적으로 Lettuce 를 활용하기위해 &lt;code class=&quot;language-text&quot;&gt;LettuceConnectionFactory&lt;/code&gt; 팩토리 메소드를 생성했습니다. 이 메소드 객체는 redis 와의 연결을 수행합니다.&lt;/p&gt;
&lt;p&gt;또 &lt;code class=&quot;language-text&quot;&gt;RedisMessageListenerContainer&lt;/code&gt; 는 &lt;code class=&quot;language-text&quot;&gt;구독자(Subsscriber)&lt;/code&gt; 가 특정 &lt;code class=&quot;language-text&quot;&gt;채널(토픽)&lt;/code&gt; 을 구독하도록 만드는 역할을 수행하며, 추후 살펴볼 구독자 클래스의 &lt;code class=&quot;language-text&quot;&gt;MessageListener&lt;/code&gt; 인터페이스를 구현한 클래스인 RedisSubscriber 메시지 리스너를 등록하고 관리합니다. ( MessageListener 인터페이스를 구현한 클래스는 구독자가 됩니다.)&lt;/p&gt;
&lt;p&gt;또한 이전에 다루었던 내용이지만 redisTemplate 을 등록하고, 활용할 팩토리 메소드와 직렬화 &amp;#x26; 역직렬화할 데이터 타입을 명시해주고 있습니다. 위의 경우는 key 값을 문자열(String) 타입을, value 값을 JSON 타입을 직렬화해서 redis 에 저장함을 지정하고 있습니다. 여기서는 사용하지 않았지만, &lt;code class=&quot;language-text&quot;&gt;setDefaultSerializer&lt;/code&gt; 를 활용할 경우 key 와 value 에 대해 한꺼번에 직렬화 타입을 동일하게 명시할 수도 있음을 알아만둡시다.&lt;/p&gt;
&lt;h3 id=&quot;추가적인-직렬화-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B6%94%EA%B0%80%EC%A0%81%EC%9D%B8-%EC%A7%81%EB%A0%AC%ED%99%94-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;추가적인 직렬화 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;추가적인 직렬화 방식&lt;/h3&gt;
&lt;p&gt;이번 주제와 조금 벗어날 수 있는 내용이므로 자세한 설명은 생략하겠지만, 직렬화를 가능한 타입은 다음과 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;StringRedisSerializer, JdkSerializationRedisSerializer&lt;/li&gt;
&lt;li&gt;Jackson2JsonRedisSerializer, GenericJackson2JsonRedisSerializer&lt;/li&gt;
&lt;li&gt;OxmSerializer, ByteArrayRedisSerializer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이들을 활용하면 다양한 타입으로 직렬화가 가능하다고 하니, 자세한 내용은 공식문서를 참고합시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;publisher--subscriber-정의&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#publisher--subscriber-%EC%A0%95%EC%9D%98&quot; aria-label=&quot;publisher  subscriber 정의 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Publisher &amp;#x26; Subscriber 정의&lt;/h2&gt;
&lt;h3 id=&quot;redispublisher&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redispublisher&quot; aria-label=&quot;redispublisher permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedisPublisher&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisPublisher&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisPublisher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ChannelTopic&lt;/span&gt; topic&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;convertAndSend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;topic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTopic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Publisher 에 대한 정의 클래스입니다. &lt;code class=&quot;language-text&quot;&gt;publish()&lt;/code&gt; 를 보면 외부로부터 전달받은 채널(토픽) 에다가 Book 타입의 메시지를 발행하는 기능을 수행합니다. 이때 외부란 말이 햇갈릴 수 있는데, 아래와 같이 API 를 호출시 존재하는 Service 단의 메소드를 의미하는 것입니다. 그리고 RedisTemplate 의 내장 메소드인 &lt;code class=&quot;language-text&quot;&gt;convertAndSend()&lt;/code&gt; 을 통해서 실제로 Book 타입의 메시지를 Redis 에다 발행되는 메커니즘입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ... (업데이트 로직)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// bookRepository.save(book);&lt;/span&gt;
  redisPublisher&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ChannelTopic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;book-renewal&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;redissubscriber&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redissubscriber&quot; aria-label=&quot;redissubscriber permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedisSubscriber&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisSubscriber&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MessageListener&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CacheManager&lt;/span&gt; cacheManager&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisSubscriber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                           &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CacheManager&lt;/span&gt; cacheManager&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                           &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisMessageListenerContainer&lt;/span&gt; redisMessageListener&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cacheManager &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;cacheManager&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;ChannelTopic&lt;/span&gt; bookRenewalChannel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChannelTopic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;book-renewal&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        redisMessageListener&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMessageListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bookRenewalChannel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Message&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; pattern&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; body &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBody&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValueSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deserialize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        cacheManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;evict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;book&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이어서 Subscriber 에 대한 정의 클래스입니다.다른 애플리케이션에서 메시지가 발행되었을 때, 정해진 명령을 실행하는 구독자입니다. 앞서 &lt;code class=&quot;language-text&quot;&gt;RedisConfig&lt;/code&gt; 클래스의 설명을 잘 읽었다면 &lt;code class=&quot;language-text&quot;&gt;MessageListener&lt;/code&gt; 인터페이스를 구현했다는 것을 볼 수 있는데, MessageListener 를 구현한 RedisSubscriber 클래스는 &lt;code class=&quot;language-text&quot;&gt;메시지 리스너&lt;/code&gt; (= 메시지를 전달받는자)로 등록되고 관리됩니다. 정확히는 &lt;code class=&quot;language-text&quot;&gt;onMessage()&lt;/code&gt; 메소드를 오버라이드하고 활용하기 위해 인터페이스를 구현한 것입니다.&lt;/p&gt;
&lt;h4&gt;OnMessage()&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;onMessage()&lt;/code&gt; 메소드는 메시지가 발행되었을때 자동으로 호출되는 메소드입니다. &lt;code class=&quot;language-text&quot;&gt;MessageListener&lt;/code&gt; 를 구현한 클래스가 메소드가 발행되었을때 어떤 행동을 취할지 이 메소드 안에 정의해주면 됩니다. 저는 이번 메소드가 발행되었을때 위처럼 &lt;strong&gt;CacheManager를 사용하여 &quot;book&quot; 캐시에서 변경이 발생한 캐시를 무효화(invalidation)하는 로직&lt;/strong&gt;을 정의해줬습니다. 즉, 이 메소드를 수행한 모든 Subscriber 서버의 로컬캐시의 데이터는 삭제(= evict) 되는 것입니다.&lt;/p&gt;
&lt;h4&gt;캐시 무효화란?&lt;/h4&gt;
&lt;p&gt;캐시의 무효화란 캐시에서 갱신된 특정 데이터를 캐시에서 삭제하는 행위를 의미한다고 보면 됩니다. 즉, 캐시 무효화가 수행된 구독자 서버들은 해당 데이터를 조회하는 API 를 다음에 호출할때는 데이터베이스로부터 새롭게 조회해와야 합니다. 그리고 그 가져온 새로운 데이터를 캐시에 write 함으로써 로컬캐시의 데이터가 최신화 되는 것입니다.&lt;/p&gt;
&lt;h4&gt;RedisMessageListenerContainer&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;RedisMessageListenerContainer&lt;/code&gt; 는 특정 토픽 또는 채널을 구독하는 역할을 수행하며, MessageListener 구현한 클래스인 RedisSubscriber 메시지 리스너에게 새로운 채널을 등록하고 관리하도록 도와주는 역할을 수행합니다. 그래서 위 코드의 생성자를보면, &quot;book-renewal&quot; 이라는 새로운 채널을 Subscriber 에게 만들어주는 역할을 수행하는걸 볼 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;스프링부트-애플리케이션-api-코드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-api-%EC%BD%94%EB%93%9C&quot; aria-label=&quot;스프링부트 애플리케이션 api 코드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링부트 애플리케이션 API 코드&lt;/h2&gt;
&lt;h3 id=&quot;bookcontroller&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bookcontroller&quot; aria-label=&quot;bookcontroller permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BookController&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Slf4j&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookService&lt;/span&gt; bookService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/data&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBookInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestParam&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; param&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bookService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBookInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@PostMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/data&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateBookInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestParam&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; param&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        bookService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 Book 객체에 대해 조회 및 수정이 가능한 API 2개를 정의했습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;bookservice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bookservice&quot; aria-label=&quot;bookservice permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BookService&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookRepository&lt;/span&gt; bookRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisPublisher&lt;/span&gt; redisPublisher&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BookRepository&lt;/span&gt; bookRepository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisPublisher&lt;/span&gt; redisPublisher&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bookRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bookRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisPublisher &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisPublisher&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Cacheable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;readOnly &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBookInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; param&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Finding book&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; param &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; from databases...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bookRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findByName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Get Complete!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ... (업데이트 로직)&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bookRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findByName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        book&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        bookRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;book&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        redisPublisher&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ChannelTopic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;book-renewal&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;getBookInfo&lt;/h4&gt;
&lt;p&gt;우선 &lt;code class=&quot;language-text&quot;&gt;@Cacheable&lt;/code&gt; 어노테이션을 통해 캐시에 없는 데이터에 대해 캐싱하도록 처리했습니다. 이 메소드의 리턴타입인 Book 이 로컬캐시에 저장되도록 했는데, 이 타입을 잠깐 유의해서 보면 앞서 RedisConfig 에서 살펴본 직렬화 타입과도 매칭합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setKeySerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringRedisSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setValueSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Jackson2JsonRedisSerializer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 key, value 에 대한 직렬화 타입을 명시해줬는데, 여기서 key 값에는 &quot;book&quot; 이 저장되며, &quot;value&quot; 로는 Jackson JSON 라이브러리를 사용하여 Book 객체를 JSON 형식으로 직렬화하여 저장하는 것입니다. 역직렬화도 동일한 타입으로 수행됩니다.&lt;/p&gt;
&lt;h4&gt;updateBook&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;redisPublisher&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ChannelTopic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;book-renewal&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞서 RedisPublisher 클래스에서 정의한 &lt;code class=&quot;language-text&quot;&gt;publish()&lt;/code&gt; 메소드를 통해 &quot;book-renewal&quot; 이라는 채널에 book 객체라는 메시지를 발행하는 모습입니다.&lt;/p&gt;
&lt;p&gt;참고로 이 메소드는 리턴타입으로 void 타입을 명시해줬는데, 별도의 리턴타입을 굳이 명시할 필요가 없었기 때문입니다. 그저 Redis 서버의 &quot;book-renewal&quot; 이라는 채널에 메시지를 잘 전달하고, Subscriber 서버는 로컬캐시에서 &lt;code class=&quot;language-text&quot;&gt;캐시 무효화&lt;/code&gt; 작업만 잘 수행해주면 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;실행결과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%ED%96%89%EA%B2%B0%EA%B3%BC&quot; aria-label=&quot;실행결과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실행결과&lt;/h2&gt;
&lt;p&gt;여러 서버에 요청을 여러번 보내고 데이터 수정 요청을 보내도, 로컬 캐시가 잘 동기화되어 일관성 문제를 해결할 수 있었습니다. 또한 캐시의 특성을 살려서 평균 &lt;code class=&quot;language-text&quot;&gt;28ms&lt;/code&gt; 이라는 매우 빠른 속도로 데이터를 조회할 수 있게 되었습니다 🙂&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해야할-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EC%95%BC%ED%95%A0-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해야할 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해야할 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Spring Data Redis : MessageListener, RedisMessageListenerContainer, RedisConnectionFactory&lt;/li&gt;
&lt;li&gt;직렬화/역직렬화&lt;/li&gt;
&lt;li&gt;Kafka&lt;/li&gt;
&lt;li&gt;Producer/Consumer&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://brunch.co.kr/@springboot/557&quot;&gt;https://brunch.co.kr/@springboot/557&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.naver.com/ds4ouj/222801168888&quot;&gt;https://blog.naver.com/ds4ouj/222801168888&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/spring-projects/spring-data-redis/releases/tag/3.0.0-M4&quot;&gt;https://github.com/spring-projects/spring-data-redis/releases/tag/3.0.0-M4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pompitzz.github.io/blog/Redis/LocalCacheSyncWithRedisPubSub.html#redis-pub-sub&quot;&gt;https://pompitzz.github.io/blog/Redis/LocalCacheSyncWithRedisPubSub.html#redis-pub-sub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/pub-sub-vs-message-queues&quot;&gt;https://www.baeldung.com/pub-sub-vs-message-queues&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/frientrip/pub-sub-%EC%9E%98-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90-de9dc1b9f739&quot;&gt;https://medium.com/frientrip/pub-sub-%EC%9E%98-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90-de9dc1b9f739&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL 인덱스에 카티널리티를 고려하여 적용하는 기준]]></title><description><![CDATA[카니널리티(Cardinality)  란 사전적 의미로 "그룹 내 요소의 개수" 를 의미합니다. 인덱스를 적용시, 카디널리티가 높은, 즉  에 대해 적용시켜주면 됩니다. 예를들어 PK…]]></description><link>https://haon.site/haon/index/cardinality/</link><guid isPermaLink="false">https://haon.site/haon/index/cardinality/</guid><pubDate>Fri, 16 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;카니널리티cardinality&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B9%B4%EB%8B%88%EB%84%90%EB%A6%AC%ED%8B%B0cardinality&quot; aria-label=&quot;카니널리티cardinality permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;카니널리티(Cardinality)&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;카니덜리티(Cardinality)&lt;/code&gt; 란 사전적 의미로 &quot;그룹 내 요소의 개수&quot; 를 의미합니다. 인덱스를 적용시, 카디널리티가 높은, 즉 &lt;code class=&quot;language-text&quot;&gt;중복 수치가 낮은컬럼&lt;/code&gt; 에 대해 적용시켜주면 됩니다. 예를들어 PK 컬럼이 카디널리티가 높은 경우에 해당될텐데, 유니크한 특성으로인해 중복되는 값이 존재하지 않으므로 수치가 높을것입니다.&lt;/p&gt;
&lt;p&gt;예를들어 아래와 같은 Member 테이블이 있다고 해봅시다. 각 컬럼의 카디널리티(= 요소의 개수) 를 계산해보면, &quot;성별&quot; 컬럼의 경우, 값의 중복이 굉장히 많고 남자와 여자밖에 존재하지 않기 때문에 카디널리티가 굉장히 낮은것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e83dea3c-3caf-4c14-ade7-3d067b15c7ee/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;따라서 우리는 카디널리티가 낮은, 즉 중복도가 높은 컬럼에 대해 인덱스를 적용해야합니다. 여기서는 id 나 이메일, 주민번호, 이름등이 인덱스 적용 대상이 될 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;인덱스를-적용시키기-좋은-추가-case&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%EC%A0%81%EC%9A%A9%EC%8B%9C%ED%82%A4%EA%B8%B0-%EC%A2%8B%EC%9D%80-%EC%B6%94%EA%B0%80-case&quot; aria-label=&quot;인덱스를 적용시키기 좋은 추가 case permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스를 적용시키기 좋은 추가 case&lt;/h2&gt;
&lt;p&gt;카티널리티 외에도 추가적으로 어떤 경우에 인덱스를 적용시키면 좋을까요?&lt;/p&gt;
&lt;h3 id=&quot;1-cardinality&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-cardinality&quot; aria-label=&quot;1 cardinality permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Cardinality&lt;/h3&gt;
&lt;p&gt;앞서 말했듯이, 중복도가 낮은 컬럼에 대해 인덱스를 적용시키면 좋을겁니다. 만야 중복도가 높은 컬럼에 대해 인덱스를 적용시키고 추후 조건절에서 활용한다면, 인덱스 탐색 범위가 굉장히 넓어지게 되고 최악의 경우는 차리리 &lt;code class=&quot;language-text&quot;&gt;테이블 풀 스캔(Table Full Scan)&lt;/code&gt; 을 시도하는 경우가 더 나을 수도 있습니다.&lt;/p&gt;
&lt;p&gt;예를들어 인덱스 레인지 스캔을 수행하는 경우, &lt;a href=&quot;https://velog.io/@msung99/MySQL-8.0-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EC%BF%BC%EB%A6%AC-%EC%84%B1%EB%8A%A5-%ED%8A%9C%EB%8B%9D%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%9E%9C%EB%8D%A4-IO-%EC%99%80-%EC%88%9C%EC%B0%A8-IO&quot;&gt;[MySQL 8.0] 데이터베이스의 쿼리 성능 튜닝을 위한 랜덤 I/O 와 순차 I/O&lt;/a&gt; 에서도 언급했듯이 이 기준은 읽어야할 레코드가 20~25% 를 넘으면 테이블의 데이터를 직접 읽는것이 더 효율적인 처리 방식이라고 했었습니다.&lt;/p&gt;
&lt;h3 id=&quot;2-조건절에-자주-사용되는-컬럼인-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EC%A1%B0%EA%B1%B4%EC%A0%88%EC%97%90-%EC%9E%90%EC%A3%BC-%EC%82%AC%EC%9A%A9%EB%90%98%EB%8A%94-%EC%BB%AC%EB%9F%BC%EC%9D%B8-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;2 조건절에 자주 사용되는 컬럼인 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 조건절에 자주 사용되는 컬럼인 경우&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;WHERE&lt;/code&gt; , &lt;code class=&quot;language-text&quot;&gt;JOIN&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ORDER BY&lt;/code&gt; 와 같이 조건절에 자주 사용되는 컬럼인 경우, 인덱스를 적용시키는게 좋습니다. &lt;a href=&quot;https://velog.io/@msung99/MySQL-8.0-MySQL-%EC%97%90%EC%84%9C%EC%9D%98-B-Tree-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%ED%86%B5%ED%95%9C-%EB%A0%88%EC%BD%94%EB%93%9C-%EC%8A%A4%EC%BA%94-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B0%A9%EC%8B%9D-Index-Scan&quot;&gt;[MySQL 8.0] MySQL 에서 B+ Tree 인덱스 스캔을 통한 성능 최적화 방식 (Index Scan)&lt;/a&gt; 에서도 언급했듯이, 만약 조건절에 인덱스 컬럼이 없다면 인덱스는 활용되지 못하고 방치되는 상황이기 때문에 쓸모없는 추가 공간만 낭비하는 상황이 되는 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;3-insert-update-delete-가-자주-발생-안하는-컬럼인-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-insert-update-delete-%EA%B0%80-%EC%9E%90%EC%A3%BC-%EB%B0%9C%EC%83%9D-%EC%95%88%ED%95%98%EB%8A%94-%EC%BB%AC%EB%9F%BC%EC%9D%B8-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;3 insert update delete 가 자주 발생 안하는 컬럼인 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. insert, update, delete 가 자주 발생 안하는 컬럼인 경우&lt;/h3&gt;
&lt;p&gt;계속 언급했던 것이지만, &lt;code class=&quot;language-text&quot;&gt;B+ Tree&lt;/code&gt; 구조는 삽입, 수정, 삭제 성능을 버린 대신에 조회(select) 의 성능에 최적화된 구조입니다. 때문에 이러한 연산이 자주 발생하지 않는 컬럼에 대해 인덱스를 만드는것이 좋습니다.&lt;/p&gt;
&lt;h3 id=&quot;4-규모가-작지-않은-테이블&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-%EA%B7%9C%EB%AA%A8%EA%B0%80-%EC%9E%91%EC%A7%80-%EC%95%8A%EC%9D%80-%ED%85%8C%EC%9D%B4%EB%B8%94&quot; aria-label=&quot;4 규모가 작지 않은 테이블 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 규모가 작지 않은 테이블&lt;/h3&gt;
&lt;p&gt;그리고 규모가 작은 테이블에는 인덱스를 적용하더라도 효과가 미미하기 떄문에, 규모가 작지 않은 테이블에 인덱스를 적용하는 것이 효과가 좋습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;인덱스-유의사항&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EC%9C%A0%EC%9D%98%EC%82%AC%ED%95%AD&quot; aria-label=&quot;인덱스 유의사항 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스 유의사항&lt;/h2&gt;
&lt;p&gt;추가적으로 인덱스를 사용시, &lt;strong&gt;잘 호출되지 않는 컬럼에 대한 인덱스는 과감히 제거하는 것이 좋습니다.&lt;/strong&gt; 조건절에 사용되더라도 자주 사용해야 가치가 있기 때문이죠. 방치되는 인덱스라면 불필요한 인덱스로써 성능 저하의 요인만 될 것입니다.&lt;/p&gt;
&lt;p&gt;반대로 자주 사용되는 컬럼이더라도 삽입, 수정, 삭제가 자주 일어나는지 고려하는 것이 좋습니다. 일반적인 트랜잭션 온라인 환경에서는 읽기와 쓰기의 비율이 2:8 ~ 1:9 수준입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;클러스터링-인덱스-clustering-index&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4-clustering-index&quot; aria-label=&quot;클러스터링 인덱스 clustering index permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클러스터링 인덱스 (Clustering Index)&lt;/h2&gt;
&lt;h3 id=&quot;장점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90&quot; aria-label=&quot;장점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h3&gt;
&lt;p&gt;지난 &lt;a href=&quot;https://velog.io/@msung99/MySQL-8.0-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%ED%98%95-%EC%9D%B8%EB%8D%B1%EC%8A%A4Clustered-Index-%EC%99%80-%EB%B9%84%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%ED%98%95-%EC%9D%B8%EB%8D%B1%EC%8A%A4Non-Clustered-Index-%EA%B8%B0%EB%B3%B8%ED%82%A4%EB%A5%BC-%ED%86%B5%ED%95%9C-%EA%B5%B0%EC%A7%91%ED%99%94&quot;&gt;[MySQL 8.0] 클러스터형 인덱스(Clustered Index) 와 비클러스터형 인덱스(Non-Clustered Index), 기본키를 통한 군집화&lt;/a&gt; 에서도 언급했듯이, MySQL 8.0 에서는 클러스터링 인덱스로 PK 를 보통 취하고있습니다. 이러한 클러스터링 인덱스를 통해,
&lt;code class=&quot;language-text&quot;&gt;PK 값으로 조회 쿼리문을 수행시 처리 속도가 매우빠르다&lt;/code&gt; 는 특징을 지니고있습니다. 또 테이블의 모든 세컨터리 인덱스가 PK 를 가지고있기 때문에, 인덱스만으로 처리될 수 있는 경우가 굉장히 많습니다. 이를 &lt;code class=&quot;language-text&quot;&gt;커버링 인덱스&lt;/code&gt; 라고 했습니다.&lt;/p&gt;
&lt;h3 id=&quot;auto-increment-보다-업무적인-컬럼으로-pk-를-생성하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#auto-increment-%EB%B3%B4%EB%8B%A4-%EC%97%85%EB%AC%B4%EC%A0%81%EC%9D%B8-%EC%BB%AC%EB%9F%BC%EC%9C%BC%EB%A1%9C-pk-%EB%A5%BC-%EC%83%9D%EC%84%B1%ED%95%98%EC%9E%90&quot; aria-label=&quot;auto increment 보다 업무적인 컬럼으로 pk 를 생성하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AUTO-INCREMENT 보다 업무적인 컬럼으로 PK 를 생성하자&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d3eb8356-4d5b-41b9-8a3b-f473c9b0a3de/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;InnoDB 의 PK 는 대부분의 경우 클러스터링 키로 사용되며, 이 값에의해 &lt;strong&gt;레코드의 위치가 결정됩니다.&lt;/strong&gt; 즉, PK 로 조회 쿼리문을 실행하는 경우 클러스터링 되지 않은 테이블에 비해 매우 빠르게 처리될 수가 있죠. 따라서 &lt;code class=&quot;language-text&quot;&gt;AUTO INCREMENT&lt;/code&gt; 전략도 좋겠지만, 매우 중요한 key 인 만큼 업무적으로 해당 레코드를 대표할 수 있게 만들어준다면 더 좋을것입니다.&lt;/p&gt;
&lt;p&gt;PK 값이 변경되는 일은 거의 없겠지만, 만약 변동이 생긴다면 레코드의 구조를 PK 값을 기준으로 재정렬 해야하는 비용이 발생하므로, 이를 최소화하는 것이 좋을겁니다.&lt;/p&gt;
&lt;h3 id=&quot;pk-는-반드시-명시하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pk-%EB%8A%94-%EB%B0%98%EB%93%9C%EC%8B%9C-%EB%AA%85%EC%8B%9C%ED%95%98%EC%9E%90&quot; aria-label=&quot;pk 는 반드시 명시하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PK 는 반드시 명시하자&lt;/h3&gt;
&lt;p&gt;가끔 PK 가 없는 테이블을 볼 수 있는데, 가능하면 &lt;code class=&quot;language-text&quot;&gt;AUTO INCREMENT&lt;/code&gt; 컬럼을 이용해서라도 PK 는 생성하는게 좋습니다. 이전에 다루었듯이, 만약 PK 를 정의하지 않으면 InnoDB 스토리지 엔진에 내부적으로 &lt;code class=&quot;language-text&quot;&gt;GEN_CLUST_INDEX&lt;/code&gt; 라는 값을 자동으로 추가하며 이는 사용자에게 보이지도 않고 쿼리문에서 활용하지도 못합니다.&lt;/p&gt;
&lt;p&gt;결국 PK 를 정의하지 않은 경우와 &lt;code class=&quot;language-text&quot;&gt;AUTO_INCREMENT&lt;/code&gt; 컬럼을 생성하고 PK 를 설정한 경우가 모두 똑같은 상황입니다. 따라서 이왕이면 사용자가 사용 가능한 PK 로 설정하는게 좋을겁니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;옵티마이저&lt;/li&gt;
&lt;li&gt;다중 컬럼 인덱스&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Real MySQL 8.0 (백은빈, 이성욱 지음)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=edpYzFgHbqs&amp;#x26;t=1230s&quot;&gt;https://www.youtube.com/watch?v=edpYzFgHbqs&amp;#x26;t=1230s&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[어노테이션 기반 Redis 캐싱]]></title><description><![CDATA[RedisConfig 지난 내용과 대부분 유사한 테스트 코드를 짜봤습니다. 로컬 캐시를 Redis…]]></description><link>https://haon.site/haon/redis/caching/</link><guid isPermaLink="false">https://haon.site/haon/redis/caching/</guid><pubDate>Fri, 16 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;redisconfig&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redisconfig&quot; aria-label=&quot;redisconfig permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedisConfig&lt;/h2&gt;
&lt;p&gt;지난 내용과 대부분 유사한 테스트 코드를 짜봤습니다. 로컬 캐시를 Redis 기반의 글로벌 캐시로 바꿨다는점이 이번 내용의 가장 핵심이자 지난 내용과의 차이점이 될것입니다. 즉, &lt;code class=&quot;language-text&quot;&gt;ConcurrentHashMap&lt;/code&gt; 에서 &lt;code class=&quot;language-text&quot;&gt;RedisCache&lt;/code&gt; 로 전환하는 것이 이번 내용입니다.&lt;/p&gt;
&lt;h3 id=&quot;redis-포트-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-%ED%8F%AC%ED%8A%B8-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;redis 포트 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;redis 포트 설정&lt;/h3&gt;
&lt;p&gt;docker 를 이용해 redis 를 간단히 띄웠으며, 6379 포트로 지정해줬습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;spring&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;host&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;localhost
spring&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;password&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1234&lt;/span&gt;
spring&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6379&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;redisconfig-전체코드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redisconfig-%EC%A0%84%EC%B2%B4%EC%BD%94%EB%93%9C&quot; aria-label=&quot;redisconfig 전체코드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedisConfig 전체코드&lt;/h3&gt;
&lt;p&gt;Redis Cache를 적용하기 위해, &lt;code class=&quot;language-text&quot;&gt;RedisCacheManager&lt;/code&gt; 및 &lt;code class=&quot;language-text&quot;&gt;RedisConnectionFactory&lt;/code&gt; 를 등록해주면 Redis 캐시 설정이 완료됩니다. 아래 전체 코드를 일부 단위로 몇개씩 끊어서 상세히 설명해보겠습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;${spring.data.redis.host}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; redisHost&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;${spring.data.redis.port}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; redisport&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LettuceConnectionFactory&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;connectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LettuceConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;redisHost&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; redisport&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;redisTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDefaultSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenericJackson2JsonRedisSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CacheManager&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;redisCacheManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RedisCacheConfiguration&lt;/span&gt; redisCacheConfiguration &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisCacheConfiguration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;defaultCacheConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;serializeKeysWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RedisSerializationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SerializationPair&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringRedisSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;serializeValuesWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RedisSerializationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SerializationPair&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenericJackson2JsonRedisSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;RedisCacheManager&lt;/span&gt; redisCacheManager &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisCacheManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RedisCacheManagerBuilder&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cacheDefaults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;redisCacheConfiguration&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; redisCacheManager&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;configuration-등록&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#configuration-%EB%93%B1%EB%A1%9D&quot; aria-label=&quot;configuration 등록 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@Configuration 등록&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;${spring.data.redis.host}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; redisHost&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;${spring.data.redis.port}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; redisport&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;우선 해당 클래스가 스프링의 구성 클래스임을 나타냈습니다. 어려운 내용은 아니므로, 자세한 설명은 생략하겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;lettuceconnectionfactory-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lettuceconnectionfactory-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;lettuceconnectionfactory 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LettuceConnectionFactory 생성&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LettuceConnectionFactory&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;connectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LettuceConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;redisHost&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; redisport&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;LettuceConnectionFactory&lt;/code&gt; 를 생성하여 Redis와의 연결을 설정하는 빈(Bean)입니다. redisHost와 redisPort 값을 사용하여 LettuceConnectionFactory를 구성하고 반환합니다. &lt;code class=&quot;language-text&quot;&gt;LettuceConnectionFactory&lt;/code&gt; 는 Redis와의 연결을 관리하기 위한 Spring Data Redis의 연결 팩토리(Factory) 클래스로, Redis 데이터베이스와의 통신을 처리합니다.&lt;/p&gt;
&lt;h3 id=&quot;redistemplate-생성을-위한-bean&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redistemplate-%EC%83%9D%EC%84%B1%EC%9D%84-%EC%9C%84%ED%95%9C-bean&quot; aria-label=&quot;redistemplate 생성을 위한 bean permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;redisTemplate 생성을 위한 Bean&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;redisTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDefaultSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenericJackson2JsonRedisSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Redis 와 상호작용하기 위한 주요 클래스로써 RedisTemplate 를 활용하기위해, RedisTemplate 을 생성하는 빈을 선언했습니다. 앞서 connectionFactory( ) 메소드에서 생성한 &lt;code class=&quot;language-text&quot;&gt;LettuceConnectionFactory&lt;/code&gt; 를 RedisTemplate 와 연결하도록 했습니다. 또한, &lt;code class=&quot;language-text&quot;&gt;GenericJackson2JsonRedisSerializer&lt;/code&gt; 를 기본 직렬화기로 설정해서 객체를 JSON 형식으로 직렬화하여 Redis 에 저장하는데 사용되도록 했습니다.&lt;/p&gt;
&lt;h3 id=&quot;rediscachemanager-생성을-위한-bean&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rediscachemanager-%EC%83%9D%EC%84%B1%EC%9D%84-%EC%9C%84%ED%95%9C-bean&quot; aria-label=&quot;rediscachemanager 생성을 위한 bean permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedisCacheManager 생성을 위한 Bean&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CacheManager&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;redisCacheManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;RedisCacheConfiguration&lt;/span&gt; redisCacheConfiguration &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisCacheConfiguration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;defaultCacheConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;serializeKeysWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RedisSerializationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SerializationPair&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringRedisSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;serializeValuesWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RedisSerializationContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SerializationPair&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenericJackson2JsonRedisSerializer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;RedisCacheManager&lt;/span&gt; redisCacheManager &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisCacheManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RedisCacheManagerBuilder&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromConnectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connectionFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cacheDefaults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;redisCacheConfiguration&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; redisCacheManager&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;RedisCacheManager를 생성하는 빈입니다. &lt;code class=&quot;language-text&quot;&gt;RedisCacheConfiguration&lt;/code&gt; 을 설정하고, connectionFactory() 메서드에서 생성한 &lt;code class=&quot;language-text&quot;&gt;LettuceConnectionFactory&lt;/code&gt; 와 연결하여 &lt;code class=&quot;language-text&quot;&gt;RedisCacheManager&lt;/code&gt; 를 구성합니다. 직렬화 설정으로는 &lt;code class=&quot;language-text&quot;&gt;StringRedisSerializer&lt;/code&gt; 를 키(key) 직렬화기로,&lt;code class=&quot;language-text&quot;&gt;GenericJackson2JsonRedisSerializer&lt;/code&gt; 를 값(value) 직렬화기로 사용합니다. 이 설정은 캐시된 데이터의 키와 값을 Redis에서 직렬화하고 역직렬화하는 데 사용됩니다.&lt;/p&gt;
&lt;p&gt;참고로 Redis 에 객체를 저장할떄 이렇게 serializer 를 통해 직렬화 해줘야하는데, 이 직렬화 방법에는 다음과 같은 방법들이 있으니 참고바랍니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jackson2JsonRedisSerializer&lt;/li&gt;
&lt;li&gt;StringRedisSerializer&lt;/li&gt;
&lt;li&gt;GenericJackson2JsonRedisSerializer&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;스프링부트-코드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%BD%94%EB%93%9C&quot; aria-label=&quot;스프링부트 코드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링부트 코드&lt;/h2&gt;
&lt;h3 id=&quot;bookcontroller&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bookcontroller&quot; aria-label=&quot;bookcontroller permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BookController&lt;/h3&gt;
&lt;p&gt;테스트를 위한 코드들은 지난번과 거의 동일하다고 헀었습니다. 여기에 새롭게 컨트롤러 하나가 추가되었습니다. 문자열 하나를 쿼리스트링으로 받고, 이에대해 Service 단에서 캐싱을 진행하는 로직입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Slf4j&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookService&lt;/span&gt; bookService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/data&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestParam&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; param&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;------- call Controller&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; start &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bookService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBookById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; end &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;------ controller time = {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;bookservice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bookservice&quot; aria-label=&quot;bookservice permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BookService&lt;/h3&gt;
&lt;p&gt;서비스단의 코드는 지난번과 매우 흡사하나, 파라미터와 리턴값만 조금 달리해봤습니다. String 을 받고, 이에 대한 캐싱을 진행하는 방식입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Cacheable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBookById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; param&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Finding book&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; param &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; from databases...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 데이터베이스 조회 쿼리가 5초 걸린다는 가정&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; param&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;실행결과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%ED%96%89%EA%B2%B0%EA%B3%BC&quot; aria-label=&quot;실행결과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실행결과&lt;/h2&gt;
&lt;p&gt;postman 을 통해 간단히 실행결과를 확인해봤습니다. 우선 애플리케이션 실행후 최초로 API 를 호출한 결과로, 아직 캐싱이 진행되지 않은 상태입니다. 오른쪽에 &quot;Time&quot; 란을 보면 86ms 라는 시간이 걸린것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/956cdfbe-e0fa-40be-943f-2a65b56e1598/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;다시한번 호출했을때 8ms 로 응답시간이 대폭 감소한 것을 볼 수 있습니다.
&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ba744e4d-a44a-479f-9201-0ec53e298278/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://liasn.tistory.com/5&quot;&gt;https://liasn.tistory.com/5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev-setung.tistory.com/28&quot;&gt;https://dev-setung.tistory.com/28&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://souljit2.tistory.com/72&quot;&gt;https://souljit2.tistory.com/72&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@bagt/Redis-%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94-%EC%82%BD%EC%A7%88%EA%B8%B0-feat.-RedisSerializer&quot;&gt;https://velog.io/@bagt/Redis-역직렬화-삽질기-feat.-RedisSerializer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@mooh2jj/Redis-Cacheable-CacheEvict&quot;&gt;https://velog.io/@mooh2jj/Redis-Cacheable-CacheEvict&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://deveric.tistory.com/71&quot;&gt;https://deveric.tistory.com/71&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@qotndus43/Cache#%EC%8A%A4%ED%94%84%EB%A7%81%EC%9D%98-%EC%BA%90%EC%8B%9C-%EC%B6%94%EC%83%81%ED%99%94&quot;&gt;https://velog.io/@qotndus43/Cache#스프링의-캐시-추상화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[스프링부트 @Cacheable 기본 캐싱]]></title><description><![CDATA[캐시 (Cache) 캐시는 서버의 부하를 감소하고 성능을 높이고자 사용되는 기술입니다. DB…]]></description><link>https://haon.site/haon/spring/caching/</link><guid isPermaLink="false">https://haon.site/haon/spring/caching/</guid><pubDate>Fri, 16 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;캐시-cache&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BA%90%EC%8B%9C-cache&quot; aria-label=&quot;캐시 cache permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;캐시 (Cache)&lt;/h2&gt;
&lt;p&gt;캐시는 서버의 부하를 감소하고 성능을 높이고자 사용되는 기술입니다. DB 에서조회 및 계산처리를 하는 과정이 복잡하고 시간이 오래걸리는 경우, 캐시에 결과를 저장해두고 추후에 재요청없이 즉시 캐시에서 가져옴으로써 빠른 처리가 가능합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;cacheable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cacheable&quot; aria-label=&quot;cacheable permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@Cacheable&lt;/h2&gt;
&lt;p&gt;스프링부트에서는 &lt;code class=&quot;language-text&quot;&gt;@Cacheable&lt;/code&gt; 이라는 어노테이션을 통한 캐싱 기능을 제공합니다. 캐시는 &lt;code class=&quot;language-text&quot;&gt;메소드의 리턴값과 파라미터&lt;/code&gt;를 저장할 내용으로 주 타킷으로 삼기 때문에, 보통 메소드 단위로 설정하게 되며 클래스나 인터페이스 레벨에 캐시를 하는일은 드뭅니다.&lt;/p&gt;
&lt;p&gt;우선 아래와 같이 의존성을 추가해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;implementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;cache&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;enablecaching&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#enablecaching&quot; aria-label=&quot;enablecaching permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@EnableCaching&lt;/h3&gt;
&lt;p&gt;다음으로 일반적으로 @EnableCaching 어노테이션은 스프링 부트의 메인 애플리케이션 클래스인 @SpringBootApplication 어노테이션이 지정된 클래스에 추가해줍시다. 이렇게 하면 스프링부트 애플리케이션이 시작될 때 자동으로 캐시가 활성화됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@EnableCaching&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@SpringBootApplication&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CoreApplication&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;SpringApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CoreApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;book&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#book&quot; aria-label=&quot;book permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Book&lt;/h3&gt;
&lt;p&gt;간단한 캐시 테스트를 위해, 엔티티 클래스를 간단히나마 정의했습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@AllArgsConstructor&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Setter&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GeneratedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenerationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;bookservice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bookservice&quot; aria-label=&quot;bookservice permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BookService&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookRepository&lt;/span&gt; bookRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BookRepository&lt;/span&gt; bookRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bookRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bookRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Cacheable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBookById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookReq&lt;/span&gt; bookReq&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ... (데이터베이스에서 데이터 로딩하는 로직)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;minsung&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;캐싱을 위해서 위와 같이 메소드위에 &lt;code class=&quot;language-text&quot;&gt;@Cacheable&lt;/code&gt; 어노테이션을 붙이면됩니다.
&lt;code class=&quot;language-text&quot;&gt;value = &quot;book&quot;&lt;/code&gt; 을 부여하면, 서버의 메모리에 book 이라는 이름을 가진 해시테이블을 생성합니다. key 값으로는 파라미터인 id 와 bookReq 로 하고, value 값은 반환값인 Book 객체로 해서 데이터를 캐시에 저장하게 됩니다.&lt;/p&gt;
&lt;p&gt;현재 데이터베이스 코드는 생략했지만 &lt;code class=&quot;language-text&quot;&gt;findById()&lt;/code&gt; 등으로 DB 에 접근해서 데이터를 가져오고, 두번째 조회부터는 캐싱된 데이터를 가지고 즉각 리턴하게됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;로깅을-통해-직접-확인해보기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EA%B9%85%EC%9D%84-%ED%86%B5%ED%95%B4-%EC%A7%81%EC%A0%91-%ED%99%95%EC%9D%B8%ED%95%B4%EB%B3%B4%EA%B8%B0&quot; aria-label=&quot;로깅을 통해 직접 확인해보기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로깅을 통해 직접 확인해보기&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Cacheable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBookById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookReq&lt;/span&gt; bookReq&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Finding book&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; from databases...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 데이터베이스 조회 쿼리가 5초 걸린다는 가정&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ... (데이터베이스에서 데이터 로딩하는 로직)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;minsung&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같은 코드를 만들어서 캐싱이 잘 되는지를 직접 확인해봅시다. 쓰레드를 5초동안 잠재우도록 했는데, 이는 데이터베이스의 조회 쿼리가 5초 걸린다는 가정과 같은 맥락입니다.&lt;/p&gt;
&lt;h3 id=&quot;apprunner&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#apprunner&quot; aria-label=&quot;apprunner permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AppRunner&lt;/h3&gt;
&lt;p&gt;로깅을 위한 AppRunner 라는 클래스를 만들어봤습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AppRunner&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CommandLineRunner&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AppRunner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookService&lt;/span&gt; bookService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AppRunner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BookService&lt;/span&gt; bookService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bookService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bookService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;BookReq&lt;/span&gt; bookReq1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookReq&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;책1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;BookReq&lt;/span&gt; bookReq2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookReq&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;책2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;......Fetching Books........&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bookId1 = &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; bookService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBookById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bookReq1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bookId1 = &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; bookService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBookById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bookReq1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bookId1 = &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; bookService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBookById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bookReq1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bookId1 = &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; bookService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBookById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bookReq1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bookId2 = &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; bookService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBookById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bookReq2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bookId2 = &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; bookService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBookById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bookReq2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bookId2 = &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; bookService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBookById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bookReq2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bookId2 = &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; bookService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBookById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bookReq2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드는 1번 책을 4번 조회하고, 2번 책을 4번 조회하는 코드입니다. 캐싱을 했기 때문에 결과를 예상해보자면 첫번째 조회 결과에서는 5초가 걸릴것이고, 2번째 조회부터는 캐싱된 데이터를 사용하기 때문에 1번, 2번책 데이터를 곧바로 가져올겁니다.&lt;/p&gt;
&lt;h3 id=&quot;캐싱-결과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BA%90%EC%8B%B1-%EA%B2%B0%EA%B3%BC&quot; aria-label=&quot;캐싱 결과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;캐싱 결과&lt;/h3&gt;
&lt;p&gt;그 결과를 보면 아래와 같습니다. 첫번째 조회를 시도할때는 데이터를 얻기위해 5초동안 대기해야하나, 2번째 부터는 매우 빠른 속도로 데이터를 조회할 수 있게됩니다. 또한 보듯이 각 호출 결과를 보면 고작 0.001 초밖에 차이가 나지 않습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d10ffec8-ed5e-458f-91f0-fbceef4c7ad0/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;cacheevict-cacheput&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cacheevict-cacheput&quot; aria-label=&quot;cacheevict cacheput permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@CacheEvict, @CachePut&lt;/h2&gt;
&lt;p&gt;이 외에도 스프링부트는 캐싱을 위해 &lt;code class=&quot;language-text&quot;&gt;@CacheEvict&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;@CachePut&lt;/code&gt; 어노테이션을 제공합니다. 앞서 &lt;code class=&quot;language-text&quot;&gt;@Cacheable&lt;/code&gt; 로 캐싱을 했다면, 반대로 캐싱된 데이터를 캐시에서 삭제하거나 수정하는것도 가능해야할겁니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;@CachePut : @CachePut은 @Cacheable과 유사하게 실행 결과를 캐시에 저장하지만, 조회 시에 저장된 캐시의 내용을 사용하지는 않고 항상 메소드의 로직을 실행한다는 점에서 다릅니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;@CacheEvict : 기본적으로 메소드의 키에 해당하는 캐시만 제거합니다.만약 아래와 같이 메소드에 @CacheEvict를 적용하면 같은 id 값을 가진 데이터만 캐시에서 제거됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@CacheEvict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;deleteBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	bookRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deleteById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@CachePut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#book.id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ... (업데이트 로직)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; bookRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;book&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;옵션-종류&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%B5%EC%85%98-%EC%A2%85%EB%A5%98&quot; aria-label=&quot;옵션 종류 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;옵션 종류&lt;/h2&gt;
&lt;p&gt;위에서 살펴본 3가지 어노테이션의 원활한 캐싱 기능을 위해, 다양한 옵션들이 존재하니, 추가적으로 알아둡시다.&lt;/p&gt;
&lt;h3 id=&quot;key&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#key&quot; aria-label=&quot;key permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;key&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Cacheable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;books&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBookById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;캐시의 키를 지정하는 것입니다. 기본적으로 메소드의 매개변수를 기반으로 자동 생성되는데, 사용자가 이 옵션으로 직접 지정도 가능합니다. 위 예시에서 @Cacheable 어노테이션의 key 옵션은 #id로 지정되어 있습니다. 이렇게 하면 메소드의 id 매개변수 값을 기반으로 캐시 키가 생성됩니다.&lt;/p&gt;
&lt;h3 id=&quot;condition&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#condition&quot; aria-label=&quot;condition permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;condition&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Cacheable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;books&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; condition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#result != null&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBookById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;조건문을 지정하며, 조건이 true인 경우에만 캐싱이 수행됩니다. 예를들어, 위와 같이하면 메소드의 실행 결과가 null이 아닌 경우에만 캐싱이 수행됩니다.&lt;/p&gt;
&lt;h3 id=&quot;sync&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sync&quot; aria-label=&quot;sync permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;sync&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@CachePut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;books&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#book.id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sync &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;@CachePut 어노테이션에서 사용되며, 캐시 갱신 작업을 동기 또는 비동기로 수행할지를 지정합니다. 예를들어 위처럼 sync 옵션은 true로 지정하면, 캐시 최신화 작업이 동기적으로 수행됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/179&quot;&gt;[Spring] 캐시(Cache) 추상화와 사용법(@Cacheable, @CachePut, @CacheEvict)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@bey1548/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%BA%90%EC%8B%9CCacheable-CacheEvict&quot;&gt;스프링 캐시(@Cacheable, @CacheEvict)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jeong-pro.tistory.com/170&quot;&gt;3가지만 기억하자. 스프링 부트 초간단 캐시 @EnableCaching, @Cacheable, @CacheEvict (spring boot cache example)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL 클러스터형 인덱스와 비 클러스터형 인덱스]]></title><description><![CDATA[현 포스팅은 MySQL 8.0 InnoDB 스토리지 엔진을 기반으로 작성했습니다. 차이는 리프노드(leaf node) 에 있다 InnoDB…]]></description><link>https://haon.site/haon/index/cluster/</link><guid isPermaLink="false">https://haon.site/haon/index/cluster/</guid><pubDate>Thu, 15 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;현 포스팅은 MySQL 8.0 InnoDB 스토리지 엔진을 기반으로 작성했습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;차이는-리프노드leaf-node-에-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%A8%EC%9D%B4%EB%8A%94-%EB%A6%AC%ED%94%84%EB%85%B8%EB%93%9Cleaf-node-%EC%97%90-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;차이는 리프노드leaf node 에 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;차이는 리프노드(leaf node) 에 있다&lt;/h2&gt;
&lt;p&gt;InnoDB 스토리지 엔진은 &lt;code class=&quot;language-text&quot;&gt;B+ Tree&lt;/code&gt; 구조의 인덱스를 취하고 있습니다. 결론부터 말하면, 대부분 통상적으로 말하는 &quot;인덱스&quot; 라는 개념은 &lt;code class=&quot;language-text&quot;&gt;비 클러스터링 인덱스(Non-Clustering Index)&lt;/code&gt; 를 말하는듯 합니다.&lt;/p&gt;
&lt;p&gt;먼저 요약해보자면, &lt;strong&gt;클러스터형 인덱스란 레코드를 실제 레코드를 별도로 분리하지 않고, 인덱스가 레코드와 함께 군집화되어 저장되는 방식&lt;/strong&gt;입니다. 반면 리프 노드에서 각 리프 페이지의 요소들이 포인터로 별도로 분리된 데이터 파일의 레코드를 조회해야 하는 방식이라면, &lt;code class=&quot;language-text&quot;&gt;논 클러스터링 인덱스(Non-Clustering Index&lt;/code&gt; 라고 합니다.&lt;/p&gt;
&lt;h3 id=&quot;클러스터링-인덱스의-기본-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;클러스터링 인덱스의 기본 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클러스터링 인덱스의 기본 생성&lt;/h3&gt;
&lt;p&gt;MySQL 8.0 의 InnoDB 스토리지 엔진은 각 테이블당 기본적으로 하나 이상의 인덱스를 생성하는 것을 강제로 하고있으며, 이 때문에 기본적으로 &lt;code class=&quot;language-text&quot;&gt;클러스터링 인덱스&lt;/code&gt;를 생성합니다. 이에 대한 내용은 아래에서 자세히 설명하겠습니다.&lt;/p&gt;
&lt;p&gt;또한 개발자가 직접 인덱스를 새롭게 추가하는 것은 &lt;code class=&quot;language-text&quot;&gt;논 클러스터링 인덱스&lt;/code&gt; 를 새롭게 추가하는 것임을 미리 알고 넘어갑시다. 즉, 기본적으로 존재하는 클러스터링 인덱스에 이어서 새로운 논 클러스터링 인덱스를 추가하는 행위라고 보면됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;인덱스가-없었더라면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4%EA%B0%80-%EC%97%86%EC%97%88%EB%8D%94%EB%9D%BC%EB%A9%B4&quot; aria-label=&quot;인덱스가 없었더라면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스가 없었더라면?&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7828b570-cbe8-4869-ab86-4de2d7b90144/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;조금 더 원초적인 질문으로 들어가봅시다. 만약에 인덱스가 없는 경우에 &quot;민서&quot; 라는 데이터를 스캔하는 조회문을 실행할 경우 어떻게 동작할까요?&lt;/p&gt;
&lt;p&gt;위와같이 3개의 페이지가 있다고 해봅시다. 이는 클러스터나 논 클러스터 인덱스를 적용하지 않은 상태입니다. 그렇다면 디스크의 맨 첫번째 페이지부터 시작해서 순차적으로 모든 레코드를 스캔해오는 &lt;code class=&quot;language-text&quot;&gt;데이터 풀 스캔(Full Table Scan)&lt;/code&gt; 이 발생하기 떄문에 &lt;code class=&quot;language-text&quot;&gt;O(n)&lt;/code&gt; 의 시간이 소요될 것입니다. 이 때문에 인덱스가 등장한 것이죠. 특정 컬럼을 인덱스로 관리함으로써 &lt;code class=&quot;language-text&quot;&gt;O(logbN)&lt;/code&gt; 이라는 더 효율적인 조회문을 실행한다는 것입니다. 그래서 인덱싱의 종류로 등장한 것이 &lt;code class=&quot;language-text&quot;&gt;클러스터링 인덱스&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;논 클러스터링 인덱스&lt;/code&gt;입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;클러스터링-인덱스-clustering-index&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4-clustering-index&quot; aria-label=&quot;클러스터링 인덱스 clustering index permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클러스터링 인덱스 (Clustering Index)&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/190cb1bf-4b0d-4132-95d5-9754cb2741e0/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&quot;클러스터링&quot; 이란 &lt;code class=&quot;language-text&quot;&gt;군집화&lt;/code&gt;의 의미와도 같습니다. 클러스터링 인덱스란 &lt;strong&gt;B+ Tree 구조에서 리프 노드가 실제 데이터와 별도로 분리되지 않고 함께 군집화되어 인덱싱되는 방식&lt;/strong&gt;입니다. 데이터와 함께 전체 테이블이 함께 정렬됩니다.&lt;/p&gt;
&lt;p&gt;테이블에 중복되는 값이 있어서는 안되며, &lt;strong&gt;이 때문에 &lt;code class=&quot;language-text&quot;&gt;기본키(primary key)&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;UNIQUE+NOT NULL&lt;/code&gt; 제약조건이 걸린 특정 컬럼에 대해서만 인덱스를 생성가능합니다.&lt;/strong&gt; 만약 기본키와 UNIQUE 제약조건이 걸린 컬럼에 대해서 별도로 명시적으로 인덱스를 생성하지 않았더라도 자동으로 생성됩니다. 만약 테이블에 위 조건을 만족하는 컬럼이 없다면, InnoDB 엔진은 내부적으로 &lt;code class=&quot;language-text&quot;&gt;GEN_CLUST_INDEX&lt;/code&gt; 라는 컬럼을 생성해서 클러스터형 인덱스를 생성합니다. &lt;code class=&quot;language-text&quot;&gt;GEN_CLUST_INDEX&lt;/code&gt; 는 행이 생성된 순서대로 값이 부여됩니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;각 루트 및 브랜치 노드는 인덱스 컬럼 값(ex. PK값) 을 보유하며, 리프 노드(실제 레코드) 를 가리키고 있는 형태가됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;innodb-엔진의-디폴트-클러스터링-인덱스-선택방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#innodb-%EC%97%94%EC%A7%84%EC%9D%98-%EB%94%94%ED%8F%B4%ED%8A%B8-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EC%84%A0%ED%83%9D%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;innodb 엔진의 디폴트 클러스터링 인덱스 선택방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;InnoDB 엔진의 디폴트 클러스터링 인덱스 선택방식&lt;/h3&gt;
&lt;p&gt;위 예시를 보면 id 컬럼은 대학교 학생들의 각 학번이라고 해봅시다. 이 컬럼을 PK 로 설정해 클러스터링 인덱스를 생성한 모습이라고 이해하면 될것입니다. 걸론적으로, InnoDB 엔진에서는 &lt;strong&gt;리프 페이지에서 기본적으로 key 값으로 PK 를 가지고 있고, 데이터를 직접 들고있는 모습&lt;/strong&gt;입니다. 즉 리프 노드는 실제 데이터 레코드 그 자체로 동일한 객체입니다. 또, InnoDB 엔진에서는 직접 들고있는 이 레코드들을 &lt;code class=&quot;language-text&quot;&gt;PK 값&lt;/code&gt; 을 기준으로 순차정렬하고, 군집화하는 형태를 지닙니다.&lt;/p&gt;
&lt;p&gt;InnoDB 는 아래와 같은 기준에따라 우선순위를 클러스터링 인덱스를 선정하니, 참고하기릴 바랍니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;primary key 가 있으면 1순위로 primary key 를 클러스터링 인덱스로 선택&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;pk가 없다면, UNIQUE + NOT NULL 제약조건이 걸맄 컬럼을 선택&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;둘다 없다면, &quot;GEN_CLUST_INDEX&quot; 이라는 자동으로 유니크한 값을 가지도록 증가되는 컬럼을 내부적으로 추가한 후, 클러스터링 인덱스로 선택 (쿼리에서 명시적으로 사용 불가능)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;비클러스터링-인덱스-non-clustering-index&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4-non-clustering-index&quot; aria-label=&quot;비클러스터링 인덱스 non clustering index permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비클러스터링 인덱스 (Non-Clustering Index)&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c7d79044-5686-4174-8787-09c1ea8e8d57/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;비 클러스터링 인덱스와 클러스터링 인덱스의 차이는 리프노드가 실제 레코드를 군집화해서 저장하는가, 아니면 따로 저장하는가에 있습니다. 비 클러스터링 인덱스는 실제 레코드를 따로 관리(저장)하는데, &lt;strong&gt;인덱스와 분리된 실제 레코드를 포인터로 가리키는 방식입니다.&lt;/strong&gt; 즉, 리프 페이지에는 인덱스 컬럼값외에도 실제 레코드의 주솟값을 저장하고 있습니다.&lt;/p&gt;
&lt;p&gt;한 컬럼에 &lt;code class=&quot;language-text&quot;&gt;UNIQUE&lt;/code&gt; 제약조건을 걸면 자동 생성 가능하며, 또는 개발자가 직접 &lt;code class=&quot;language-text&quot;&gt;UNIQUE INDEX &lt;/code&gt; 쿼리로 중복을 허용하지 않으면서 인덱스를 직접 생성 가능합니다. 이때 UNIQUE 옵션을 빼면 중복이 허용된다는점에 유의합시다.&lt;/p&gt;
&lt;p&gt;인덱스 페이지는 정렬되어 있지만, 실제 데이터 페이지는 정렬되지 않으므로 클러스터형 인덱스에 비해 삽입, 수정, 삭제 작업이 비교적 빠릅니다. 데이터 페이지에는 정렬 순서 상관없이 빈 곳에 데이터를 삽입하면 되기 때문이죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 논 클러스터링 인덱스 생성 예시&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; Member
&lt;span class=&quot;token keyword&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;CONSTRAINT&lt;/span&gt; uniq_name &lt;span class=&quot;token keyword&quot;&gt;UNIQUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;UNIQUE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; unq_idx_name
&lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; Member &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; idx_name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// =&gt; 중복 허용&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; Member &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;멀티-인덱스--클러스터링-인덱스와-비클러스터링-인덱스의-혼합된-형태&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%80%ED%8B%B0-%EC%9D%B8%EB%8D%B1%EC%8A%A4--%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EC%99%80-%EB%B9%84%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EC%9D%98-%ED%98%BC%ED%95%A9%EB%90%9C-%ED%98%95%ED%83%9C&quot; aria-label=&quot;멀티 인덱스  클러스터링 인덱스와 비클러스터링 인덱스의 혼합된 형태 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;멀티 인덱스 : 클러스터링 인덱스와 비클러스터링 인덱스의 혼합된 형태&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/8e83ac03-aec0-482b-8ae9-e58ac05c7859/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그런데 현실적으로는 기본적으로 생성되는 &lt;code class=&quot;language-text&quot;&gt;클러스터링 인덱스&lt;/code&gt; 외에도 &lt;code class=&quot;language-text&quot;&gt;논 클러스터링 인덱스&lt;/code&gt; 가 생성되는 경우가 대다수입니다. 즉, 하나에 테이블에 클러스터형 인덱스와 논 클러스터링 인덱스가 혼합되어 있는 경우가 많죠. 클러스터링 인덱스는 대부분 PK 일텐데 PK 는 기본적으로 대부분의 테이블에 반드시 존재하며, 추가적으로 조회가 자주 발생하는 컬럼에 대해 개발자가 새로운 논 클러스터링 인덱스를 추가하기 때문입니다.&lt;/p&gt;
&lt;p&gt;이런 경우에는 &lt;strong&gt;논 클러스터링 인덱스를 먼저 거치고, 이어 클러스터형 인덱스를 거쳐 데이터를 찾습니다.&lt;/strong&gt; 이때, &lt;strong&gt;비클러스터링 인덱스의 리프노드는 실제 레코드에 대한 주솟값 대신 클러스터링 인덱스에 대한 컬럼 값(PK 값)을 갖습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;또 유심히보면, 클러스터링 인덱스는 B+ Tree 트리의 구조를 이루기 때문에, InnoDB 엔진에서 조회문을 실행시 &lt;strong&gt;primary key 값을 활용해서 B+ Tree 구조에서 실제 레코드에 접근하는 흐름&lt;/strong&gt;으로 처리된다고 이해하시면 됩니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;정리)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;InnoDB 스토리지 엔진은 논 클러스터링 인덱스에서 클러스터링 인덱스로 넘어가는 형태이다.&lt;/li&gt;
&lt;li&gt;논 클러스터링 인덱스의 리프노드는 PK 값을 저장하고있다. 이 PK 값에 해당하는 클러스터링 페이지를 특정 셀(실제 레코드)을 찾아내서, 그 실제 레코드로부터 원하는 컬럼 값을 조회하는 방식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;바로 위에서 정리한 내용중에 논 클러스터링 인데스의 리프노드는 &quot;PK 값&quot; 을 가지고 조회한다고 했는데, 정확히는 클러스터링 인덱스의 컬럼값이라고 보는게 맞긴합니다. 앞서 설명하길, 클러스터링 인덱스는 PK 값 외에도 unique 제약조건을 가진 컬럼이 될수도 있다고 했기 때문에, 반드시 PK 값이라고도 정의내릴 순 없습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;다중-컬럼-인덱스-multi-column-index&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%A4%91-%EC%BB%AC%EB%9F%BC-%EC%9D%B8%EB%8D%B1%EC%8A%A4-multi-column-index&quot; aria-label=&quot;다중 컬럼 인덱스 multi column index permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다중 컬럼 인덱스 (Multi Column Index)&lt;/h2&gt;
&lt;p&gt;추가적으로, 학습을 하면서 햇갈렸던점을 하나 정리해보고자 합니다.
우선 &lt;code class=&quot;language-text&quot;&gt;다중 컬럼 인덱스(Multi Column Index)&lt;/code&gt; 란 개발자가 인덱스를 생성시 2개 이상의 여러 컬럼으로 생성해낸 인덱스입니다.&lt;/p&gt;
&lt;p&gt;예를들어 &lt;code class=&quot;language-text&quot;&gt;Member&lt;/code&gt; 라는 테이블이 있다고해봅시다. 이 테이블의 PK 는 &lt;code class=&quot;language-text&quot;&gt;member_id&lt;/code&gt; 이며, 그외의 컬럼으로는 &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt; , &lt;code class=&quot;language-text&quot;&gt;std_id&lt;/code&gt; , &lt;code class=&quot;language-text&quot;&gt;grade&lt;/code&gt; 가 있다고해봅시다. 어떤 경우가 클러스터링/논 클러스터링 인덱스만 존재하며, 또는 어떤 경우가 혼합된 멀티 인덱스가 존재하는 경우일까요? 또 어떤 경우가 다중 컬럼 인덱스일까요? 아래와 같이 개발자가 인덱스를 생성하는 5가지 경우를 생각해봅시다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;(name, std_id)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;(name, grade)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;(member_id, name, std_id)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;(name, member_id)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;(name)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;우선 member_id 에 대한 클러스터링 인덱스가 자동으로 생성되었을 겁니다.
또 5가지 경우 모두 논 클러스터링 인덱스가 생성되는 경우로 생각할 수 있습니다. 앞서 언급했듯이, 직접 인덱스를 생성하는 경우는 모두 논 클러스터링 인덱스임을 잊지맙시다.&lt;/p&gt;
&lt;p&gt;위 케이스는 모두 &quot;멀티 인덱스&quot;가 됩니다. 즉, 기존에 자동으로 생성된 PK 기반의 클러스터링 인덱스에 위와 같은 논 클러스터링 인덱스를 개발자가 직접 추가해주었으니, 멀티 인덱스 형태가 되는 것입니다.&lt;/p&gt;
&lt;p&gt;또 5번을 제외하면 &quot;다중 컬럼 인덱스(Multi Column Index)&quot; 에 모두 해당됩니다. 5번의 경우는 2개 이상의 컬럼을 활용하지 않았기 떄문에 해당되지 않습니다.&lt;/p&gt;
&lt;h3 id=&quot;비효율적인-인덱스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%9D%B8%EB%8D%B1%EC%8A%A4&quot; aria-label=&quot;비효율적인 인덱스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비효율적인 인덱스&lt;/h3&gt;
&lt;p&gt;또 위 인덱스에서 어떤 인덱스가 비효율적으로 생성되었는지 생각해봤을 때, PK 를 포함하는 3번과 4번이 해당됩니다. PK 는 이미 클러스터링 인덱스를 통해 생성되었으므로 인덱스로 조회 가능한데도, 굳이 논 클러스터링 인덱스 (다중 컬림 인덱스) 에서 또 PK 값을 포함해서 새로운 인덱스를 중복해서 생성할 필요는 없습니다. &lt;code class=&quot;language-text&quot;&gt;커버링 인덱스&lt;/code&gt; 와 같은 혜택을 누리는 것도, 이렇게 중복 생성을 하지 않고도 누릴 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;옵티마이저&lt;/li&gt;
&lt;li&gt;파티셔닝과 샤딩&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Real MySQL 8.0 (백은빈, 이성욱 지음)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=edpYzFgHbqs&quot;&gt;https://www.youtube.com/watch?v=edpYzFgHbqs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gilssang97.tistory.com/52&quot;&gt;https://gilssang97.tistory.com/52&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jaehoney.tistory.com/57&quot;&gt;https://jaehoney.tistory.com/57&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/db-clustered-and-non-clustered-index/&quot;&gt;https://hudi.blog/db-clustered-and-non-clustered-index/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL의 B+ Tree 구조 기반 인덱스 스캔 방식]]></title><description><![CDATA[[MySQL 8.0] InnoDB 스토리지 엔진에서의 B+ Tree 인덱스를 통한 레코드 스캔 구조 에셔 다루었듯이, MySQL 8.0 버전부터 InnoDB 엔진에 기반하여 가동되며 인덱스는 B+ Tree…]]></description><link>https://haon.site/haon/index/scan-type/</link><guid isPermaLink="false">https://haon.site/haon/index/scan-type/</guid><pubDate>Wed, 14 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/MySQL-8.0-InnoDB-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80-%EC%97%94%EC%A7%84%EC%97%90%EC%84%9C%EC%9D%98-B-Tree-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%ED%86%B5%ED%95%9C-%EB%A0%88%EC%BD%94%EB%93%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%8A%A4%EC%BA%94-%EC%B5%9C%EC%A0%81%ED%99%94-%EA%B5%AC%EC%A1%B0&quot;&gt;[MySQL 8.0] InnoDB 스토리지 엔진에서의 B+ Tree 인덱스를 통한 레코드 스캔 구조&lt;/a&gt; 에셔 다루었듯이, MySQL 8.0 버전부터 InnoDB 엔진에 기반하여 가동되며 인덱스는 B+ Tree 로 구현되어 있습니다.&lt;/p&gt;
&lt;p&gt;이때 어떤 경우에 인덱스를 사용하게 유도할지, 또는 사용하지 못하게 할지 판단하려면 MySQL이 어떻게 인덱스를 활용해서 실제 레코드를 조회하는지 알아야합니다. 그에 대한 대표적인 스캔 방식으로 3가지가 존재합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;인덱스-레인지-스캔-index-range-scan&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EB%A0%88%EC%9D%B8%EC%A7%80-%EC%8A%A4%EC%BA%94-index-range-scan&quot; aria-label=&quot;인덱스 레인지 스캔 index range scan permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스 레인지 스캔 (Index Range Scan)&lt;/h2&gt;
&lt;p&gt;인덱스 레인지 스캔이란 &lt;strong&gt;검색해야 할 인덱스의 범위가 결정됐을 때 사용되는 방식&lt;/strong&gt;입니다. 인덱스 레인지 스캔방식은 다음 3가지 과정에 따라 수행됩니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;1.인덱스 탐색(Index Seek) : 인덱스에서 조건을 만족하는 값이 저장된 위치를 루트에서부터 리프까지 내려가며 탐색한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;2.인덱스 스캔(Index Scan) : 앞서 탐색된 리프노드 위치에서부터 필요한만큼 인덱스를 차례대로 쭉 스캔한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;3.레코드 조회 : 앞선 스캔 과정으로 읽어들인 리프노드의 key 값과 레코드 주솟값으로 레코드가 저장된 페이지를 가져오고, 최종 레코드를 읽어온다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;B+ Tree&lt;/code&gt; 의 인덱스 구조에 따라, 루트 노드에서부터 시작해서 브랜치 노드를 걸쳐서 원하는 특정 리프 노드까지 찾아 들어가서 레코드의 스캔 시작지점을 찾을 수 있게됩니다. 각 리프노드는 링크드리스트로 연결되어 있으므로, 만약 스캔을 하다 한 리프노드의 끝까지 스캔하면 다음 노드로 넘어가서 스캔을 계속 이어나갑니다.&lt;/p&gt;
&lt;h3 id=&quot;random-io-를-고려한-스캔-여부-결정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#random-io-%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%9C-%EC%8A%A4%EC%BA%94-%EC%97%AC%EB%B6%80-%EA%B2%B0%EC%A0%95&quot; aria-label=&quot;random io 를 고려한 스캔 여부 결정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Random I/O 를 고려한 스캔 여부 결정&lt;/h3&gt;
&lt;p&gt;이때 리프 노드에서 쿼리문의 검색 조건에 일치하는 데이터를 찾으면 실제 데이터 파일에서 레코드를 일거오는 과정이 필요한데, 이때 레코드 한건 단위를 읽어올때마다 디스크 &lt;code class=&quot;language-text&quot;&gt;랜덤 I/O&lt;/code&gt; 가 발생하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/MySQL-8.0-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EC%BF%BC%EB%A6%AC-%EC%84%B1%EB%8A%A5-%ED%8A%9C%EB%8B%9D%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%9E%9C%EB%8D%A4-IO-%EC%99%80-%EC%88%9C%EC%B0%A8-IO&quot;&gt;[MySQL 8.0] 데이터베이스의 쿼리 성능 튜닝을 위한 랜덤 I/O 와 순차 I/O&lt;/a&gt; 에서도 언급했듯이, 랜덤 I/O 는 비용이 꽤 많이 발생하는 작업입니다. 따라서 인덱스 레인지 스캔은 상황에 따라 활용여부를 결정해야 하는데, 이 기준은 읽어야할 레코드가 20~25% 를 넘으면 테이블의 데이터를 직접 읽는것이 더 효율적인 처리 방식이 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;실행계획execution-plan-을-통해-확인해보기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%ED%96%89%EA%B3%84%ED%9A%8Dexecution-plan-%EC%9D%84-%ED%86%B5%ED%95%B4-%ED%99%95%EC%9D%B8%ED%95%B4%EB%B3%B4%EA%B8%B0&quot; aria-label=&quot;실행계획execution plan 을 통해 확인해보기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실행계획(Execution Plan) 을 통해 확인해보기&lt;/h3&gt;
&lt;p&gt;실행 계획을 통해 실제로 인덱스 레인지 스캔일 발생하는지를 직접 확인해봅시다.
&lt;code class=&quot;language-text&quot;&gt;실행 계획(Execution Plan)&lt;/code&gt;이란 MySQL 의 &lt;code class=&quot;language-text&quot;&gt;옵티마이저(Optimizer)&lt;/code&gt; 가 쿼리를 최적화하여 처리할 계획을 나타낸 정보로, &lt;code class=&quot;language-text&quot;&gt;EXPLAIN&lt;/code&gt; 명령어를 통해 계획을 확인할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;alter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;index&lt;/span&gt; user_em &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;우선 위와같이 email 컬럼에 대해 user_em 이라는 인덱스를 생성했습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;explain&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; password &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; email &lt;span class=&quot;token operator&quot;&gt;between&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;m&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;z&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러고 between 으로 email 컬럼에 대한 인덱스에 대해 레인지 스캔이 발생하도록 유도해봤습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a406974e-68c7-42e1-9086-71f6bccfa290/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그 결과는 위와 같이 type 이 range 임을 확인하 수 있습니다. 즉, 실행계획을 직접보니, 위 쿼리는 정상적으로 레인지 스캔이 수행될 것임을 확인할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;커버링-인덱스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EB%B2%84%EB%A7%81-%EC%9D%B8%EB%8D%B1%EC%8A%A4&quot; aria-label=&quot;커버링 인덱스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커버링 인덱스&lt;/h3&gt;
&lt;p&gt;SELECT, WHERE, ORDER BY 등과 같은 조회 쿼리문에 현재 인덱스로 활용되고있는 모든 컬럼이 담겨있는 경우가 있습니다. 즉, &lt;strong&gt;쿼리를 실행시킬 수 있는 데이터를 인덱스가 모두 가지고 있는 인덱스&lt;/strong&gt;를 커버링 인덱스라고 합니다.&lt;/p&gt;
&lt;p&gt;예를들어 아래와 같이 Member 테이블이 있을때, 프라이머리 키인 member_id 가 있고 email 이라는 컬럼이 모두 인덱스로 등록되었다고 할때, 이 두 컬럼은 커버링 인덱스가 되는것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; member_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; email
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; Member&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;커버링 인덱스로 처리되는 인덱스는 디스크의 레코드는 랜덤 I/O 가 발생하지 않습니다.&lt;/strong&gt; 조회를 원하던 데이터들이 모두 인덱스에 담겨있기 떄문에, 굳이 디스크를 접근하지 않고도 조회가 가능하기 때문이죠. 이 떄문에 랜덤 I/O 가 상당히 줄어들고 성능이 그만큼 빨라집니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;인덱스-풀-스캔-index-full-scan&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4-%ED%92%80-%EC%8A%A4%EC%BA%94-index-full-scan&quot; aria-label=&quot;인덱스 풀 스캔 index full scan permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스 풀 스캔 (Index Full Scan)&lt;/h2&gt;
&lt;p&gt;인덱스 레인지 스캔처럼 범위 탐색을 하지만, &lt;strong&gt;인덱스 처음부터 끝까지 전부를 스캔하는 방식&lt;/strong&gt;을 인덱스 풀 스캔이라고 합니다. &lt;strong&gt;쿼리의 조건절이 인덱스의 첫 컬럼이 아닌 경우&lt;/strong&gt;에 이 방식이 적용됩니다.&lt;/p&gt;
&lt;p&gt;예를들어 인덱스를 (name, email, password) 로 설정해줬는데, 조건절은 email 또는 password 로 사용되는 경우입니다. 또는 &lt;code class=&quot;language-text&quot;&gt;COUNT&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ORDER BY&lt;/code&gt; 와 같이 전체 테이블을 조회해야 하는 경우도 마찬가지로 인덱스 풀 스캔이 유리하기 때문에, 이 방식이 사용됩니다.&lt;/p&gt;
&lt;h3 id=&quot;인덱스-풀-스캔의-발생조건&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4-%ED%92%80-%EC%8A%A4%EC%BA%94%EC%9D%98-%EB%B0%9C%EC%83%9D%EC%A1%B0%EA%B1%B4&quot; aria-label=&quot;인덱스 풀 스캔의 발생조건 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스 풀 스캔의 발생조건&lt;/h3&gt;
&lt;p&gt;이 경우는 쿼리가 인덱스에 명시된 컬럼만으로 처리할 수 있는 &lt;code class=&quot;language-text&quot;&gt;커버링 인덱스&lt;/code&gt; 일때만 사용됩니다. 인덱스에 있는 컬럼만으로 처리할 수 없는 경우, 인덱스 풀 스캔이 사용되면 레코드마다 랜덤 I/O 가 발생해서 비효율적이기 떄문에 절대로 이 방식이 사용되지 않습니다. 즉, 커버링 인덱스가 아니라면 &lt;code class=&quot;language-text&quot;&gt;테이블 풀 스캔&lt;/code&gt; 을 수행합니다.&lt;/p&gt;
&lt;h3 id=&quot;실행계획으로-직접-확인해보기-1--인덱스-풀-스캔&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%ED%96%89%EA%B3%84%ED%9A%8D%EC%9C%BC%EB%A1%9C-%EC%A7%81%EC%A0%91-%ED%99%95%EC%9D%B8%ED%95%B4%EB%B3%B4%EA%B8%B0-1--%EC%9D%B8%EB%8D%B1%EC%8A%A4-%ED%92%80-%EC%8A%A4%EC%BA%94&quot; aria-label=&quot;실행계획으로 직접 확인해보기 1  인덱스 풀 스캔 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실행계획으로 직접 확인해보기 (1) : 인덱스 풀 스캔&lt;/h3&gt;
&lt;p&gt;정말로 풀 스캔이 걸리는지 직접 확인해봅시다. 우선 아래와 같이 3개의 컬럼을 포함하는 인덱스를 새롭게 생성해주었습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; all_column_index &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러고 첫번째 컬럼이 아닌 3번째 컬럼을 조건절에 넣고 조회문을 실행시켜봤습니다. 또한 select 로 추출하려는 컬럼은 커버링 인덱스에 해당됨을 짚고 넘어갑시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;EXPLAIN&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; user_id &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;12345678&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그 결과는 아래와 같이 type 에 &lt;code class=&quot;language-text&quot;&gt;index&lt;/code&gt; 가 걸린것을 볼 수 있습니다. 이때 index 란 인덱스 풀 스캔이 걸린것을 의미하니, 당황하지 맙시다. 이렇게 인덱스의 모든 범위를 탐색하게 된 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4fa75899-fb48-4d9e-8353-7a90a1bc094c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;실행계획으로-직접-확인해보기-2--풀-테이블-스캔full-table-scan&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%ED%96%89%EA%B3%84%ED%9A%8D%EC%9C%BC%EB%A1%9C-%EC%A7%81%EC%A0%91-%ED%99%95%EC%9D%B8%ED%95%B4%EB%B3%B4%EA%B8%B0-2--%ED%92%80-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%8A%A4%EC%BA%94full-table-scan&quot; aria-label=&quot;실행계획으로 직접 확인해보기 2  풀 테이블 스캔full table scan permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실행계획으로 직접 확인해보기 (2) : 풀 테이블 스캔(Full Table Scan)&lt;/h3&gt;
&lt;p&gt;앞선 방식은 select 로 추출하려는 컬럼이 인덱스에 포함된 커버링 인덱스입니다. 그런데, 만약 이 컬럼이 쿼리에서 인덱스를 구성하지 있지 않은 컬럼인 경우라면 어떻게 될까요? 아래처럼 인덱스에 포함되지 않은 gender 컬럼을 쿼리에 포함하여 실행계획을 살펴봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; ix_gender_birthdate&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gender&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; birth_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;우선 위처럼 인덱스를 새롭게 만들어주겠습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;EXPLAIN&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; password &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; birth_date &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;1998-01-01&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러고 커버링 인덱스에 해당되지 않는 password 컬럼을 조회해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/79f1d790-6723-4973-b84d-5f740fe0378d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그런데 실행결과를 보면 type 이 index 가 아니라 &lt;code class=&quot;language-text&quot;&gt;ALL&lt;/code&gt; 임을 확인할 수 있습니다. ALL 은 &lt;code class=&quot;language-text&quot;&gt;테이블 풀 스캔&lt;/code&gt; 이 발생했음을 의미합니다. 즉, &lt;strong&gt;커버링 인덱스가 아닌 경우는 옵티마이저가 인덱스 풀 스캔보다 테이블 풀 스캔이 효율적이라고 판단&lt;/strong&gt;하게 딥니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;루스-인덱스-스캔-loose-index-scan&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A3%A8%EC%8A%A4-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EC%8A%A4%EC%BA%94-loose-index-scan&quot; aria-label=&quot;루스 인덱스 스캔 loose index scan permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;루스 인덱스 스캔 (Loose Index Scan)&lt;/h2&gt;
&lt;p&gt;말그래도 루스 인덱스는 느슨느슨하게 인덱스를 읽는것을 의미합니다. 이 방식은 &lt;strong&gt;중간에 필요치 않은 인덱스 키 값은 무시(skip) 하고 다음으로 넘어가는 형태로 처리&lt;/strong&gt;합니다. 일반적으로 &lt;code class=&quot;language-text&quot;&gt;GROUP BY&lt;/code&gt; 또는 집합 함수 가운대 &lt;code class=&quot;language-text&quot;&gt;MAX()&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;MIN()&lt;/code&gt; 함수에 대해 최적화를 하는 경우에 사용됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; user_number&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MIN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; user_number &lt;span class=&quot;token operator&quot;&gt;between&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; user_number&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 User 테이블은 &lt;code class=&quot;language-text&quot;&gt;(user_number, email)&lt;/code&gt; 인덱스가 생성되었다고 해봅시다. &lt;code class=&quot;language-text&quot;&gt;GROUP_BY&lt;/code&gt; 를 통해 user_number 컬럼으로 그룹화를 할것이며, &lt;code class=&quot;language-text&quot;&gt;MIN&lt;/code&gt; 집계함수로 그룹별로 email 이 최솟값인 것을 가져올것입니다.&lt;/p&gt;
&lt;p&gt;이때 인덱스는 &lt;code class=&quot;language-text&quot;&gt;(user_number, email)&lt;/code&gt; 조합으로 이미 자동정렬 된 상태이기 때문에, user_number 그룹별로 첫번째 레코드의 email 값만 읽어오면 됩니다. 즉, 옵티마이저는 인덱스에서 WHERE 조건을 만족하는 모든 범위 전체를 싹다 읽어올 필요가 없다는 것을 알고있기 때문에, 조건에 만족하지 않는 레코드는 그냥 무시하고 다음 레코드를 읽어옵니다.&lt;/p&gt;
&lt;p&gt;위 상황에서는 user_number 로 그룹화된 user_number = 1,2,3 인 각 그룹에서 email 값으로 최솟값을 가지는 맨 앞의 레코드만 읽어오는 방식입니다.&lt;/p&gt;
&lt;h3 id=&quot;실행계획으로-직접-확인하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%ED%96%89%EA%B3%84%ED%9A%8D%EC%9C%BC%EB%A1%9C-%EC%A7%81%EC%A0%91-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0&quot; aria-label=&quot;실행계획으로 직접 확인하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실행계획으로 직접 확인하기&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;alter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;index&lt;/span&gt; user_id_email&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user_number&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 인덱스를 만들어줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;EXPLAIN&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; user_number&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MIN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; user_number &lt;span class=&quot;token operator&quot;&gt;between&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; user_number&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러고 위처럼 쿼리의 실행계획에 대한 실행결과를 보면, type 이 &lt;code class=&quot;language-text&quot;&gt;range&lt;/code&gt; 로 지정됩니다. 그런데 &lt;code class=&quot;language-text&quot;&gt;Extra&lt;/code&gt; 를 살펴보면 &lt;code class=&quot;language-text&quot;&gt;Using index for group-by&lt;/code&gt; 가 걸린것을 볼 수 있는데, 이는 루스 인덱스 스캔이 적용되었음을 의미합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;인덱스-스킵-스캔-index-skip-scan&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EC%8A%A4%ED%82%B5-%EC%8A%A4%EC%BA%94-index-skip-scan&quot; aria-label=&quot;인덱스 스킵 스캔 index skip scan permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스 스킵 스캔 (Index Skip Scan)&lt;/h2&gt;
&lt;p&gt;앞서 &lt;code class=&quot;language-text&quot;&gt;인덱스 풀 스캔&lt;/code&gt;에서 다루었기를, 인덱스의 첫번째 컬럼이 조건절에 해당하지 않으면 인덱스를 타지 않는다고 했었습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; idx_gen_bir&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gender&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; birth_date&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 새로운 인덱스를 추가하고&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; gender&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;M&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; birth_date&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;1999-01-01&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; birth_date &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;1999-01-01&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같은 두 쿼리를 실행시켜본다고 해봅시다. 그렇다면 쿼리(1) 의 경우는 인덱스의 첫번째 컬럼 birth_date 를 WHERE 조건절에서 활용하고 있기 때문에 인덱스를 타게 될겁니다. 그런데, 쿼리(2) 는 조건절에 첫번째 컬럼이 존재하지 않기 때문에 인덱스를 타지 못하게 될것 가습니다. gender 로 시작하는 새로운 인덱스를 만들어서 실행해야 할것만 같죠.&lt;/p&gt;
&lt;p&gt;그런데, 생각과 달리 MySQL 옵티마이저는 인덱스 스킵 스캔이라는 최적화 기능을 도입했습니다. 위 쿼리를 아래와 같이 두개의 쿼리로 최적화하여 실행해줍니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; gender&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; employee &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; gender&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;M&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; birth_date &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;1998-01-01&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; gender&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; employee &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; gender&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;F&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; birth_date &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;1998-01-01&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 필요에따라 컬럼을 추가해주는 방식이 바로 인덱스 스킵 입니다. 그런데, 아직 MySQL 8.0 에 도입된지 얼마 안된 방식인지라 아래와 같은 단점이 존재합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;WHERE 조건절에 조건이 없는 인덱스의 선행 컬럼의 유니크한 값의 개수가 적어야함&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;커버링 인덱스&lt;/code&gt; 이여야한다. 즉, 쿼리가 인덱스에 존재하는 컬럼만으로 처리가 가능해야 합니다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Real MySQL 8.0 (백은빈, 이성욱 지음)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/286&quot;&gt;[MySQL] B-Tree로 인덱스(Index)에 대해 쉽고 완벽하게 이해하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gywn.net/2012/04/mysql-covering-index/&quot;&gt;MySQL에서 커버링 인덱스로 쿼리 성능을 높여보자!!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;#x26;blogId=gglee0127&amp;#x26;logNo=221336088285&quot;&gt;인덱스 스캔 방식 종류 및 특징&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@mu1616/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%9D%B8%EB%8D%B1%EC%8A%A4-Index&quot;&gt;데이터베이스 인덱스 (Index)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[MySQL B+Tree 구조를 통한 인덱스(Index) 연산 방식]]></title><description><![CDATA[인덱스 (Index…]]></description><link>https://haon.site/haon/index/concept/</link><guid isPermaLink="false">https://haon.site/haon/index/concept/</guid><pubDate>Tue, 13 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;인덱스-index&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4-index&quot; aria-label=&quot;인덱스 index permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스 (Index)&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;인덱스(Index)&lt;/code&gt; 란 데이터베이스에서 원하는 데이터를 빠르게 검색하고 엑세스하기 위해 사용되는 자료구조입니다. 정확히는, 이 자료구조 내에서 각 단위를 인덱스라고 지칭하는 것이며, 여러 종류의 인덱스 중에서 추후 살펴볼 &lt;code class=&quot;language-text&quot;&gt;노드(node)&lt;/code&gt; 가 데이터 레코드(실제 데이터) 를 포인터로 가리키는 형태입니다. 이때 대부분의 DBMS 에서는 인덱스에 대한 저장방식으로 &lt;code class=&quot;language-text&quot;&gt;B-Tree&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;B+Tree&lt;/code&gt; , 그리고 &lt;code class=&quot;language-text&quot;&gt;Hash&lt;/code&gt; 자료구조를 채택하고 있습니다.&lt;/p&gt;
&lt;p&gt;각 인덱스는 데이터 레코드의 특정 컬럼을 타킷으로 해서, 컬럼의 값과 해당 레코드가 저장된 주소(포인터)를 key-value 쌍으로 저장하고 있습니다.&lt;/p&gt;
&lt;p&gt;정확히는, 이 자료구조 내에서 각 단위를 인덱스라고 지칭하는 것이며, 여러 종류의 인덱스 중에서 추후 살펴볼 &lt;code class=&quot;language-text&quot;&gt;노드(node)&lt;/code&gt; 가 데이터 레코드(실제 데이터) 를 포인터로 가리키는 형태입니다. 이때 대부분의 DBMS 에서는 인덱스에 대한 저장방식으로 &lt;code class=&quot;language-text&quot;&gt;B-Tree&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;B+Tree&lt;/code&gt; , 그리고 &lt;code class=&quot;language-text&quot;&gt;Hash&lt;/code&gt; 자료구조를 채택하고 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;조회select-를-위한-최적화-구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A1%B0%ED%9A%8Cselect-%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%B5%9C%EC%A0%81%ED%99%94-%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;조회select 를 위한 최적화 구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;조회(select) 를 위한 최적화 구조&lt;/h3&gt;
&lt;p&gt;인덱스는 &lt;code class=&quot;language-text&quot;&gt;B+ Tree&lt;/code&gt; 와 같은 저장방식을 취함으로써 항상 정렬된 상태를 유지합니다. 반면 인덱스들이 가리키는 데이터 파일의 레코드들은 별도의 순서 정렬없이 데이터가 유입되는대로 곧바로 저장합니다. 떄문에 &lt;strong&gt;DBMS 상에서 인덱스는 데이터의 저장(insert, delete, update)의 성능을 희생한 대신에, 조회(select) 의 성능을 최적화한 구조&lt;/strong&gt;라고 볼 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;b-tree-index&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#b-tree-index&quot; aria-label=&quot;b tree index permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;B-Tree Index&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/56f352c6-0a3c-486d-a36d-3e20987a5034/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;B-Tree 는 이진트리와 달리 자식 노드를 여러개 가질 수 있는 균형잡힌(Balanced) 트리 구조입니다. 가장 최상위에는 하나의 &lt;code class=&quot;language-text&quot;&gt;루트 노드(root node)&lt;/code&gt; 가 존재하며, 맨 하위 계층에는 &lt;code class=&quot;language-text&quot;&gt;리프 노드(leaf node)&lt;/code&gt; 가 존재하며, 이들 사이에 있는 중간노드로 &lt;code class=&quot;language-text&quot;&gt;브랜치 노드(branch node)&lt;/code&gt; 가 존재합니다. 여기서 key 값이란, 실제 데이터 레코드의 key 값을 의미하는 것으로, 각 레코드를 식별할 수 있는 (중복되지 않는) 값을 지닌 컬럼의 값을 의미합니다. 대표적으로 &lt;code class=&quot;language-text&quot;&gt;프라이머리 키(primary key)&lt;/code&gt; 값이 이에 해당되겠죠.&lt;/p&gt;
&lt;p&gt;바로 뒤에 살펴볼 B+ Tree 와 다른점은 &lt;code class=&quot;language-text&quot;&gt;리프노드&lt;/code&gt; 에서 가장 큰 차이점을 보입니다. B+ Tree 의 리프노드는 각 노드끼리 링크드리스트로 연결된 형태이나, B-Tree 는 그렇지 않습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;b-tree-index-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#b-tree-index-1&quot; aria-label=&quot;b tree index 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;B+ Tree Index&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/dda72d71-7323-4205-a903-cbeca57ca581/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;b--tree-와의-차이점--리프노드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#b--tree-%EC%99%80%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90--%EB%A6%AC%ED%94%84%EB%85%B8%EB%93%9C&quot; aria-label=&quot;b  tree 와의 차이점  리프노드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;B- Tree 와의 차이점 : 리프노드&lt;/h3&gt;
&lt;p&gt;B+ Tree 는 앞서 살펴본 B- Tree 에서 응용된 구조로, 몇가지 특징에서 차이점을 보입니다. 우선 모든 노드에서 레코드에 대한 primary key 값을 저장하고 있는 B-Tree 와 달리, &lt;strong&gt;오직 리프 노드에서만 실제 레코드에 대한 primary key 값을 저장하고 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;여기서 리프 노드에서는 실제 레코드를 가리키기 위한 포인터를 저장하고 있는것처럼 묘사되었는데, 이는 &lt;code class=&quot;language-text&quot;&gt;비클러스터링 인덱스(Non-Clustering Index)&lt;/code&gt; 에 해당하는 경우입니다.&lt;/p&gt;
&lt;h3 id=&quot;1-더블리-링크드리스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EB%8D%94%EB%B8%94%EB%A6%AC-%EB%A7%81%ED%81%AC%EB%93%9C%EB%A6%AC%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;1 더블리 링크드리스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 더블리 링크드리스트&lt;/h3&gt;
&lt;p&gt;또한 같은 깊이(depth) 에 존재하는 노드들은 서로 &lt;code class=&quot;language-text&quot;&gt;링크드리스트&lt;/code&gt; 구조로 연결된 형태를 취합니다. 이에따라, 리프 노드끼리도 더블리 링크드리스트로 연결된 구조를 지닙니다.&lt;/p&gt;
&lt;p&gt;여기서 바로 B+ Tree 의 강점이 드러납니다. &lt;code class=&quot;language-text&quot;&gt;레인지 스캔(Range Scan)&lt;/code&gt; 이나 &lt;code class=&quot;language-text&quot;&gt;풀 스캔(Full Scan)&lt;/code&gt; 수행하는 경우 B- Tree 보다 훨씬 유리하다는 특징이 있죠. 이들을 수행하는 경우 &lt;code class=&quot;language-text&quot;&gt;O(logbN)&lt;/code&gt; 타임에 루트부터 시작해서 리프까지 도달한후에, 특정 리프에서 부터 시작해서 링크드리스트를 따라 넘어가면서 여러 리프노드들을 순차적으로 &lt;code class=&quot;language-text&quot;&gt;선형탐색&lt;/code&gt; 방식으로 스캔하면 됩니다.&lt;/p&gt;
&lt;p&gt;그러나 B- Tree 의 경우 링크트리스트로 연결된 구조가 아니기 때문에, 이를 수행할 경우 모든 노드에 대해 일일이 스캔을 수행해야한다는 번거로움이 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;2-더-많은-인덱스를-담을-수-있는구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EB%8D%94-%EB%A7%8E%EC%9D%80-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%EB%8B%B4%EC%9D%84-%EC%88%98-%EC%9E%88%EB%8A%94%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;2 더 많은 인덱스를 담을 수 있는구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 더 많은 인덱스를 담을 수 있는구조&lt;/h3&gt;
&lt;p&gt;또한 &lt;strong&gt;리프 노드를 제외하고 데이터를 담아두지 않기 때문에 메모리를 더 확보&lt;/strong&gt;함으로써 더 많은 key들을 수용할 수 있게됩니다. 하나의 노드에 더 많은 key들을 담을 수 있기에 트리의 높이는 더 낮아지게 되는 것이죠. (cache hit를 높일 수 있음)&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;b-tree-레코드-테이블&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#b-tree-%EB%A0%88%EC%BD%94%EB%93%9C-%ED%85%8C%EC%9D%B4%EB%B8%94&quot; aria-label=&quot;b tree 레코드 테이블 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;B+ Tree 레코드 테이블&lt;/h2&gt;
&lt;p&gt;인덱스는 테이블의 key 컬럼만 저장하고 있기 떄문에, 레코드에서 원하는 &lt;code class=&quot;language-text&quot;&gt;나머지 컬럼들 (Secondary Index)&lt;/code&gt; 을 조회하는 경우 데이터 파일에서 해당 레코드를 찾아야합니다.&lt;/p&gt;
&lt;p&gt;이때 InnoDB 테이블에서 인덱스를 통해 레코드를 읽을때는 데이터 파일에서 즉시 조회하지 못합니다. 이 이유는 데이터 파일 또한 B+ Tree 형태로 구현되어있기 때문이죠.&lt;/p&gt;
&lt;p&gt;데이터 파일의 B+ Tree 구조에서, 인덱스(리프 노드)에 저장되어 있는 key 값과 일치하는 레코드를 찾아서 O(logbN) 타임에 다시 내려가서 원하는 레코드를 조회해야하는 방식입니다.&lt;/p&gt;
&lt;p&gt;즉, 정리하자면 &lt;strong&gt;InnoDB 스토리지 엔진에서는 모든 세컨더리 인덱스 검색해서 데이터 레코드를 조회하기 위해 반드시 key 값을 저장하고있는 B+ Tree를 다시 한번 탐색&lt;/strong&gt;해야 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;b-tree-의-인덱스-key-연산&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#b-tree-%EC%9D%98-%EC%9D%B8%EB%8D%B1%EC%8A%A4-key-%EC%97%B0%EC%82%B0&quot; aria-label=&quot;b tree 의 인덱스 key 연산 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;B+ Tree 의 인덱스 key 연산&lt;/h2&gt;
&lt;p&gt;테이블의 레코드를 저장하거나 변경하는 경우 인덱스의 key 값이 추가되거나, 삭제되는등의 연산이 발생할겁니다. 인덱스의 key 값 추가나 변경, 삭제등이 어떻게 처리되는것이 이해해두면 쿼리의 성능을 예측하기 쉬워질겁니다.&lt;/p&gt;
&lt;h3 id=&quot;인덱스-key-값-추가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4-key-%EA%B0%92-%EC%B6%94%EA%B0%80&quot; aria-label=&quot;인덱스 key 값 추가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스 key 값 추가&lt;/h3&gt;
&lt;p&gt;새로운 key 값이 저장되기 위해선, 루트에서 부터 시작해서 범위 값을 대소비교하면서 삽입될 리프 노드에 찾아가서 삽입되면 됩니다. 이때 주의할점은 삽입되어야할 리프 노드가 key 값이 꽉 차서 더이상 저장할 수 없는 경우, 리프 노드가 &lt;code class=&quot;language-text&quot;&gt;split&lt;/code&gt; 되어야한다는 특징이 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/174c11c2-4b72-46c9-b249-e2c44aec6737/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;split 될때 새롭게 생긴 오른쪽의 노드의 맨 앞 원소는 두 리프노드의 부모 노드에 삽입되어야 한다는 과정이 있는데, 이때 부모 노드에서도 split 을 해야하는 상황이 발생할 수 있습니다. &lt;strong&gt;최악의 경우에는 이러한 split 의 전파가 루트 노드까지 올라간다는 특징&lt;/strong&gt; 때문에, B+ Tree 의 key 값 추가는 비용이 많이 드는것으로 알려져있습니다.&lt;/p&gt;
&lt;p&gt;이러한 단점 때문에 InnoDB 엔진에서는 INSERT 문이 실행되면 필요에따라 인덱스의 key 추가 작업을 지연시켜서 나중에 한번에 처리된다고 합니다. 하지만 primary key 나 unique key 인덱스의 경우는 중복 체크가 필요하기 때문에 즉시 B+ Tree 에 추가하거나 삭제한다고 합니다.&lt;/p&gt;
&lt;h3 id=&quot;인덱스-key-값-삭제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4-key-%EA%B0%92-%EC%82%AD%EC%A0%9C&quot; aria-label=&quot;인덱스 key 값 삭제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스 key 값 삭제&lt;/h3&gt;
&lt;p&gt;삭제는 꽤나 간단합니다. 리프 노드중에 해당 key 값이 저장되어 있는 곳을 찾아내서 해당 key 값을 인덱스에서 삭제시켜주면 끝입니다. 이때 삭제된 공간은 추후 재활용된다는 가정하에, 그대로 냅둡니다.&lt;/p&gt;
&lt;p&gt;몰론 이때 삭제된 key 값이 가리키는 실제 레코드 데이터는 데이터 파일에서 삭제되지 않습니다. 실제 레코드는 그저 인덱스를 보유하지 않는 상태가 되는것입니다.&lt;/p&gt;
&lt;h3 id=&quot;인덱스-key-값-변경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4-key-%EA%B0%92-%EB%B3%80%EA%B2%BD&quot; aria-label=&quot;인덱스 key 값 변경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스 key 값 변경&lt;/h3&gt;
&lt;p&gt;인덱스의 key 값은 해당 key 값에따라 리프 노드의 위치가 결정되므로, 단순히 인덱스상의 key 값만 변경하는 것은 불가능합니다. 먼저 기존 key 값을 삭제 연산을 진행한 후, 새로운 key 값을 추가 연산을 진행하는 형태로 처리됩니다. 이때 InnoDB 엔진에서는 이 작업을 모두 &lt;code class=&quot;language-text&quot;&gt;체인지 버퍼(Change Buffer)&lt;/code&gt; 를 통해 지연 처리될 수 있다고합니다.&lt;/p&gt;
&lt;h3 id=&quot;인덱스-key-값-조회&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4-key-%EA%B0%92-%EC%A1%B0%ED%9A%8C&quot; aria-label=&quot;인덱스 key 값 조회 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스 key 값 조회&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5d88eb2b-27ef-4497-b4da-5ebcf3adca72/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;key 값을 조회하는 것은 여러 방식이 있겠지만, 여기서는 하나의 특정 key 값을 조회하는 경우를 가정하겠습니다. 앞서 말했듯이, 이 연산은 루트에서부터 시작해서 특정 리프에 있는 key 값을 조회해오면 끝입니다. 이 과정이 &lt;code class=&quot;language-text&quot;&gt;O(logbN)&lt;/code&gt; 에 수행된다는 것이죠.&lt;/p&gt;
&lt;p&gt;이때 주의할 점은 &lt;code class=&quot;language-text&quot;&gt;변형된 key 값&lt;/code&gt; 에 대해서는 B+ Tree 의 장점을 누릴 수 없다는 것입니다. 인덱스의 key 값에 변형이되면 해당 key 값은 B+ Tree 인덱스에 존재하지 않는 값이기 떄문이죠. 따라서 함수나 연산등으로 수행한 결과를 정렬하거나 검색하는 작업은 B+ Tree 의 인덱스에 존재하는 대상이기 때문에 주의해서 사용해야 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/96&quot;&gt;[Database] 인덱스(index)란?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@evelyn82ny/B-Tree-index-feat-difference-from-B-plus-Tree&quot;&gt;[MySQL 8.0] B-Tree Index (feat. B+Tree와의 차이)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rebro.kr/169&quot;&gt;[DB] 10. B-Tree (B-트리)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[데이터베이스 순차 I/O 와 랜덤 I/O]]></title><description><![CDATA[데이터베이스의 쿼리 성능의 최적화를 위해,  에 대한 깊은 이해는 필수입니다. 인덱스를 본격적으로 다루기전에, 디스크의 I/O 관점에서 왜 랜덤 I/O 와 순차 I/O 를 이해하고 어떻게 쿼리가 튜닝되는것인지를 다루어보고자 합니다. HDD, SSD…]]></description><link>https://haon.site/haon/index/io/</link><guid isPermaLink="false">https://haon.site/haon/index/io/</guid><pubDate>Mon, 12 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;데이터베이스의 쿼리 성능의 최적화를 위해, &lt;code class=&quot;language-text&quot;&gt;인덱스(Index)&lt;/code&gt; 에 대한 깊은 이해는 필수입니다. 인덱스를 본격적으로 다루기전에, 디스크의 I/O 관점에서 왜 랜덤 I/O 와 순차 I/O 를 이해하고 어떻게 쿼리가 튜닝되는것인지를 다루어보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;hdd-ssd&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hdd-ssd&quot; aria-label=&quot;hdd ssd permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HDD, SSD&lt;/h2&gt;
&lt;p&gt;최근 HDD 에서 SSD 로 많이 대중화되며 활용되고 있는 추세이지만, 여전히 데티어 저장 매체는 컴퓨터에서 가장 느린 디스크( Disk) 라는 것은 변함없는 사실입니다. 때문에 데이터베이스의 성능 튜닝은 어떻게 디스크를 I/O 를 줄이느냐가 관건입니다.&lt;/p&gt;
&lt;p&gt;초당 처리 횟수를 분석해봤을때, Flash Memory 를 사용하는 SSD 는 HDD 에 비해 약 1000배 가량의 차이를 보이며, 이 떄문에 DBMS 용으로 사용할 서버는 대중적으로 많이 채택하고 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;성능차이&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B1%EB%8A%A5%EC%B0%A8%EC%9D%B4&quot; aria-label=&quot;성능차이 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;성능차이&lt;/h3&gt;
&lt;p&gt;그런데 디스크의 헤더를 움직이지 않고 한번에 많은 데이터를 읽는 &lt;code class=&quot;language-text&quot;&gt;순차 I/O&lt;/code&gt; 에서는 SSD 가 HDD 와 성능상 거의 차이가 없긴합니다. 그러나, SSD 는 &lt;code class=&quot;language-text&quot;&gt;랜덤 I/O&lt;/code&gt; 에서 장점이 두각됩니다. DB 서버에서의 대부분의 I/O 는 작은 데이터를 읽고 쓰는 랜덤 I/O 작업입니다. 어떤 자료에 따르면, SSD 와 HDD 의 초당 트랜잭션 처리수의 차이가 무려 7배가 차이 난다고 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;random-io-sequential-io&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#random-io-sequential-io&quot; aria-label=&quot;random io sequential io permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Random I/O, Sequential I/O&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7e0633c2-201e-4160-bcae-cdc232523d27/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;우선 랜덤 I/O 란 데이터를 임의의 위치에서 읽거나 쓰는 작업을 의미하며, 순차 I/O 는 데이터를 연속적인 블록으로 순차적으로 읽거나 쓰는 작업을 의미합니다. 이러한 랜덤 I/O 와 순차 I/O 의 공통점은, &lt;strong&gt;HDD 의 플래터(원판) 을 돌려서 읽어야 할 데이터가 저장된 위치가 디스크 헤더를 이동시킨 다음 데이터를 행위를 포함하는 것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;둘의 차이점은 &lt;code class=&quot;language-text&quot;&gt;디스크 헤더의 위치 이동횟수&lt;/code&gt; 에서 큰 차이를 보입니다. 랜덤 I/O 는 페이지(Page), 즉 데이터를 디스크에 쓰기위해 &quot;매번&quot; 디스크 헤더를 움직여서 쓰고 쓸 위치로 이동시키는 시스템 콜을 호출하는 방식입니다. 반면, 순차 I/O 는 매번 헤더를 이동시킬 필요가 없는 작업이 동반됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;각각-언제-어떻게-발생하는건데-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%81%EA%B0%81-%EC%96%B8%EC%A0%9C-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94%EA%B1%B4%EB%8D%B0-&quot; aria-label=&quot;각각 언제 어떻게 발생하는건데  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;각각 언제, 어떻게 발생하는건데 !?&lt;/h2&gt;
&lt;p&gt;둘의 발생 상황을 정리해보면 다음과 같습니다.&lt;/p&gt;
&lt;h3 id=&quot;랜덤-io&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9E%9C%EB%8D%A4-io&quot; aria-label=&quot;랜덤 io permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;랜덤 I/O&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;특정 레코드나 데이터 블록을 찾기 위해 인덱스를 탐색하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;WHERE 절에 조건을 포함한 쿼리를 실행하여 특정 레코드를 찾는 경우&lt;/li&gt;
&lt;li&gt;임의의 데이터 위치에 대한 갱신 또는 삭제 작업을 수행하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이처럼 랜덤 I/O는 디스크 헤드가 여러 위치로 이동해야 하므로 비교적 느린 작업입니다. 따라서 랜덤 I/O가 많이 발생하는 경우 성능 저하가 발생할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;순차-io&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%9C%EC%B0%A8-io&quot; aria-label=&quot;순차 io permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;순차 I/O&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;테이블의 모든 레코드를 스캔하는 SELECT 쿼리를 실행하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;인덱스의 모든 블록을 읽거나 쓰는 경우&lt;/li&gt;
&lt;li&gt;대량의 데이터를 정렬하거나 그룹화하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;반면, 순차 I/O는 디스크에서 연속적인 데이터를 읽거나 쓰므로 랜덤 I/O에 비해 더 빠른 작업입니다. 따라서 순차 I/O는 대량의 데이터 액세스에 유리한 특성을 가지고 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;쿼리-튜닝을-통해-random-io-를-최소화하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BF%BC%EB%A6%AC-%ED%8A%9C%EB%8B%9D%EC%9D%84-%ED%86%B5%ED%95%B4-random-io-%EB%A5%BC-%EC%B5%9C%EC%86%8C%ED%99%94%ED%95%98%EA%B8%B0&quot; aria-label=&quot;쿼리 튜닝을 통해 random io 를 최소화하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쿼리 튜닝을 통해 Random I/O 를 최소화하기&lt;/h2&gt;
&lt;p&gt;데이터베이스 쿼리의 성능을 향상시키기 위해서는 가능한한 순차 I/O를 증가시키고, 랜덤 I/O를 최소화하는 것이 최적의 해결책이 되긴합니다. 그러나, 사실 쿼리를 튜닝해서 랜덤 I/O 를 순차 I/O 로 바꿔서 실행할 방법은 그리 많지 않습니다. &lt;strong&gt;일반적으로 쿼리를 튜닝하는 것은 랜덤 I/O 자체를 줄이는 것이 목적&lt;/strong&gt;이러고 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;여기서 랜덤 I/O 줄인다는 것은, &lt;strong&gt;쿼리를 처리하는데 꼭 필요한 데이터만을 읽고 쓰도록 쿼리를 개선하는 것을 의미&lt;/strong&gt;합니다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Jenkins, Nginx 배포 환경에서 발생하는 다운타임을 온전히 제거할 수 있을까?]]></title><description><![CDATA[학습배경 [CI/CD & Nginx] Worker Process 튜닝으로 다운타임을 0.015초로 줄이기 까지의 개선과정 에서 다루었듯이, Nginx 는 reload 시 아주 짧은 다운타임이 발생합니다. 그러나 Nginx…]]></description><link>https://haon.site/haon/infra/ci-cd/zero-downtime/</link><guid isPermaLink="false">https://haon.site/haon/infra/ci-cd/zero-downtime/</guid><pubDate>Sat, 27 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/CICD-Nginx-Worker-Process-%ED%8A%9C%EB%8B%9D%EC%9C%BC%EB%A1%9C-%EB%8B%A4%EC%9A%B4%ED%83%80%EC%9E%84%EC%9D%84-0.015%EC%B4%88%EB%A1%9C-%EC%A4%84%EC%9D%B4%EA%B8%B0-%EA%B9%8C%EC%A7%80%EC%9D%98-%EA%B0%9C%EC%84%A0%EA%B3%BC%EC%A0%95&quot;&gt;[CI/CD &amp;#x26; Nginx] Worker Process 튜닝으로 다운타임을 0.015초로 줄이기 까지의 개선과정&lt;/a&gt; 에서 다루었듯이, Nginx 는 reload 시 아주 짧은 다운타임이 발생합니다. 그러나 Nginx 공식문서에서는 gracefully shutdown 을 지원함으로써, &lt;code class=&quot;language-text&quot;&gt;제로타임(zero-downtime)&lt;/code&gt; 이 가능케한다고 주장하고 있습니다. 하지만, 실제로는 reload 시 다운타임이 발생하고 있으며, 이는 graceful 하지 못합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이번에는 Nginx 의 문제점을 분석하고, 제가 어떻게 제로타임을 구현할 수 있을지에 대해 고민한 흔적에 대해 다루고자 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;문제-발생상황--급한-불은-껐는데-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%A0%9C-%EB%B0%9C%EC%83%9D%EC%83%81%ED%99%A9--%EA%B8%89%ED%95%9C-%EB%B6%88%EC%9D%80-%EA%BB%90%EB%8A%94%EB%8D%B0-&quot; aria-label=&quot;문제 발생상황  급한 불은 껐는데  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문제 발생상황 : 급한 불은 껐는데.. 🔥&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/9c63f663-5376-437d-891f-58116c93fda5/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/CICD-Nginx-Worker-Process-%ED%8A%9C%EB%8B%9D%EC%9C%BC%EB%A1%9C-%EB%8B%A4%EC%9A%B4%ED%83%80%EC%9E%84%EC%9D%84-0.015%EC%B4%88%EB%A1%9C-%EC%A4%84%EC%9D%B4%EA%B8%B0-%EA%B9%8C%EC%A7%80%EC%9D%98-%EA%B0%9C%EC%84%A0%EA%B3%BC%EC%A0%95&quot;&gt;[CI/CD &amp;#x26; Nginx] Worker Process 튜닝으로 다운타임을 0.015초로 줄이기 까지의 개선과정&lt;/a&gt; 에서도 계속 봤던 문제 상황이지만, &lt;code class=&quot;language-text&quot;&gt;Blue/Green 배포 아키텍처&lt;/code&gt; 에서 다운타임을 개선하는데 까지 많은 개선사항이 있었고, 결국 다운타임을 0.3초에서 시작해서 0.015초까지 감소시키는것은 성공했습니다. 그러나, 아직 다운타임이 발생한다는 그 근본적인 문제는 해결되지 않았으며, 이를 해결하고자 Nginx 의 공식문서에서 주장하는 &lt;code class=&quot;language-text&quot;&gt;Nginx Graceful Shutdown&lt;/code&gt; 의 특징에 대해 깊게 파해쳐 봤습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx-의-reload-의-모순&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EC%9D%98-reload-%EC%9D%98-%EB%AA%A8%EC%88%9C&quot; aria-label=&quot;nginx 의 reload 의 모순 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 의 reload 의 모순&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.nginx.com/faq/how-does-zero-downtime-configuration-testingreload-in-nginx-plus-work/&quot;&gt;Nginx 공식문서&lt;/a&gt; 에서 말하기를, &lt;code class=&quot;language-text&quot;&gt;nginx -s reload&lt;/code&gt; 명령은 현재 실행중인 Nginx 프로세스에 reload 시그널을 보냅니다. Nginx 프로세는 nginx.conf 의 설정정보를 다시 읽어들여서 리버스프록시만 바꾸면서 Blue/Green 배포가 가능하다고 주장하고 있습니다. 하지만 앞서 계속 봤듯이, Nginx 는 reloading 시에 아주 짧은 다운타임이 발생하는 모순이 발생하고 있습니다. 이는 graceful 하지 못하다고 볼 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;클라이언트와-nginx-서버간의-tcp-커넥션-처리-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%EC%99%80-nginx-%EC%84%9C%EB%B2%84%EA%B0%84%EC%9D%98-tcp-%EC%BB%A4%EB%84%A5%EC%85%98-%EC%B2%98%EB%A6%AC-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;클라이언트와 nginx 서버간의 tcp 커넥션 처리 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클라이언트와 Nginx 서버간의 TCP 커넥션 처리 방식&lt;/h2&gt;
&lt;p&gt;Nginx 의 reload 시 내부 동작방식을 이해하고 왜 reload 시 다운타임이 발생하는지 이해하려면, 먼저 클라이언트와 nginx 서버 사이에서 어떻게 TCP 커넥션을 맺고 처리하는지를 이해해야합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;우선 클라이언트와 Nginx 서버의 IP 포트로 TCP 연결을 시도합니다.&lt;/li&gt;
&lt;li&gt;nginx 서버는 클라이언트의 요청을 수신하고, 해당 요청을 처리하기 위해 Worker Process 에서 사용 가능한 워커 프로세스를 할당합니다.&lt;/li&gt;
&lt;li&gt;할당된 워커는 요청에 대한 처리작업을 수행하고, HTTP 응답을 클라이언트에 보냅니다.&lt;/li&gt;
&lt;li&gt;클라이언트 또는 서버의 어느 한쪽에 연결을 종료하려는 경우, TCP 커넥션은 종료됩니다. 커넥션이 끊어지게 되면, 클라이언트와 서버는 &lt;strong&gt;종료된 커넥션으로 더 이상 데이터 교환을 할 수 없게 됩니다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;여기서 중점으로 봐야할 부분은 &quot;종료된(끊어진) 커넥션으로 데이터 교환이 더 이상 불가능하다&quot; 는점 입니다. 당연한 말이지만, &lt;strong&gt;어떤 커넥션이 종료되었다면 해당 커넥션으로는 다시 요청이 불가능&lt;/strong&gt;하므로 새로운 커넥션을 생성해서 재요청을 했을때만 서버에서 요청을 처리 가능합니다.&lt;/p&gt;
&lt;h3 id=&quot;connection--keep-value&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#connection--keep-value&quot; aria-label=&quot;connection  keep value permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Connection : keep-value&lt;/h3&gt;
&lt;p&gt;또, HTTP Header 의 Connection 의 value 값으로 &quot;keep-alive&quot; 와 &quot;close&quot; 속성을 부여 가능합니다. 클라이언트로부터 온 패킷을 뜯어봤을때 keep-alive 속성이 부여된 패킷이라면, 해당 nginx 서버에 설정된 &lt;code class=&quot;language-text&quot;&gt;keep-alive&lt;/code&gt; 만큼 커넥션이 유지됩니다. 즉, &lt;code class=&quot;language-text&quot;&gt;keep-alive=60s;&lt;/code&gt;로 지정된 경우라면 클라이언트는 TCP 연결을 매번 재시도 할 필요없이 60초 동안 해당 커넥션으로 계속 요청을 시도하는 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx-의-reload-시-내부-동작방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EC%9D%98-reload-%EC%8B%9C-%EB%82%B4%EB%B6%80-%EB%8F%99%EC%9E%91%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;nginx 의 reload 시 내부 동작방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 의 reload 시 내부 동작방식&lt;/h2&gt;
&lt;p&gt;Nginx 의 실제 내부동작 방식을 알면, graceful 하지 못하다는 것을 알 수 있습니다. 결론부터 말하자면, HTTP Header 의 connection = keep-alive 으로 부여된경우, 문제가 발생하게됩니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Nginx 가 reload 시그널을 받습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;워커 프로세스들은 현재 처리중인 http 요청까지만 응답하고, tcp 커넥션을 끊어버립니다.&lt;/li&gt;
&lt;li&gt;만약 클라이언트의 http header 의 Connection 의 value 가 &quot;keep-alive&quot; 으로 설정되어있는 경우, 앞서 연결이 끊어진 tcp 커넥션으로 재요청을 시도해서 에러가 발생하게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;keep-alive-로-지정된-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#keep-alive-%EB%A1%9C-%EC%A7%80%EC%A0%95%EB%90%9C-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;keep alive 로 지정된 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;keep-alive 로 지정된 경우&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/72646d43-ba85-43e9-bb23-ee11a1ee79ab/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;keep-alive 로 지정된 경우를 가정해봅시다. 기본적으로 &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc2616#section-8.1&quot;&gt;HTTP/1.1 Connection 헤더&lt;/a&gt; 에서는 &lt;code class=&quot;language-text&quot;&gt;Connection: Keep-alive&lt;/code&gt; 헤더가 존재할 경우 tcp 커넥션을 재사용하도록 합니다. 위와 같이 클라이언트가 요청을 최초로 시도한다면 커넥션을 새롭게 생성합니다. 또 keep-alive 속성으로 인해, keep-alive = 60s; 인 경우를 가정한다면 60초 동안 해당 커넥션으로 요청을 재시도하게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;다운타임-발생원인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%9A%B4%ED%83%80%EC%9E%84-%EB%B0%9C%EC%83%9D%EC%9B%90%EC%9D%B8&quot; aria-label=&quot;다운타임 발생원인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다운타임 발생원인&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6e83e21f-31af-4412-a82e-b7c4d19869c9/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그런데 nginx -s reload 를 수행한 경우를 생각해봅시다. 그러면 새로운 worker process 가 생성되고 Nginx 에 의해 커넥션이 끊어지게(종료) 됩니다. 그러나, 기존에 커넥션을 생성한 클라이언트는 keep-alive 속성으로 인해 여전히 Nginx 에 의해 끊긴 TCP 커넥션을 통해 HTTP 요청을 재생성하고 해서 에러가 발생하게되고, 해당 요청은 실패하게 됩니다. 이때 바로 다운타임이 발생하게 되는 것이며, 클라이언트는 새로운 TCP 커넥션을 통해서 재요청을 시도해야합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;http11-connecion-헤더&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http11-connecion-%ED%97%A4%EB%8D%94&quot; aria-label=&quot;http11 connecion 헤더 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP/1.1 Connecion 헤더&lt;/h2&gt;
&lt;p&gt;앞서 말씀드렸듯이, HTTP/1.1 스팩은 클라이언트와 서버는 응답에 &lt;code class=&quot;language-text&quot;&gt;Connection: Keep-alive&lt;/code&gt; 헤더가 존재할 경우 TCP 커넥션을 재사용해야 합니다. HTTP/1.1 은 Connection 헤더의 디폴트 값이 Keep-alive 이고, 이는 Connection 헤더를 따로 설정하지 않는 이상 항상 TCP 커넥션을 재사용해야 하는 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;connection-close&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#connection-close&quot; aria-label=&quot;connection close permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Connection: close&lt;/h3&gt;
&lt;p&gt;그렇다면 단순하게 keep-alive 대신에 &lt;code class=&quot;language-text&quot;&gt;Connecion: close&lt;/code&gt; 헤더를 통해 매번 요청을 보낼때마다 TCP 연결을 맺고나서 작업내용을 처리후 재활용없이 바로 종료되게 만들 수 있습니다. 즉, 클라이언트는 매번 요청을 보낼떄 &lt;code class=&quot;language-text&quot;&gt;Connection: close&lt;/code&gt; 를 명시해서 매번 요청을 수행한 후에는 즉시 커넥션을 재활용하지 않고 종료시키게 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;하지만, 이 방식을 활용시 다음과 같은 문제점을 지닐 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;매 요청마다 TCP 커넥션을 맺어야하니, 오버해드가 발생할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;클라이언트와 서버 사이에 Connection: close 이용을 하겠다는 규약을 맺어야합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;HTTP1.1 스팩상, Connection: close 이용을 강제하고 있지 않습니다. 특히 위 2번의 경우 Connection: close 를 이용하겠다는 규약을 특별히 맺어야한다는 번거로움이 발생하기도 하죠. 이 단순한 방법보다는, keep-alive 속성을 계속 유지하면서도 &lt;code class=&quot;language-text&quot;&gt;reload&lt;/code&gt; 가 되는 타이밍에 유의해서 커넥션을 처리해주는 더 효율적인 방법이 필요할겁니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;close-라면-제로타임-구현이-가능하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#close-%EB%9D%BC%EB%A9%B4-%EC%A0%9C%EB%A1%9C%ED%83%80%EC%9E%84-%EA%B5%AC%ED%98%84%EC%9D%B4-%EA%B0%80%EB%8A%A5%ED%95%98%EB%8B%A4&quot; aria-label=&quot;close 라면 제로타임 구현이 가능하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;close 라면 제로타임 구현이 가능하다&lt;/h2&gt;
&lt;p&gt;만약 HTTP/1.1 의 권장 스팩을 벗어나서 keep-alive 가 아니라 close 를 사용하게 된다면 제로 다운타임(zero-downtime) 구현이 가능해집니다.&lt;/p&gt;
&lt;h3 id=&quot;add_header&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#add_header&quot; aria-label=&quot;add_header permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;add_header&lt;/h3&gt;
&lt;p&gt;만약 위 방법을 적용하고 싶다면, 설정방법은 아래와 같이 &quot;close&quot; 는 지정해주면 됩니다. Nginx 가 Connecion: close 헤더를 응답에 추가되도록 설정하는 것입니다. 이러면 매번 커넥션을 새롭게 keep-alive 으로 생성하고, 해당 패킷들을 항상 close 로 변환하여 응답해주게 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;server&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        listen &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        include &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;d&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;service&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;inc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        location &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                add_header &lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;close&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                proxy_pass $service_url&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                proxy_set_header &lt;span class=&quot;token class-name&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Real&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IP&lt;/span&gt; $remote_addr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                proxy_set_header &lt;span class=&quot;token class-name&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Forwarded&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;For&lt;/span&gt; $proxy_add_x_forwarded_for&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                proxy_set_header &lt;span class=&quot;token class-name&quot;&gt;Host&lt;/span&gt; $http_host&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;다운타임-테스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%9A%B4%ED%83%80%EC%9E%84-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;다운타임 테스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다운타임 테스트&lt;/h3&gt;
&lt;p&gt;실제로 요청을 보내봤을때, 단 한건의 요청도 실패하는 것없이 제로 다운타임이 구현되는 모습을 확인 가능했습니다. 테스트를 10번 넘게 돌렸을때, 모든 테스트가 성공적으로 다운타임 이슈를 해결할 수 있었습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;keep-alive-가-살아있는한-완벽한-제로타임을-만드는-것은-불가능하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#keep-alive-%EA%B0%80-%EC%82%B4%EC%95%84%EC%9E%88%EB%8A%94%ED%95%9C-%EC%99%84%EB%B2%BD%ED%95%9C-%EC%A0%9C%EB%A1%9C%ED%83%80%EC%9E%84%EC%9D%84-%EB%A7%8C%EB%93%9C%EB%8A%94-%EA%B2%83%EC%9D%80-%EB%B6%88%EA%B0%80%EB%8A%A5%ED%95%98%EB%8B%A4&quot; aria-label=&quot;keep alive 가 살아있는한 완벽한 제로타임을 만드는 것은 불가능하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;keep-alive 가 살아있는한 완벽한 제로타임을 만드는 것은 불가능하다&lt;/h2&gt;
&lt;h3 id=&quot;이상적인-방향--reload-타이밍에만-tcp-커넥션을-재생성하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EC%83%81%EC%A0%81%EC%9D%B8-%EB%B0%A9%ED%96%A5--reload-%ED%83%80%EC%9D%B4%EB%B0%8D%EC%97%90%EB%A7%8C-tcp-%EC%BB%A4%EB%84%A5%EC%85%98%EC%9D%84-%EC%9E%AC%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0&quot; aria-label=&quot;이상적인 방향  reload 타이밍에만 tcp 커넥션을 재생성하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이상적인 방향 : reload 타이밍에만 TCP 커넥션을 재생성하기&lt;/h3&gt;
&lt;p&gt;커넥션 이슈를 해결하기 위해 며칠동안 많은 생각과 고민이 있었습니다. 아쉽게도 그 과정에서 떠올려봤던 아래와 같은 방법이 있었는데, 이는 구현이 불가능에 가깝다고 판단되어서 최선의 해결책으로만 남게 되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/13af4008-6a4f-4aec-8c0a-7ea7f8383c2f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이 방식은 구현이 매우 복잡하기 떄문에 제공되지 않는 기능이라고 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;해결책으로 생각했던 최선의 방법은 위와같이 바로 평소에 들어온 keep-alive 를 계속 유지하다가, reload 시점에만 커넥션을 끊어버리고 클라이언트가 새로운 커넥션을 생성하도록 유도하는 방식입니다. 즉, 워커 프로세스는 reload 시점에만 &lt;code class=&quot;language-text&quot;&gt;Connection: close&lt;/code&gt; 헤더를 응답으로 전송해서 클라이언트가 새로운 커넥션을 설정하도록 유도하는 것입니다. 이렇게하면 기존에 끊어진 TCP 커넥션을 재활용하려는 시도를 방지하면서, 다운타임을 방지할 수 있게 될겁니다.&lt;/p&gt;
&lt;p&gt;하지만, 아쉽게도 이 기능을 구현할 방법은 현재 존재하지 않습니다. 결국 Nginx 로 완벽한 제로 다운타임(zero-downtime) 을 만드는것은 사실상 불가능에 가깝다고 보는게 맞는 것 같습니다.&lt;/p&gt;
&lt;h3 id=&quot;이벤트-훅evevt-hook&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%B2%A4%ED%8A%B8-%ED%9B%85evevt-hook&quot; aria-label=&quot;이벤트 훅evevt hook permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이벤트 훅(Evevt Hook)&lt;/h3&gt;
&lt;p&gt;이에 대한 해결책으론, &lt;code class=&quot;language-text&quot;&gt;이벤트 훅(Hook)&lt;/code&gt; 을 사용하는 방법이 있긴합니다. 이벤트 훅은 Nginx의 특정 이벤트가 발생했을 때 실행되는 사용자 정의 함수입니다. 이를 활용하여 nginx -s reload 명령 실행 시점에서 원하는 동작을 수행할 수 있긴 합니다. 예를 들어, &lt;code class=&quot;language-text&quot;&gt;ngx_lua 모듈&lt;/code&gt;을 사용하여 Lua 스크립트를 작성하고, 해당 스크립트를 이벤트 훅으로 등록하여 실행하는 방법이 있습니다. 하지만, 이를 구현하는 것은 정말 어렵기때문에 거의 사용되지 않습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;제로타임에-더-가까워지기-위한-성능개선&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%9C%EB%A1%9C%ED%83%80%EC%9E%84%EC%97%90-%EB%8D%94-%EA%B0%80%EA%B9%8C%EC%9B%8C%EC%A7%80%EA%B8%B0-%EC%9C%84%ED%95%9C-%EC%84%B1%EB%8A%A5%EA%B0%9C%EC%84%A0&quot; aria-label=&quot;제로타임에 더 가까워지기 위한 성능개선 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제로타임에 더 가까워지기 위한 성능개선&lt;/h2&gt;
&lt;p&gt;하지만 제로타임에 더 가까워 지기위해, 더 효율적인 설정을 충분히 고민해보았습니다. 이는 불안정한 처리를 더 안정적으로 처리하기 위함이기도 합니다. 그 방법은 아래와 같습니다.&lt;/p&gt;
&lt;h3 id=&quot;1-worker_shutdown_timeout&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-worker_shutdown_timeout&quot; aria-label=&quot;1 worker_shutdown_timeout permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1) worker_shutdown_timeout&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;worker_shutdown_timeout &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;worker_shutdown_timeout 값은 graceful shutdown이 이루어지는 동안에도 각 워커 프로세스가 추가 요청을 받을 수 있는 시간을 지정합니다.&lt;/p&gt;
&lt;p&gt;이를 사용해야 하는 이유는 우선 &lt;code class=&quot;language-text&quot;&gt;keep-alive&lt;/code&gt; 때문입니다. 클라이언트와의 Keep-Alive 연결을 통해 여러 요청을 처리하는 경우, 워커 프로세스가 종료되기 전에 아직 완료되지 않은 요청이 남을 수 있습니다. 이는 클라이언트가 동일한 연결을 재사용하여 추가 요청을 보낼 수 있기 때문이죠.&lt;/p&gt;
&lt;h3 id=&quot;2-keepalive_timeout&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-keepalive_timeout&quot; aria-label=&quot;2 keepalive_timeout permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2) keepalive_timeout&lt;/h3&gt;
&lt;p&gt;다음으로 keep-alive 도 타임아웃 지정이 가능합니다. 아래처럼 작성시 클라이언트와 Nginx 간의 keep-alive 연결이 3초간 유지되는 것이죠. 이 시간은 애플리케이션 특성을 고려하여 적절히 처리해주면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;keepalive_timeout &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 방법 설정값들은 제 부하테스트 API 특성상 자원 낭비가 거의 없고, 응답속도가 빠른 편이기 때문에 매우 짧게 설정해줬습니다. 이 설정값은 애플리케이션 및 API 의 특성에 따라서 적절히 설정해주시면 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;기타-성능-개선요소&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%ED%83%80-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%EC%9A%94%EC%86%8C&quot; aria-label=&quot;기타 성능 개선요소 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기타 성능 개선요소&lt;/h3&gt;
&lt;p&gt;추가적으로 &lt;a href=&quot;https://velog.io/@msung99/CICD-Nginx-Worker-Process-%ED%8A%9C%EB%8B%9D%EC%9C%BC%EB%A1%9C-%EB%8B%A4%EC%9A%B4%ED%83%80%EC%9E%84%EC%9D%84-0.015%EC%B4%88%EB%A1%9C-%EC%A4%84%EC%9D%B4%EA%B8%B0-%EA%B9%8C%EC%A7%80%EC%9D%98-%EA%B0%9C%EC%84%A0%EA%B3%BC%EC%A0%95&quot;&gt;[CI/CD &amp;#x26; Nginx] Worker Process 튜닝으로 다운타임을 0.015초로 줄이기 까지의 개선과정&lt;/a&gt; 에서 다룬 내용이지만, http2 프로토콜을 적용하거나, 애플리케이션의 특성에 따라 Worker Process 의 개수와 커넥션 허용수를 적절히 조절함으로써 처리속도 및 성능 개선을 진행할 수도 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;성능-테스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B1%EB%8A%A5-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;성능 테스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;성능 테스트&lt;/h3&gt;
&lt;p&gt;성능이 크게 증가하진 않았지만, 평균 약 0.009 초의 다운타임을 보이는 것으로 보아 위 기능들을 도입한 결과가 없지않아 성능 개선에 있어 미약하게나마 도움은 된듯합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;결론&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EB%A1%A0&quot; aria-label=&quot;결론 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결론&lt;/h2&gt;
&lt;p&gt;결론을 내리자면, 완벽한 zero-downtime 을 만들어내는 것은 keep-alive 를 활용하지 않겠다면 &quot;가능&quot; 하고, 그게 아니라면 아쉽게도 &quot;불가능&quot; 에 매우 가깝다라고 보는게 맞습니다. 몰론 저도 모르는 모니터링 도구가 있어서 제가 생각했던 위 아이디어가 실제로 구현된다면 제로타임이 구현될 수는 있다고봅니다. 하지만 Nginx 에서 제공되는 기능 자체만으로는 불가능하다고 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;TCP 커넥션의 연결을 맺고 끊는 타이밍을 위처럼 조절한다면, 즉 keep-alive 특성을 활용하지 않겠다면 응답으로 connection = close 를 무조건 리턴하게 하는 위 방식도 활용할 수는 있겠습니다. 이 방법을 활용시에는 앞서 언급드렸듯이, 제로 다운타임이 구현은 가능해집니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;또한, 무엇보다 다운타임의 주원인은 Connection: keep-alive 일때 TCP 커넥션 처리 문제로 인해 발생한다는 것을 증명해냈습니다 !&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nginx.com/&quot;&gt;https://www.nginx.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://soonoo.me/docs/posts/2020/03/26/nginx-reload.html&quot;&gt;https://soonoo.me/docs/posts/2020/03/26/nginx-reload.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc2616#section-8.1&quot;&gt;https://datatracker.ietf.org/doc/html/rfc2616#section-8.1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Jenkins, Nginx 기반 배포 환경에서  발생하는 다운타임을 개선해보자!]]></title><description><![CDATA[학습배경 SpringBoot Graceful Shutdown & SIGTERM 시그널 : 구버전 프로세스를 안전하게 종료시켜보자! 에서 봤듯이, Graceful Shutdown 과 SIGTERM 시그널을 통해 다운타임을 0.3초 에서 0.…]]></description><link>https://haon.site/haon/infra/ci-cd/downtime-try/</link><guid isPermaLink="false">https://haon.site/haon/infra/ci-cd/downtime-try/</guid><pubDate>Fri, 26 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/SpringBoot-Graceful-Shutdown-%EA%B3%BC-SIGTERM-%EC%8B%9C%EA%B7%B8%EB%84%90-%EA%B5%AC%EB%B2%84%EC%A0%84-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EB%A5%BC-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-%EC%A2%85%EB%A3%8C%EC%8B%9C%EC%BC%9C%EB%B3%B4%EC%9E%90-pe10wqzm&quot;&gt;SpringBoot Graceful Shutdown &amp;#x26; SIGTERM 시그널 : 구버전 프로세스를 안전하게 종료시켜보자!&lt;/a&gt; 에서 봤듯이, Graceful Shutdown 과 SIGTERM 시그널을 통해 다운타임을 0.3초 에서 0.2초 감소시키도록 개선이 이루어졌으며, SIGKILL 을 통해 구버전 프로세스가 강제로 kill 되는 상황을 방지했습니다.&lt;/p&gt;
&lt;p&gt;그러나 &quot;무중단 배포&quot; 라는 컨셉에 있어서는, 아직도 0.2초란 다운타임을 줄이고 싶다는 욕심은 여전합니다. 0.2초도 몰론 매우 짧은시간이지만, 애플리케이션이 신버전으로 배포될때 &quot;무중단&quot; 되는 상태는 엄밀히 따졌을때 아닐겁니다.&lt;/p&gt;
&lt;p&gt;이번에는 제가 Nginx 을 성능을 개선 진행해서 &quot;다운타임을 0.2초에서 0.015초 까지 크게 감소&quot;시킨 과정에 대해서 소개해볼까합니다. 또 그 과정에서, Nginx 의 내부 메커니즘을 자세히 뜯어볼겁니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;02초의-다운타임-발생-환경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#02%EC%B4%88%EC%9D%98-%EB%8B%A4%EC%9A%B4%ED%83%80%EC%9E%84-%EB%B0%9C%EC%83%9D-%ED%99%98%EA%B2%BD&quot; aria-label=&quot;02초의 다운타임 발생 환경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;0.2초의 다운타임 발생 환경&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7b667ba4-237e-44df-aca4-0c0b9db5f088/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/CICD-Jenkins-%EC%99%80-Nginx-%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-BlueGreen-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC-%EC%9E%90%EB%8F%99%ED%99%94-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B5%AC%EC%B6%95&quot;&gt;[CI/CD] Jenkins 와 Nginx 를 활용한 Blue/Green 자동화 배포 아키텍처를 수동으로 구축하기 (feat. SpringBoot)&lt;/a&gt; 에서 다룬 내용은 다운타임이 무려 0.3초가 발생하는 문제가 발생했습니다.&lt;/p&gt;
&lt;p&gt;이를 방지하기 위해, 스프링부트 구버전 프로세스를 안전하게 종료시킬 수 있는 &lt;code class=&quot;language-text&quot;&gt;graceful shutdown&lt;/code&gt; 과 리눅스의 &lt;code class=&quot;language-text&quot;&gt;SIGETERM&lt;/code&gt; 시그널에 대해 &lt;a href=&quot;https://velog.io/@msung99/SpringBoot-Graceful-Shutdown-%EA%B3%BC-SIGTERM-%EC%8B%9C%EA%B7%B8%EB%84%90-%EA%B5%AC%EB%B2%84%EC%A0%84-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EB%A5%BC-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-%EC%A2%85%EB%A3%8C%EC%8B%9C%EC%BC%9C%EB%B3%B4%EC%9E%90-pe10wqzm&quot;&gt;SpringBoot Graceful Shutdown &amp;#x26; SIGTERM 시그널 : 구버전 프로세스를 안전하게 종료시켜보자!&lt;/a&gt; 에서 다루었습니다.&lt;/p&gt;
&lt;h3 id=&quot;nginx-가-다운타임-발생의-원인인-이유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EA%B0%80-%EB%8B%A4%EC%9A%B4%ED%83%80%EC%9E%84-%EB%B0%9C%EC%83%9D%EC%9D%98-%EC%9B%90%EC%9D%B8%EC%9D%B8-%EC%9D%B4%EC%9C%A0&quot; aria-label=&quot;nginx 가 다운타임 발생의 원인인 이유 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 가 다운타임 발생의 원인인 이유&lt;/h3&gt;
&lt;p&gt;맨처음에는 스프링부트 애플리케이션에서 graceful shutdown 이 혹시 잘 적용이 안된건가 하는 의심에, 아래와 같이 sleep 을 5초를 걸어서 nginx 가 리버스프록시 방향이 변경되기 전까지의 충분한 여유를 주고, 구버전 프로세스를 kill 하는 방식을 시도했습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;nginx_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;echo &apos;set \\\$service_url http://${deployment_target_ip}:8080;&apos; &gt; /etc/nginx/conf.d/service-url.inc &amp;amp;&amp;amp; service nginx reload&quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&quot;Switch the reverse proxy direction of nginx to ${deployment_target_ip} 🔄&quot;&lt;/span&gt;

sleep &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${deployment_target_ip}&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${blue_ip}&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
then
	 ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;green_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k 8080/tcp&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
	ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blue_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k 8080/tcp&quot;&lt;/span&gt;
fi
echo &lt;span class=&quot;token string&quot;&gt;&quot; ✅ 구버전 프로세스를 종료하고, 신버전 프로세스로 교체합니다.&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러나, 여전히 다운타임은 0.2초라는 문제가 발생했고 더 이상 다운타임이 개선될 상황이 보이지 않았습니다. 이로보아 Nginx 가 리버스프록시 방향을 전환시, 즉 &lt;code class=&quot;language-text&quot;&gt;nginx -s reload&lt;/code&gt; 를 수행할떄가 가장 큰 원인임을 알 수 있었으며, Nginx 를 타킷으로 성능을 개선하고자 많은 노력이 있었습니다. 결론부터 말하자면, 다운타임을 0.2초에서 약 0.015초로 대폭 감소시킬 수 있었습니다.&lt;/p&gt;
&lt;p&gt;결국, 이 다운타임을 대폭 감소시키고자 했던 Nginx 에 대한 성능을 개선시키는 방향을 생각하게 되었습니다. 그리고 그 과정을 이 포스팅에 녹여내고자합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx-의-worker-process&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EC%9D%98-worker-process&quot; aria-label=&quot;nginx 의 worker process permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 의 Worker Process&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/337432cb-d226-4f8e-919e-e42cbd7b9b82/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Nginx-1995%EB%85%84-%EC%97%AD%EC%82%AC%EB%B6%80%ED%84%B0-%EB%9C%AF%EC%96%B4%EB%B3%B4%EB%8A%94-Nginx-%EB%93%B1%EC%9E%A5%EB%B0%B0%EA%B2%BD%EB%B6%80%ED%84%B0-%EB%82%B4%EB%B6%80-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98%EA%B9%8C%EC%A7%80&quot;&gt;[Nginx] 1995년 역사부터 뜯어보는 Nginx : 등장배경부터 내부 메커니즘까지&lt;/a&gt; 에서도 정말 자세히 소개드린 내용이지만, Nginx 는 &lt;code class=&quot;language-text&quot;&gt;마스터 프로세스(Master Process)&lt;/code&gt; 에 의해 여러개의 &lt;code class=&quot;language-text&quot;&gt;워커 프로세스(Worker Process)&lt;/code&gt; 를 생산해냅니다. 또한 각 워커 프로세스는 지정된 Listen Socket 를 받고, 그 소켓에 새로운 클라이언트로부터 받은 요청에 대한 커넥션을 생성하고, 해당 요청을 처리하는 방식입니다.&lt;/p&gt;
&lt;p&gt;또, 연결된 각 TCP 커넥션은 HTTP 프로토콜에 설정된 &lt;code class=&quot;language-text&quot;&gt;keep-alive&lt;/code&gt; 시간만큼 끊기지않고 유지됩니다. 이러한 커넥션을 워커 프로세스는 동시간대에 최대 1024개까지 연결 가능합니다. 또 이벤트 큐(Event Queue) 에 담긴 이벤트들은 &lt;code class=&quot;language-text&quot;&gt;비동기적(asychoronous)&lt;/code&gt; 으로 처리됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx-gracefully-shutdown&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-gracefully-shutdown&quot; aria-label=&quot;nginx gracefully shutdown permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx Gracefully Shutdown&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/19b7d7d1-397a-4fce-b91b-b76ae6264a26/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;또 Nginx 는 gracefully shutdown 을 지원해줌으로써, &lt;code class=&quot;language-text&quot;&gt;nginx -s reload&lt;/code&gt; 와 같이 리로딩을 매끄럽게 지원해주도록 합니다. 리로딩을 시작한 타이밍은 다음과 같은 절차로 요청을 처리해줍니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;기존 Worker Process 로 트래픽이 유입되고 있는 상황입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;리로딩이 시작되면, Master Process 는 새로운 설정파일 정보에 기반하여 새로운 Worker Process 가 생성합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;새로운 Worker Process 가 요청을 받을
준비가 되었다면, 커넥션과 요청을 받기 시작하게 되고, 이때부터 기존의 Worker Process 는 gracefully 하게 shutdown 됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;즉, 구버전 Worker Process 는 바로 프로세스가 kill 되는 것이 아니라, Graceful Shutdown 을 수행하므로 기존 요청을 모두 처리한 후 안전하게 종료됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이러한 메커니즘에 따라서 Nginx 는 구버전 워커 프로세스가 안전하게 요청을 처리한 후 종료되는 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;worker-process-분산처리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#worker-process-%EB%B6%84%EC%82%B0%EC%B2%98%EB%A6%AC&quot; aria-label=&quot;worker process 분산처리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Worker Process 분산처리&lt;/h2&gt;
&lt;p&gt;지금부터 성능을 개선해봅시다. 우선 Worker Process 의 개선입니다. nginx.conf 는 별도의 설정이 없다면 아래와 같이 auto 로 지정이 되어있을텐데, 이 auto 값에는 해당 서버의 CPU Core 개수만큼이 지정됩니다. 이를 명시적으로 지정해주기 위해, 1, 4 등의 숫자로 채워줍시다. 저는 CPU 4Core 를 사용하기 때문에 4로 채워줬습니다. 이렇게 Worker Process 가 여럿 생성되면, &lt;code class=&quot;language-text&quot;&gt;라운드 로빈(Round Robin)&lt;/code&gt; 방식에 기반하여 여러 워커 프로세스가 골구로 커넥션 및 요청을 분산받고 빠른 처리를 할 수 있게 됩니다. 결국 테스크를 여러 워커 프로세스가 분할받는 &lt;code class=&quot;language-text&quot;&gt;분산처리&lt;/code&gt; 를 구현하게 된 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;user www&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
worker_processes auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 4로 변경&lt;/span&gt;
pid &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;run&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
include &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;modules&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;enabled&lt;span class=&quot;token comment&quot;&gt;/*.conf;

events {
        worker_connections 768;
        # multi_accept on;
}

http {
	// ...
}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;cpu-core-개수만큼-생성하는게-맞을까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cpu-core-%EA%B0%9C%EC%88%98%EB%A7%8C%ED%81%BC-%EC%83%9D%EC%84%B1%ED%95%98%EB%8A%94%EA%B2%8C-%EB%A7%9E%EC%9D%84%EA%B9%8C&quot; aria-label=&quot;cpu core 개수만큼 생성하는게 맞을까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CPU Core 개수만큼 생성하는게 맞을까?&lt;/h3&gt;
&lt;p&gt;그런데 과연 CPU Core 개수만큼 워커 프로세스가 생성되는게 좋을까요?&lt;/p&gt;
&lt;p&gt;CPU 4코어 환경에서 4개의 worker process가 CPU 4코어를 공유하여 처리하면, 각 worker process는 CPU 자원을 공평하게 나눠서 사용할 수 있게 됩니다. 이 경우, 각 worker process는 실제 CPU 코어 하나를 독점적으로 사용하는 것이 아니라, 여러 worker process가 동시에 CPU 코어를 사용하게 됩니다.&lt;/p&gt;
&lt;p&gt;여러 worker process가 CPU 자원을 공유하면, 각 worker process는 상대적으로 적은 처리 능력을 가지게 됩니다. 이는 동시에 실행되는 worker process들이 &lt;code class=&quot;language-text&quot;&gt;CPU 자원을 경쟁적으로 사용&lt;/code&gt;하며, 서로의 처리 속도에 영향을 미치기 때문입니다. CPU 자원이 공유되기 때문에 각 worker process는 처리량이 일정하게 나눠지지 않고, 상황에 따라 다른 worker process보다 더 많은 작업을 처리해야 할 수도 있습니다.&lt;/p&gt;
&lt;p&gt;따라서 CPU 4코어 환경에서 4개의 worker process를 사용하는 경우, 각 worker process는 상대적으로 적은 처리 능력을 가지게 되며, 이로 인해 성능 저하나 다운타임이 발생할 수 있습니다. 이를 해결하기 위해서는 적절한 worker_processes 값과 worker_connections 값 조정, CPU 코어의 활용 등을 고려하여 최적의 설정을 찾아야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ce67fcc1-5837-4cf1-9726-ea64fc52a6d8/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;하지만 저는 CPU Utilization 을 모니터링 해보니, 많이봤자 약 13% 의 사용률을 보였기 때문에, 자원 경쟁률이 미흡하다는 것을 인지하고 워커 프로세스를 4개 할당해주었습니다. 혹시 하는 마음에 worker process 를 3개로 지정해보고 테스트를 진행해봤으나, 역시나 CPU utilization 수치가 낮기 때문에 다운타임이 오히려 증가하는 것을 보고, 다시 4개로 설정해주었습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;worker-connections-개선&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#worker-connections-%EA%B0%9C%EC%84%A0&quot; aria-label=&quot;worker connections 개선 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Worker Connections 개선&lt;/h2&gt;
&lt;p&gt;다음으로 각 워커 프로세스의 최대 커넥션 허용개수도 지정해줄 수 있습니다. 앞서 말씀드렸듯이, 워커 프로세스는 동시간대에 TCP 커넥션을 최대 1024개를 유지 가능합니다. 또 Nginx 의 Graceful Shutdown 특성에 따라서, 새로운 Worker Process 가 생성되고 난 뒤에 아직 처리할게 남아있는 요청들은 gracefully 하게 처리된 후 구버전 워커 프로세스가 종료되는 방식입니다.&lt;/p&gt;
&lt;p&gt;워커 프로세스의 커넥션 허용수는 별도의 설정이 없다면 기본적으로 768 로 설정이 되어있습니다. 커넥션을 최대한 맺도록 1024로 수정해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;user www&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
worker_processes &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
pid &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;run&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
include &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;modules&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;enabled&lt;span class=&quot;token comment&quot;&gt;/*.conf;

events {
        worker_connections 1024;
        # multi_accept on;
}

http {
	// ...
}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;성능-테스트-결과--평균-0015초의-다운타임&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B1%EB%8A%A5-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B2%B0%EA%B3%BC--%ED%8F%89%EA%B7%A0-0015%EC%B4%88%EC%9D%98-%EB%8B%A4%EC%9A%B4%ED%83%80%EC%9E%84&quot; aria-label=&quot;성능 테스트 결과  평균 0015초의 다운타임 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;성능 테스트 결과 : 평균 0.015초의 다운타임&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5f5992c8-5480-4459-96d1-aa6c88f626f6/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;jmeter 를 통해 성능 테스트를 진행한 결과, 튜닝후의 다운타임은 가장 짧았을때가 0.001초라는 결과가 나왔으며, 평균적으로 약 0.015초의 결과가 나온만큼 &lt;strong&gt;다운타임이 대폭 감소&lt;/strong&gt;한 모습을 확인할 수 있습니다. 또한, 이것도 다운타임이 발생한 것이라고 볼 수 있는데, 이 다운타임은 &lt;code class=&quot;language-text&quot;&gt;nginx -s reload&lt;/code&gt; 로 새로운 설정정보를 리로딩시에 발생하게 되는 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;과연-nginx-는-graceful-shutdown-을-원활히-지원하는게-맞을까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%BC%EC%97%B0-nginx-%EB%8A%94-graceful-shutdown-%EC%9D%84-%EC%9B%90%ED%99%9C%ED%9E%88-%EC%A7%80%EC%9B%90%ED%95%98%EB%8A%94%EA%B2%8C-%EB%A7%9E%EC%9D%84%EA%B9%8C&quot; aria-label=&quot;과연 nginx 는 graceful shutdown 을 원활히 지원하는게 맞을까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;과연 Nginx 는 Graceful Shutdown 을 원활히 지원하는게 맞을까?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://nginx.org/en/docs/control.html&quot;&gt;Nginx 공식문서&lt;/a&gt; 에서도 설명하기를, Nginx 는 graceful shutdown 을 통해서 다운타임이 절대 발생하지 않는, 즉 &lt;code class=&quot;language-text&quot;&gt;제로 다운타임(zero-downtime)&lt;/code&gt; 을 지원한다고 주장하고 있습니다. 하지만, Nginx 가 gracefully 한 shutdown 을 지원해준다는 말은 완전히 맞는말은 아닙니다. 솔직히 저는 graceful 하다는 느낌도 들지 않습니다.&lt;/p&gt;
&lt;h3 id=&quot;nginx-는-다운타임이-발생하는데&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EB%8A%94-%EB%8B%A4%EC%9A%B4%ED%83%80%EC%9E%84%EC%9D%B4-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94%EB%8D%B0&quot; aria-label=&quot;nginx 는 다운타임이 발생하는데 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 는 다운타임이 발생하는데?&lt;/h3&gt;
&lt;p&gt;만약 공식 문서말처럼 Nginx 가 graceful 해서 제로타임을 지원해준다면, 위와 같이 &lt;code class=&quot;language-text&quot;&gt;nginx -s reload&lt;/code&gt; 를 수행시 아주 짧은 0.015초의 다운타임도 발생하지 않았어야합니다. 이는 맞는말이라고 보기엔 무리가 있습니다.&lt;/p&gt;
&lt;p&gt;사실 Nginx 에서 왜 다운타임이 발생하는지 최근에 알아냈습니다. 이에 관해서 조만간 다운타임이 왜 발생하고, 어떻게해야 이렇게 정말 짧은 다운타임 마저도 제거할 수 있는지 해결책도 자세하게 따로 다루어보고자 합니다. 사실 이번 주제는 이 해결첵을 알아내는데까지 고민했던 흔적과, 그로인해 Nginx 의 내부 메커니즘을 깊게 학습한 경험을 기록하고 공유하고자 작성한 것이라고 보는게 맞을것같습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;추가-개선사항&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B6%94%EA%B0%80-%EA%B0%9C%EC%84%A0%EC%82%AC%ED%95%AD&quot; aria-label=&quot;추가 개선사항 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;추가 개선사항&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Nginx-1995%EB%85%84-%EC%97%AD%EC%82%AC%EB%B6%80%ED%84%B0-%EB%9C%AF%EC%96%B4%EB%B3%B4%EB%8A%94-Nginx-%EB%93%B1%EC%9E%A5%EB%B0%B0%EA%B2%BD%EB%B6%80%ED%84%B0-%EB%82%B4%EB%B6%80-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98%EA%B9%8C%EC%A7%80&quot;&gt;[Nginx] 1995년 역사부터 뜯어보는 Nginx : 등장배경부터 내부 메커니즘까지&lt;/a&gt; 에서 언급했듯이, Nginx 는 1.7.11 이상부터 Thread Pool 을 지원해주며, 현재 안정화된 오픈 버전인 1.1 버전 이상부터 http2 를 지원해준다. 그러나 Thread Pool 은 OS 호환이 잘 안되며, 아직 안정성이 잘 검토되지 않아서 불안정 한것으로 판단되어서 제외했습니다.&lt;/p&gt;
&lt;p&gt;http2 도 추가적으로 성능을 개선할 수 있는 고려사항입니다. 만일 적용이 가능하다면 아래와 같이 80번 listen socket 에 http2 를 명시해주면 됩니다. 이 또한 고려사항에 있으나, 충분히 다운타임이 개선된 것으로 판단되어 이 내용은 튜닝 리스트에서 제외했습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;listen &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt; http2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nginx.com/faq/how-does-zero-downtime-configuration-testingreload-in-nginx-plus-work/&quot;&gt;Nginx Docs : How does zero-downtime configuration testing/reload in NGINX Plus work?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://nginx.org/en/docs/control.html&quot;&gt;Nginx Docs : Changing Configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[SpringBoot Graceful Shutdown]]></title><description><![CDATA[학습배경 [CI/CD] Jenkins 와 Nginx 를 활용한 Blue/Green 자동화 배포 아키텍처를 수동으로 구축하기 (feat. SpringBoot) 에서 Blue/Green 아키텍처를 직접 구축했습니다. 하지만 Jmeter…]]></description><link>https://haon.site/haon/infra/ci-cd/springboot-graceful/</link><guid isPermaLink="false">https://haon.site/haon/infra/ci-cd/springboot-graceful/</guid><pubDate>Fri, 26 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/CICD-Jenkins-%EC%99%80-Nginx-%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-BlueGreen-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC-%EC%9E%90%EB%8F%99%ED%99%94-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B5%AC%EC%B6%95&quot;&gt;[CI/CD] Jenkins 와 Nginx 를 활용한 Blue/Green 자동화 배포 아키텍처를 수동으로 구축하기 (feat. SpringBoot)&lt;/a&gt; 에서 Blue/Green 아키텍처를 직접 구축했습니다. 하지만 Jmeter 로 테스트시 직면했던 &lt;code class=&quot;language-text&quot;&gt;다운타임(DownTime)&lt;/code&gt; 문제와 &lt;code class=&quot;language-text&quot;&gt;요청처리 문제&lt;/code&gt;가 발생했는데, 이를 어떻게 해결하고자 했는지 다루어보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;다운타임downtime--response-실패-발생-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%9A%B4%ED%83%80%EC%9E%84downtime--response-%EC%8B%A4%ED%8C%A8-%EB%B0%9C%EC%83%9D-%EC%83%81%ED%99%A9&quot; aria-label=&quot;다운타임downtime  response 실패 발생 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다운타임(DownTime) &amp;#x26; Response 실패 발생 상황&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/aec5224a-6d2e-4b4f-b2e8-5e741fc3019d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이전에 진행했던 Blue/Green 아키텍처입니다. 신버전 프로세스가 정상적으로 실행되었는지 Health Check 를 실행한 후 Nginx 의 트래픽 분산 방향을 신버전 서버에다 분산시켰었습니다. 마지막으로 기존 구버전 프로세스는 더 이상 필요없는 것이므로 &lt;code class=&quot;language-text&quot;&gt;즉각적으로 kill&lt;/code&gt; 합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7f52d612-294d-4eae-9704-75d26b431aaa/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그런데 정말 이상합니다. Health Check 에 성공하고 문제가 없다고 판단되었을때 안전하게 리버스프록시의 방향을 변경시켰음에도 불구하고, 다운타임이 0.2 초나 발생했습니다. 또한, &lt;strong&gt;구버전 프로세스에서 처리중이였던 요청들이 거절당하고 Response 받는데에 실패했다는 문제가 발생&lt;/strong&gt;했습니다.&lt;/p&gt;
&lt;h3 id=&quot;파이프라인의-구버전-프로세스-kill-코드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8%EC%9D%98-%EA%B5%AC%EB%B2%84%EC%A0%84-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-kill-%EC%BD%94%EB%93%9C&quot; aria-label=&quot;파이프라인의 구버전 프로세스 kill 코드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파이프라인의 구버전 프로세스 kill 코드&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;nginx_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;echo &apos;set \\\$service_url http://${deployment_target_ip}:8080;&apos; &gt; /etc/nginx/conf.d/service-url.inc &amp;amp;&amp;amp; service nginx reload&quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&quot;Switch the reverse proxy direction of nginx to ${deployment_target_ip} 🔄&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${deployment_target_ip}&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${blue_ip}&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
then
	 ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;green_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k 8080/tcp&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
	ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blue_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k 8080/tcp&quot;&lt;/span&gt;
fi
echo &lt;span class=&quot;token string&quot;&gt;&quot; ✅ 구버전 프로세스를 종료하고, 신버전 프로세스로 교체합니다.&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이전에 작성했던 코드를 보면, 리버스프록시 방향이 변경되고나서 구버전 프로세스를 &quot;즉각&quot; kill 하고 있습니다. 문제는 &quot;즉각&quot; 적으로 kill 하는데에 있습니다. 구버전 프로세스는 기존에 요청받은 요청 내용들을 모두 처리하고나서 천천히 종료되어야하는데, 기존 요청들은 모두 처리하지도 못하고 위 명령으로 인해 바로 kill 되고 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;springboot-graceful-shutdown&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#springboot-graceful-shutdown&quot; aria-label=&quot;springboot graceful shutdown permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SpringBoot Graceful Shutdown&lt;/h2&gt;
&lt;p&gt;이 문제를 해결하기 위해선, &lt;strong&gt;프로세스가 아직 처리중인 요청이 있다면 바로 종료되지 않고, 해당 요청들을 모두 처리후 안전하게 종료되어야합니다.&lt;/strong&gt; 스프링부트 2.3.0 Release 부터는 &lt;code class=&quot;language-text&quot;&gt;Graceful Shutdown&lt;/code&gt; 기능을 제공해서 이 문제에 대한 해결책을 제공하고 있습니다. 즉, 스프링부트는 프로세스 종료 요청이 들어왔을 때 추가적으로 유입되는 요청을 거절하면서, 기존에 처리중이던 요청을 모두 처리한 뒤에야 안전하게 프로세스를 종료합니다.&lt;/p&gt;
&lt;h3 id=&quot;applicationyml-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#applicationyml-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;applicationyml 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;application.yml 설정&lt;/h3&gt;
&lt;p&gt;설정은 간단합니다. 아래와 같이 application.yml 에서 graceful 옵션을 적용시켜주면 끝입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;server&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  shutdown&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; graceful&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;타임아웃-추가설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%83%80%EC%9E%84%EC%95%84%EC%9B%83-%EC%B6%94%EA%B0%80%EC%84%A4%EC%A0%95&quot; aria-label=&quot;타임아웃 추가설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;타임아웃 추가설정&lt;/h3&gt;
&lt;p&gt;위와 같이 graceful shutdown 을 지정해줬을때, 만약 &lt;code class=&quot;language-text&quot;&gt;교착상태(Deadlock)&lt;/code&gt; 과 같은 상황에 빠지면 어떻게될까요? 구버전 프로세스는 정상적으로 종료되지 못하고 무한정 대기하는 상태에 빠지게 될것입니다. 이를위해, 아래와 같이 프로세스에 대한 타임아웃도 설정할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;spring&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  lifecycle&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    timeout&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;per&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;shutdown&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;phase&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;s&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;리눅스의-프로세스-종료-signal&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%EB%88%85%EC%8A%A4%EC%9D%98-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%A2%85%EB%A3%8C-signal&quot; aria-label=&quot;리눅스의 프로세스 종료 signal permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리눅스의 프로세스 종료 Signal&lt;/h2&gt;
&lt;p&gt;그런데, 위와같이 Graceful Shutdown 을 적용한다고 해서 바로 문제가 해결되지는 않습니다. 이 이유는, 구버전 프로세스를 강제로 종료시켜버리는 &lt;code class=&quot;language-text&quot;&gt;SIGKILL&lt;/code&gt; 시그널 을 사용했었기 떄문입니다.&lt;/p&gt;
&lt;h3 id=&quot;리눅스의-시그널signal&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%EB%88%85%EC%8A%A4%EC%9D%98-%EC%8B%9C%EA%B7%B8%EB%84%90signal&quot; aria-label=&quot;리눅스의 시그널signal permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리눅스의 시그널(Signal)&lt;/h3&gt;
&lt;p&gt;리눅스 OS 에서 시그널은 프로세스에게 특정 이벤트를 알리는데 사용되는 메커니즘입니다. 시그널은 프로세스간 통신, 예외처리, 프로세스 관리등 다양한 용도로 사용됩니다. 몇가지 중요한 리눅스 시그널을 나열해보면 다음과 같습니다.&lt;/p&gt;
&lt;h4&gt;SIGKILL&lt;/h4&gt;
&lt;p&gt;프로세스를 즉시 종료시키는 시그널입니다. 프로세스의 의사와 상관없이 프로세스를 강제로 종료해버리는 시그널입니다. 이 시그널은 프로세스에게 어떤 조치를 취하기도 전에 즉시 종료하라고 요청합니다.&lt;/p&gt;
&lt;h4&gt;SIGTERM&lt;/h4&gt;
&lt;p&gt;프로세스를 안전하게 종료시키는 시그널입니다. 프로세스가 현재 사용중인 자원을 해제하고, 데이터를 저장하는 등의 모든 작업을 안전하게 수행한 뒤에야 프로세스를 종료시키는 것입니다. 즉, 프로세스를 종료시키기 전에 해당 시그널을 핸들링하는 특이사항이 있다면, 해당 사항을 모두 처리후에 안전하게 프로세스를 종료시킵니다.&lt;/p&gt;
&lt;h4&gt;SIGINT&lt;/h4&gt;
&lt;p&gt;인터럽트 시그널로, 주로 터미널에서 Ctrl+C를 누르는 동작으로 생성됩니다. 대화형 프로세스에게 종료를 요청하는 시그널입니다.&lt;/p&gt;
&lt;h3 id=&quot;기존에-sigkill-을-사용하던-파이프라인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EC%A1%B4%EC%97%90-sigkill-%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8D%98-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8&quot; aria-label=&quot;기존에 sigkill 을 사용하던 파이프라인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기존에 SIGKILL 을 사용하던 파이프라인&lt;/h3&gt;
&lt;p&gt;이전에 작성했던 파이프라인 코드를 다시 살펴봅시다. 보면 &lt;code class=&quot;language-text&quot;&gt;-k&lt;/code&gt; 옵션을 통해 구버전 프로세스를 종료시키는 모습을 볼 수 있는데, 이는 추가적인 별도의 옵션이 없다면 &lt;code class=&quot;language-text&quot;&gt;SIGKILL&lt;/code&gt; 시그널로 프로세스를 강제 종료시키는 것입니다. 즉, 구버전 프로세스가 처리중이였던 여분의 요청들이 남아있음에도 불구하고 바로 kill 해버렸던 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${deployment_target_ip}&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${blue_ip}&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
then
	 ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;green_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k 8080/tcp&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
	ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blue_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k 8080/tcp&quot;&lt;/span&gt;
fi
echo &lt;span class=&quot;token string&quot;&gt;&quot; ✅ 구버전 프로세스를 종료하고, 신버전 프로세스로 교체합니다.&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;sigterm-을-적용한-파이프라인-개선-코드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sigterm-%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%9C-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EA%B0%9C%EC%84%A0-%EC%BD%94%EB%93%9C&quot; aria-label=&quot;sigterm 을 적용한 파이프라인 개선 코드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SIGTERM 을 적용한 파이프라인 개선 코드&lt;/h3&gt;
&lt;p&gt;SIGKILL 을 사용한다면, 당연히 구버전 프로세스에게 분산된 요청들의 일부는 정상 수행될 수 없을것입니다. 저희는 &lt;code class=&quot;language-text&quot;&gt;SIGTERM&lt;/code&gt; 시그널을 사용하면 구버전 프로세스가 요청받은 내용을 모두 처리 및 응답후, 자원까지 해제한 후에 종료되도 문제가 없을때가 되었을때 안전하게 종료됩니다.&lt;/p&gt;
&lt;p&gt;앞서 언급했기른 SIGTERM 은 해당 시그널을 핸들링하는 특이사항을 모두 처리후에 프로세스를 종료하는 시그널이라고 했었습니다. 이 &quot;특이사항&quot; 에 해당하는 것이 바로 스프링부트의 &lt;code class=&quot;language-text&quot;&gt;Graceful Shutdown&lt;/code&gt; 입니다. 만약 graceful shutdown 을 적용시키지 않았다면, SIGTERM 시그널은 특이사항이 없는 것으로 볼 수밖에 없기 때문에 구버전 프로세스에 대한 요청은 바로 수행되지 못하고 종료될겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${deployment_target_ip}&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${blue_ip}&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
then
	 ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;green_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k -TERM 8080/tcp&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
	ssh root@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blue_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fuser -s -k -TERM 8080/tcp&quot;&lt;/span&gt;
fi
echo &lt;span class=&quot;token string&quot;&gt;&quot; ✅ 구버전 프로세스를 종료하고, 신버전 프로세스로 교체합니다.&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;당연한 말이지만, SpringBoot 의 Graceful Shutdown 을 적용시켰다고 해서 구버전 프로세스가 모든 요청을 정상 수행후에 종료되는 것이 아닙니다. 만약 SIGKILL 을 그대로 사용했다면 구버전 프로세스는 SIGTERM 과 달리 강제로 종료되버리기 떄문에, graceful shutdown 도 자연스래 무시되버리는 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;다운타임--response-개선-결과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%9A%B4%ED%83%80%EC%9E%84--response-%EA%B0%9C%EC%84%A0-%EA%B2%B0%EA%B3%BC&quot; aria-label=&quot;다운타임  response 개선 결과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다운타임 &amp;#x26; Response 개선 결과&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/95cc8e3c-f361-4d6a-8564-1637354564b7/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;SIGETRM 시그널과 Graceful Shutdown 을 적용시킨 결과, 위와같이 구버전 프로세스에 유입된 요청은 모두 안전하게 처리후 종료되는 결과를 확인할 수 있게 되었습니다. 또한 다운타임이 0.3 초에서 0.2 초로 개선된 결과도 얻게 되었습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;아직-극복해야할-한계점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%84%EC%A7%81-%EA%B7%B9%EB%B3%B5%ED%95%B4%EC%95%BC%ED%95%A0-%ED%95%9C%EA%B3%84%EC%A0%90&quot; aria-label=&quot;아직 극복해야할 한계점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;아직 극복해야할 한계점&lt;/h2&gt;
&lt;p&gt;그러나, 여전히 일부 요청은 거절되고 다운타임이 아직 0.2초나 존재한다는 한계가 존재합니다. 아직 다운타임이 존재하는 이유는 Nginx 로 인한 원인으로 추측되고 있으니, Nginx 의 성능 튜닝과 네트워킹 문제를 개선하는 방향으로 개선후 다시 이를 주제삼아 재밌는 포스팅 내용을 다루어보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.spring.io/spring-boot/docs/current/reference/html/web.html#web.graceful-shutdown&quot;&gt;SpringBoot Document : graceful-shutdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/spring-boot-shutdown&quot;&gt;Shutdown a Spring Boot Application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sas-study.tistory.com/459&quot;&gt;Spring Boot server.shutdown graceful 옵션&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://joojimin.tistory.com/24&quot;&gt;SpringBoot 를 우아하게 종료시켜보자!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[헥사고날 아키텍처에서 영속성 어뎁터 구현하기]]></title><description><![CDATA[학습배경 [Hexagonal Architecture] 헥사고날 아키텍처에서 인커밍 웹 어댑터(Adapter…]]></description><link>https://haon.site/haon/architecture/hexagonal/outgoing/</link><guid isPermaLink="false">https://haon.site/haon/architecture/hexagonal/outgoing/</guid><pubDate>Sat, 13 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Hexagonal-Architecture-%ED%97%A5%EC%82%AC%EA%B3%A0%EB%82%A0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%97%90%EC%84%9C-%EC%9D%B8%EC%BB%A4%EB%B0%8D-%EC%96%B4%EB%8C%91%ED%84%B0Adapter-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0&quot;&gt;[Hexagonal Architecture] 헥사고날 아키텍처에서 인커밍 웹 어댑터(Adapter) 를 컨트롤러로 구현하기&lt;/a&gt; 에서 인커밍 어댑터중에 웹 어댑터를 구현했습니다. 이어서 영속성 어댑터를 통해 헥사고날의 기초 아키텍처 구성을 완성시켜보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;영속성-어댑터&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%81%EC%86%8D%EC%84%B1-%EC%96%B4%EB%8C%91%ED%84%B0&quot; aria-label=&quot;영속성 어댑터 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;영속성 어댑터&lt;/h2&gt;
&lt;p&gt;헥사고날에서 영속성 어댑터는 &lt;code class=&quot;language-text&quot;&gt;&quot;주도되는&quot;&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;&quot;아웃고잉&quot;&lt;/code&gt; 어댑터입니다. 애플리케이션 서비스는 영속성 기능을 사용하기 위해 포트 인터페이스 (아웃고잉 포트) 를 구현하며, 해당 포트 인터페이스는 영속성 어댑터 클래스에 의해 구현됩니다.&lt;/p&gt;
&lt;h3 id=&quot;의존성-역전&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%97%AD%EC%A0%84&quot; aria-label=&quot;의존성 역전 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;의존성 역전&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/2f12e187-a6c6-42cc-8bbe-a04311696be6/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;포트는 사실상 애플리케이션 서비스와 영속성 코드 사이의 간접적인 계층입니다. 매번 언급했듯이 영속성 문제에 신경쓰지 않고 도메인 코드를 개발하기 위해, 즉 영속성 계층에 대한 코드 의존성을 제거하도록 이러한 간접 계층을 추가하고 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;영속성-어댑터의-책임&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%81%EC%86%8D%EC%84%B1-%EC%96%B4%EB%8C%91%ED%84%B0%EC%9D%98-%EC%B1%85%EC%9E%84&quot; aria-label=&quot;영속성 어댑터의 책임 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;영속성 어댑터의 책임&lt;/h2&gt;
&lt;p&gt;영속성 어댑터는 일반적으로 다음과 같은 역할을 수행합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;아웃고잉 포트로부터 입력을 받는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;2.입력을 데이터베이스 포맷(ex. JPA 엔티티 객체) 로 변환한다.&lt;/li&gt;
&lt;li&gt;3.입력을 데이터베이스로 보낸다.&lt;/li&gt;
&lt;li&gt;4.데이터베이스 출력을 애플리케이션 서비스 계층에 대한 포맷으로 변환한다.&lt;/li&gt;
&lt;li&gt;5.출력을 아웃고잉 포트에게 리턴한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;영속성 어댑터는 아웃고잉 포트를 통해 입력을 전달받습니다. 이때 &lt;code class=&quot;language-text&quot;&gt;입력모델&lt;/code&gt;은 포트 인터페이스가 지정한 도메인 엔티티나, 또는 특정 데이터베이스 연산 전용 객체가 될 것입니다.&lt;/p&gt;
&lt;p&gt;그러고 영속성 어댑터는 전달받은 입력을 SQL 쿼리문으로 변환하거나, DB 를 변경하는데 사용할 수 있는 형식으로 입력을 변환시킬겁니다. 예를들어 JPA 를 사용하는 경우, 입력을 JPA 엔티티 객체로 변환하겠죠. 만약 JPA 를 사용하지 않아도, jdbcTemplate 과 같이 입력모델을 평범한 SQL 구문으로 변환해서 DB 에 쿼리를 보내도 됩니다.&lt;/p&gt;
&lt;p&gt;그 뒤로, 영속성 어댑터는 DB 에 쿼리를 날리고 쿼리 결과를 받아오게 됩니다. 그리고 DB 로 부터 받은 결과값을 포트에 정의된 출력모델로 변환해서 서비스 계층에 응답하게 되는 메커니즘입니다.&lt;/p&gt;
&lt;p&gt;이때 가장 중요한것은, 영속성 어댑터의 입력모델이 영속성 어댑터 내부에 있는것이 아니라 에플리케이션 코어(아웃고잉 포트)에 있다는 점입니다. 따라서 &lt;strong&gt;영속성 어댑터 내부를 변경하는 것이 코어에 영향을 미치지 않습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;또 &lt;strong&gt;입출력 모델이 영속성 어댑터가 아닌, 애플리케이션 코어에 위치해 있다는 점&lt;/strong&gt;을 기억합시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;아웃고잉-포트-세부-분할하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%84%EC%9B%83%EA%B3%A0%EC%9E%89-%ED%8F%AC%ED%8A%B8-%EC%84%B8%EB%B6%80-%EB%B6%84%ED%95%A0%ED%95%98%EA%B8%B0&quot; aria-label=&quot;아웃고잉 포트 세부 분할하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;아웃고잉 포트 세부 분할하기&lt;/h2&gt;
&lt;p&gt;스프링부트 애플리케이션에서 어떻게 구현할지 코드를 직접 보기전에, 유용한 설계 방법을 짚고 넘어갑시다.&lt;/p&gt;
&lt;h3 id=&quot;isp-를-적용한-플러그-앤드-플레이-plug-and-play&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#isp-%EB%A5%BC-%EC%A0%81%EC%9A%A9%ED%95%9C-%ED%94%8C%EB%9F%AC%EA%B7%B8-%EC%95%A4%EB%93%9C-%ED%94%8C%EB%A0%88%EC%9D%B4-plug-and-play&quot; aria-label=&quot;isp 를 적용한 플러그 앤드 플레이 plug and play permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ISP 를 적용한 플러그 앤드 플레이 (Plug and Play)&lt;/h3&gt;
&lt;p&gt;우선 &lt;strong&gt;각 서비스는 실제로 필요한 포트 인터페이스에만 최소한으로 의존해야합니다.&lt;/strong&gt; 즉, 포트를 하나로 놓고 그 안에 여러 기능을 위한 메소드를 엄청 만들어서 모든 서비스가 해당 포트에만 의존해야하는 설계가 아니라, 여러 기능에 대한 포트를 여러개로 분할해서 각 서비스가 본인에게 알맞는 포트에만 최소한으로 의존하게 만들자는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e1189726-6760-4354-ae45-7056965a381d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;만약 하나의 아웃고잉 포트 인터페이스에 모든 데이터베이스 연산을 모아두면 모든 서비스가 실제로는 필요하지 않은 메소드에 의존하게 됩니다. 그렇게 된다면, DB 연산에 의존하는 각 서비스는 인터페이스에서 단 하나의 메소드만 사용하더라도 &lt;code class=&quot;language-text&quot;&gt;&quot;넓은&quot; 포트 인터페이스&lt;/code&gt;에 의존성을 갖게 됩니다. 코드에 불필요한 의존성이 생길 수 있게 되는것입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6a7d6a0c-d9a0-4970-8b76-f15239037bd2/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이때 바로 &lt;code class=&quot;language-text&quot;&gt;ISP (인터페이스 분리 원칙)&lt;/code&gt; 을 적용하면, 클라이언트는 오로지 본인이 필요로하는 메소드만 알면 되도록 넓은 인터페이스를 &lt;code class=&quot;language-text&quot;&gt;특화된 좁은 포트 인터페이스&lt;/code&gt;로 분리할 수 있게됩니다. 즉, ISP 를 적용하면 각 서비스는 필요한 최소한의 포트에만 의존하게 됩니다. 이렇게 매우 좁은 포트를 만드는것은 &lt;code class=&quot;language-text&quot;&gt;플러그 앤드 플레이(plug-and-play)&lt;/code&gt; 경험으로 만들며, 서비스 코드를 짤때는 그저 필요한 포트에만 꽂기만하면 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;스프링부트-영속성-어댑터-구현하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%98%81%EC%86%8D%EC%84%B1-%EC%96%B4%EB%8C%91%ED%84%B0-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0&quot; aria-label=&quot;스프링부트 영속성 어댑터 구현하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링부트 영속성 어댑터 구현하기&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Hexagonal-Architecture-%ED%97%A5%EC%82%AC%EA%B3%A0%EB%82%A0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%97%90%EC%84%9C-%EC%9D%B8%EC%BB%A4%EB%B0%8D-%EC%96%B4%EB%8C%91%ED%84%B0Adapter-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0&quot;&gt;[Hexagonal Architecture] 헥사고날 아키텍처에서 인커밍 웹 어댑터(Adapter) 를 컨트롤러로 구현하기&lt;/a&gt; 에서 다루었듯이, 도메인 계층과 웹 계층을 구현했다면 지금부터 영속성 계층을 구현해 볼 상황입니다. 지금부터 애플리케이션을 간단히 설계해봅시다.&lt;/p&gt;
&lt;h3 id=&quot;프로젝트-개요&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B0%9C%EC%9A%94&quot; aria-label=&quot;프로젝트 개요 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프로젝트 개요&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/3a0928d6-b93d-4257-80d2-68dfa585969e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;프로젝트 구성을 간단히 짚어보자면 위와 같습니다. adapter.out 을 보면 Account 와 Billing 관련 어댑터가 구현된 것을 볼 수 있습니다. 또 AccountRepository 를 볼 수 있는데, 이는 추후 또 살펴보겠지만 Spring Data JPA 의 인퍼페이스입니다. 또한 port.out 에 아웃고잉 포트가 존재하는 것을 볼 수 있는데, 이는 아웃고잉 포트를 의미합니다.&lt;/p&gt;
&lt;p&gt;모든 폴더의 클래스 및 인터페이스를 살펴보지는 말고, 영속성 계층과 그와 관련한 애플리케이션 계층의 일부를 살펴봅시다.&lt;/p&gt;
&lt;h3 id=&quot;account&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#account&quot; aria-label=&quot;account permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Account&lt;/h3&gt;
&lt;p&gt;우선 도메인 엔티티인 Account 를 다시 보고 넘어가야합니다. 이전까지 저는 도메인 엔티티를 JPA 의 @Entity 어노테이션을 붙인 영속성 계층의 엔티티에다 일부 비즈니스 로직을 포함시킨 개념인것으로 착각하고 있었습니다.&lt;/p&gt;
&lt;p&gt;하지만 둘은 엄연히 다릅니다. 도메인 엔티티는 DDD 에서 비즈니스 핵심 로직을 나타내는 객체입니다. 반면 영속성 계층의 엔티티는 데이터베이스와의 상호작용을 처리하고 영속성과 관련된 기술적인 책임을 지닙니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;usecase&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Account&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 비즈니스 규칙 검증을 위한 메소드&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 비즈니스 규칙 : &quot;출금 계좌는 초과 인출되어서는 안된다&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;withdraw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; sendPrice&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; remainPrice&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; sendPrice &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; remainPrice&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;loadaccountport-updateaccountstateport&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#loadaccountport-updateaccountstateport&quot; aria-label=&quot;loadaccountport updateaccountstateport permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LoadAccountPort, UpdateAccountStatePort&lt;/h3&gt;
&lt;p&gt;아웃고잉 포트를 담당하는 2개의 인터페이스입니다. 이때 LoadAccountPort 를 보면 Money 라는 리턴타입이 있는데, 이는 애플리케이션 계층에 출력을 리턴해주기 위한 출력 모델입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoadAccountPort&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getRemainMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; accountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UpdateAccountStatePort&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateAccountRemainMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;money&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#money&quot; aria-label=&quot;money permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Money&lt;/h3&gt;
&lt;p&gt;아웃고잉 포트의 출력모델인 Money 는 아래와 같이 간단히 구현해줬습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; savedPrice&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; savedPrice&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;savedPrice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; savedPrice&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;accountjpaentity&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#accountjpaentity&quot; aria-label=&quot;accountjpaentity permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AccountJpaEntity&lt;/h3&gt;
&lt;p&gt;영속성 계층에 해당하는 엔티티입니다. 도메인 엔티티와는 엄연히 다른 개념임을 주의하고 넘어가야합니다. 앞서 언급했듯이, 영속성 계층의 엔티티는 데이터베이스와의 상호작용을 처리하고 영속성과 관련된 기술적인 기능을 담당합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;account&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Data&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@AllArgsConstructor&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AccountJpaEntity&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GeneratedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenerationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;AUTO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 어떤 유저에 대한 계좌인지&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; savedPrice&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 계좌 잔여량&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;accountrepository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#accountrepository&quot; aria-label=&quot;accountrepository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AccountRepository&lt;/h3&gt;
&lt;p&gt;기본적인 CRUD 기능과 DB 에서 원하는것을 로드하기 위해 스프링부트에서 제공하는 인터페이스인 Spring Data JPA 를 사용했습니다. 스프링부트는 이 레포지토리 인터페이스에 대한 구현체를 자동으로 제공하는 마법을 부립니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AccountRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JpaRepository&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AccountJpaEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// @Query(&quot;select m from AccountJpaEntity m where m.userId = :userId&quot;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;AccountJpaEntity&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findAccountJpaEntityByUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;accountpersistenceadapter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#accountpersistenceadapter&quot; aria-label=&quot;accountpersistenceadapter permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AccountPersistenceAdapter&lt;/h3&gt;
&lt;p&gt;이번에 다루는 내용중의 가장 핵심인 영속성 어댑터입니다. 보듯이 이 어댑터는 LoadAccountPort, UpdateAccountStatePort 라는 2개의 아웃고잉 포트를 구현하면서, 의존성 역전을 하게됩니다. 또 직전에 살펴본 AccountRepository 인터페이스를 통해 데이터베이스와 원하는 결과를 추출할 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;updateAccountRemainMoney 메소드를 살펴보면서, &lt;code class=&quot;language-text&quot;&gt;영속성 어댑터의 책임&lt;/code&gt; 이 잘 지켜지고 있는지를 봅시다. 우선 파라미터인 userId, money 를 보면 입력을 받게되고, accountRespository 를 활용해서 입력을 데이터베이스 포맷으로 변환 및 전송하는 것을 볼 수 있습니다. 즉, 입력(userId, money) 를 JPA 엔티티 객체인 AccountJpaEntity 로 변환하게 됩니다.&lt;/p&gt;
&lt;p&gt;또 updateAccountRemainMoney 에는 출력값이 단순히 boolean 타입이지만, getRemainMoney 메소드를 보면 DB 로 부터 리턴받은 결과 (AccountJpaEntity) 를 애플리케이션 포맷인 Money 로 변환하고 리턴하는 모습을 볼 수 있습니다. 이 Money 는 아웃고잉 포트인 LoadAccountPort 에서 규약해놓은 것이므로, 이 영속성 어댑터에서는 Money 타입으로 반드시 애플리케이션 계층에다 리턴해야합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AccountPersistenceAdapter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoadAccountPort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UpdateAccountStatePort&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AccountRepository&lt;/span&gt; accountRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getRemainMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; accountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;AccountJpaEntity&lt;/span&gt; accountJpaEntity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; accountRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt; money &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accountJpaEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSavedPrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateAccountRemainMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;token comment&quot;&gt;// 입력을 데이터베이스 포맷(JPA 엔티티 객체) 로 변환&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;AccountJpaEntity&lt;/span&gt; accountJpaEntity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; accountRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findAccountJpaEntityByUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; remainPrice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; accountJpaEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSavedPrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        accountJpaEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setSavedPrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remainPrice &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        accountRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accountJpaEntity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// 출력모델을 애플리케이션 계층에 리턴&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;데이터베이스-트랜잭션-처리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%B2%98%EB%A6%AC&quot; aria-label=&quot;데이터베이스 트랜잭션 처리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 트랜잭션 처리&lt;/h2&gt;
&lt;p&gt;트랜잭션은 하나의 특정한 유즈케이스에 대해서 일어나는 모든 쓰기 작업에 걸쳐 있어야합니다. 그래야지 그 중 하나라도 실패할경우 다 같이 롤백될 수 있기 때문입니다. 스프링에서 제공하는 선언전 트랜잭션 &lt;code class=&quot;language-text&quot;&gt;@Transactional&lt;/code&gt; 어노테이션을 애플리케이션 서비스 클래스에 붙여서, 스프링이 모든 public 메소드를 트랜잭션으로 감싸게 해줍시다.&lt;/p&gt;
&lt;p&gt;영속성 어댑터는 어떤 데이터베이스 연산이 같은 유스케이스에 포함되는지 알지 못하기 때문에, 언제 트랜잭션을 열고 닫을지 결정할 수 없습니다. 따라서 이 책임은 영속성 어댑터 호출을 관장하는 서비스에 위임해야하는 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SendMoneyService&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SendMoneyUseCase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;도메인 코드에 플러그인처럼 동작하는 영속성 어댑터를 만들면 도메인 코드가 영속성 코드와 관련된 것으로부터 분리되어 풍부한 도메인 모델을 만들 수 있습니다. 또한 &lt;code class=&quot;language-text&quot;&gt;좁은 포트 인터페이스&lt;/code&gt;를 사영하면 포트마다 다른 방식으로 구현할 수 있는 유연함이 생깁니다.&lt;/p&gt;
&lt;p&gt;심지어 포트 뒤에서 애플리케이션이 모르는데도 다른 영속성 기술로 바꿔치기 하는것도 가능하죠. &lt;code class=&quot;language-text&quot;&gt;포트의 명세&lt;/code&gt; 만 잘 지켜진다면 영속성 계층 전체를 교체할 수도 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;아직-해결하지-못한-궁금증-포인트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%84%EC%A7%81-%ED%95%B4%EA%B2%B0%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EA%B6%81%EA%B8%88%EC%A6%9D-%ED%8F%AC%EC%9D%B8%ED%8A%B8&quot; aria-label=&quot;아직 해결하지 못한 궁금증 포인트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;아직 해결하지 못한 궁금증 포인트&lt;/h2&gt;
&lt;p&gt;사실 앞서 언급했듯이 도메인과 영속성 엔티티는 서로 분리되어야하는 것이 맞다고 했으나, 아직 정확한 이해를 하지 못했습니다. 도메인에 Account 에 JPA 어노테이션인 @Entity 를 붙여서 이를 그대로 데이터베이스에 엔티티로 저장하면 안될까? 라는 생각이 아직도듭니다. 추후 학습하겠지만, 이렇게 분리하는 이유는 &lt;code class=&quot;language-text&quot;&gt;매핑하지 않기 전략&lt;/code&gt; 을 위해서라고 합니다. 조만간 이 궁금증을 꼭 해결해봐야 겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;매핑하기 않기 전략 : 도메인 엔티티와 영속성 엔티티의 구분을 통해 어떻게 풍부한 도메인 설계가 가능해지는 것인가?&lt;/li&gt;
&lt;li&gt;바운디드 컨텍스트&lt;/li&gt;
&lt;li&gt;애그리거트&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[헥사고날 아키텍처에서 인커밍 어뎁터 구현하기]]></title><description><![CDATA[학습배경 [Hexagonal Architecture] 헥사고날 아키텍처에서 유즈케이스(UserCase…]]></description><link>https://haon.site/haon/architecture/hexagonal/incoming/</link><guid isPermaLink="false">https://haon.site/haon/architecture/hexagonal/incoming/</guid><pubDate>Tue, 09 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Hexagonal-Architecture-%ED%97%A5%EC%82%AC%EA%B3%A0%EB%82%A0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%97%90%EC%84%9C-%EC%9C%A0%EC%A6%88%EC%BC%80%EC%9D%B4%EC%8A%A4UserCase-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0&quot;&gt;[Hexagonal Architecture] 헥사고날 아키텍처에서 유즈케이스(UserCase) 구현하기&lt;/a&gt; 에서는 헥사고날의 코어에 위치한 핵심 계층에 속한 도메인과 유즈케이스를 구현헤봤습니다. 이에 이어서, &lt;code class=&quot;language-text&quot;&gt;웹 어댑터(인커밍 어댑터)&lt;/code&gt; 를 구현하고 헥사고날에 대해 더 깊이있는 학습을 진행하고자 포스팅을 진행합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;웹-어댑터&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%B9-%EC%96%B4%EB%8C%91%ED%84%B0&quot; aria-label=&quot;웹 어댑터 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;웹 어댑터&lt;/h2&gt;
&lt;p&gt;웹 어댑터는 외부로부터 요청을 받아 애플리케이션 코어를 호출하고 무슨일을 해야하는지 알려줍니다. 이떄 제어흐름은 웹 어댑터에 있는 컨트롤러에서 애플리케이션 계층에 있는 서비스로 흐르죠. &lt;strong&gt;애플리케이션 계층은 웹 어댑터가 통신할 수 있는 특정 포트를 제공해주며, 서비스는 이 포트를 구현하며 웹 어댑터는 이 포트를 호출할 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;의존성-역전&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%97%AD%EC%A0%84&quot; aria-label=&quot;의존성 역전 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;의존성 역전&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/fe6823af-3235-4874-b741-5336827bf567/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;예를들어 위와같이 각 서비스는 인커밍 포트 인터페이스를 구현하고 있으며, 웹 어댑터들은 본인의 기호에 알맞게 원하는 포트들을 의존하며 호출할 수 있게됩니다. 이를통해 저희는 서비스와 인커밍 포트 사이에서 &lt;code class=&quot;language-text&quot;&gt;의존성 역전 원칙&lt;/code&gt; 이 적용된 것을 발견할 수 있습니다. 또 DIP 가 적용됨으로써 코어 계층을 보호할 수 있게 되는 것이죠.&lt;/p&gt;
&lt;h3 id=&quot;웹-어댑터가-아웃고잉-어댑터-역할도-동시에-수행하는-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%B9-%EC%96%B4%EB%8C%91%ED%84%B0%EA%B0%80-%EC%95%84%EC%9B%83%EA%B3%A0%EC%9E%89-%EC%96%B4%EB%8C%91%ED%84%B0-%EC%97%AD%ED%95%A0%EB%8F%84-%EB%8F%99%EC%8B%9C%EC%97%90-%EC%88%98%ED%96%89%ED%95%98%EB%8A%94-%EC%83%81%ED%99%A9&quot; aria-label=&quot;웹 어댑터가 아웃고잉 어댑터 역할도 동시에 수행하는 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;웹 어댑터가 아웃고잉 어댑터 역할도 동시에 수행하는 상황&lt;/h3&gt;
&lt;p&gt;참고로 알아야 할 것은, 웹소캣과 같이 실시간 데이터를 웹 어댑터에 보내야하는 것들은 인커밍 포트가 아닌 아웃고잉 포트를 거치게 됩니다. 즉 서비스가 아닌 웹 어댑터가 포트(아웃고잉 포트) 를 구현하게 되면서, 웹 어댑터가 인커밍 어댑터이면서 동시에 아웃고잉 어댑터가 되는데 이러한 특이 케이스들은 이번에 고려하지 않겠습니다. &lt;strong&gt;다시말해, 웹 어댑터가 일반적인 케이스인 인커밍 어댑터 역할만 한다고 가정하겠습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;포트port-계층은-왜-필요할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8F%AC%ED%8A%B8port-%EA%B3%84%EC%B8%B5%EC%9D%80-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;포트port 계층은 왜 필요할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;포트(Port) 계층은 왜 필요할까?&lt;/h3&gt;
&lt;p&gt;또한 의존성 역전 원칙이 적용됨으로써, 제어흐름이 왼쪽에서 오른쪽 방향으로 흐르기 때문에 웹 어댑터가 유스케이스를 직접 호출할 수 있게됩니다. 그렇다면 왜 어댑터와 유스케이스 사이에 또 다른 간접 계층(포트) 를 넣어야할까 의문점이 생길 수 있는데, 이는 &lt;strong&gt;애플리케이션 코어가 외부 세계와 통신할 수 있는곳에 대한 명세가 바로 포트이기 떄문입니다.&lt;/strong&gt; 포트를 적절한 곳에 위치시키면, 외부와 어떤 통신이 일어나고 있는지 정확히 알 수 있고, 이는 레거시코드를 다루는 유지보수 엔지니어에게는 정말 소중한 정보입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;웹-어댑터의-책임&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%B9-%EC%96%B4%EB%8C%91%ED%84%B0%EC%9D%98-%EC%B1%85%EC%9E%84&quot; aria-label=&quot;웹 어댑터의 책임 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;웹 어댑터의 책임&lt;/h2&gt;
&lt;p&gt;다음으로 웹 어댑터가 수행하는 책임에 대해 파악하고, 그를 기반으로 스프링부트 애플리케이션에서 어떻게 구현을 할 수 있는지 알아봅시다. 우선 웹 어댑터는 일반적으로 다음과 같은 역할을 수행합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;HTTP 요청을 자바 객체로 변환&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;입력 (웹 어댑터의 입력 모델) 유효성 검증&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;입력 (웹 어댑터의 입력 모델) 을 유즈케이스 입력모델로 변환&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;유스케이스 호출&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;유스케이스의 출력을 HTTP 로 변환&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;HTTP 응답을 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;웹 어댑터는 &lt;code class=&quot;language-text&quot;&gt;HTTP 요청을 수신&lt;/code&gt;하고, 해당 HTTP 요청에 담긴 여러 정보를 기반으로 자바의 객체로 받아들이게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;입력-유효성-검증&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%85%EB%A0%A5-%EC%9C%A0%ED%9A%A8%EC%84%B1-%EA%B2%80%EC%A6%9D&quot; aria-label=&quot;입력 유효성 검증 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;입력 유효성 검증&lt;/h3&gt;
&lt;p&gt;그러고 &lt;code class=&quot;language-text&quot;&gt;입력 유효성 검증&lt;/code&gt;을 수행하는데, 이때 말하는 입력 유효성 검증은 이전에 다루었던 유즈케이스에서의 유효성 검증과는 엄연히 다른것입니다. 여기서 말하는 것은 웹 어댑터의 입력 모델에 대한 유효성 검증이며, 당연히 유즈케이스에서의 입력 모델에 대한 유효성 검증과는 다릅니다. 유즈케이스의 입력 모델과는 구조나 의미가 완전히 다를 수 있기 떄문에, 또 다른 유효성 검증을 웹 어댑터에서 입력 모델에 대해 수행해야합니다.&lt;/p&gt;
&lt;p&gt;또한 유스캐이스 입력 모델에서 했던 유효성 검증을 똑같이 웹 어댑터에서도 구현해야하는 것은 아닙니다. 대신에, &lt;strong&gt;웹 어댑터의 입력 모델을 유스케이스의 입력 모델로 변환할 수 있다는 것을 검증해야합니다.&lt;/strong&gt; 이 변환이 불가능하다면 유효성 검증 에러가 되는것이죠.&lt;/p&gt;
&lt;h3 id=&quot;유스케이스-호출&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9C%A0%EC%8A%A4%EC%BC%80%EC%9D%B4%EC%8A%A4-%ED%98%B8%EC%B6%9C&quot; aria-label=&quot;유스케이스 호출 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;유스케이스 호출&lt;/h3&gt;
&lt;p&gt;그렇게 입력 유효성 검증에 성공했다면, 자연스래 웹 어댑터의 입력모델은 유스케이스의 입력 모델로 변환되어 있을겁니다. 변환된 해당 인풋모델을 유스케이스로 전송하고
( = 유스케이스를 호출하고 ), 유스케이스가 일련을 과정을 수행후 뱉어낸 출력을 웹 어댑터에게 가져다주면 그를 HTTP 로 변환해서 클라이언트에게 응답을 보내면 될 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;컨트롤러-구현하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0&quot; aria-label=&quot;컨트롤러 구현하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨트롤러 구현하기&lt;/h2&gt;
&lt;p&gt;그럼 지금부터 인커밍 어댑터를 스프링부트 애플리케이션에서 간단하게 구현해보겠습니다. 인커밍 어댑터의 종류는 정말 다양하겠지만, 앞서 언급해왔던 웹 어댑터를 가정했으며 어떻게해야 더 좋은 아키텍처 구조를 만들 수 있을지에 대해 간단히 언급해보고자 합니다.&lt;/p&gt;
&lt;h3 id=&quot;컨트롤러-세분화-분할&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC-%EC%84%B8%EB%B6%84%ED%99%94-%EB%B6%84%ED%95%A0&quot; aria-label=&quot;컨트롤러 세분화 분할 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨트롤러 세분화 분할&lt;/h3&gt;
&lt;p&gt;웹 어댑터는 컨트롤러로 구현됩니다. 이 &lt;strong&gt;컨트롤러는 가능한 많게 세분화하여 배치하는 것이 좋습니다.&lt;/strong&gt; 여러 컨트롤러로 세분화시킴으로써, 각 컨트롤러가 소수의 기능만 가지면서 타 컨트롤러와 적은 자원(클래스 등등)을 공유하도록 하는것이 좋다는 것입니다.&lt;/p&gt;
&lt;p&gt;예를들어 AccountController 를 정의했다고 해봅시다. 이 컨트롤러 안에는 다음과 같이 계좌와 관련된 여러 Rest API 기능을 제공한다고 해보죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AccountController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoadAccountQuery&lt;/span&gt; loadAccountQuery&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 인커밍 포트 유즈케이스들&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ListAccountQuery&lt;/span&gt; listAccountQuery&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GetAccountBalanceQuery&lt;/span&gt; getAccountBalanceQuery&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SendMoneyUseCase&lt;/span&gt; sendMoneyUseCase&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/accounts/{accountId}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;AccountResource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getAccount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@PathVariable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;accountId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; accountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; loadAccountQuery&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAccountInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/accounts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AccountResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;listAccounts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; listAccountQuery&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAccountListInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@PostMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/accounts/create&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;AccountResource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerAccount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestBody&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AccountResource&lt;/span&gt; accountResource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;어렇게 되면 한 컨트롤러내에 계좌와 관련한 모든것들이 있어서 &quot;계좌관련 기능&quot; 어댑터 자체를 찾는것은 쉽습니다. 그러나 이 컨트롤러안에 계좌와 관련된 API 가 1000개가 넘어간다, &lt;strong&gt;단번에 계좌의 특정 프로덕션(기능)을 제공하는 특정 API 를 찾기란 꽤나 어렵습니다.&lt;/strong&gt; 또 컨트롤러에 코드가 많으면 그에 해당하는 테스크 코드도 정말 많아지므로, 특정 프로덕션(기능)의 API 에 대한 테스트 코드를 작성후, 추후에 그 테스트 코드가 어딨는지 찾기란 정말 어려워집니다.&lt;/p&gt;
&lt;p&gt;또한 위처럼 &quot;계좌 관련&quot; &lt;strong&gt;모든 연산을 단일 컨트롤러에 넣는것은 동일한 데이터 구조(모델 클래스) 재활용을 촉진하게 된다는 문제점을 지닙니다.&lt;/strong&gt; 위에서는 AccountResource 라는 동일한 모델 클래스를 여러 연산에서 사용하고 있는데, 각 연산은 이 모델 클래스에서 각각 필요없는 필드들이 있을텐데, 그들에 대해서 어떻게 처리해야할지 혼동을 줄 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;유즈케이스를-고려한-컨트롤러&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9C%A0%EC%A6%88%EC%BC%80%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%9C-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC&quot; aria-label=&quot;유즈케이스를 고려한 컨트롤러 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;유즈케이스를 고려한 컨트롤러&lt;/h3&gt;
&lt;p&gt;또 &lt;strong&gt;메소드와 클래스명은 최대한 유즈케이스를 반영헤서 작성하는 것이 좋습니다.&lt;/strong&gt; 예를들어 계좌를 새롭게 생성하는 컨트롤러 및 API 를 개발하고 싶은경우, 위와같이 AccountController 라고 포괄적인 의미를 가진 이름을 정의하는 것이 아니라, RegisterAccountController 와 같이 정의하고, 그 안의 메소드명은 regstierAccount 와 같이 더 명확히 식별가능한 이름으로 지어주는 것이 좋겠죠?&lt;/p&gt;
&lt;h3 id=&quot;유즈케이스를-고려한-전용-모델-클래스-정의&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9C%A0%EC%A6%88%EC%BC%80%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%9C-%EC%A0%84%EC%9A%A9-%EB%AA%A8%EB%8D%B8-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%A0%95%EC%9D%98&quot; aria-label=&quot;유즈케이스를 고려한 전용 모델 클래스 정의 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;유즈케이스를 고려한 전용 모델 클래스 정의&lt;/h3&gt;
&lt;p&gt;마찬가지 이유로, &lt;strong&gt;각 컨트롤러마다 전용 모델 클래스를 보유하는 것이 좋습니다.&lt;/strong&gt; 가령 RegisterAccountController 라는 컨트롤러를 만들었을떄, 해당 컨트롤러에 대한 전용 클래스로 AccountResource 가 아니라 RegisterAccountResource 라는 전용 모델 클래스를 부여하는 것이 좋을겁니다.&lt;/p&gt;
&lt;p&gt;또한 전용 모델 클래스는 컨트롤러의 패키지에 대한 private 으로 선언하는 것이 좋습니다. 그를통해 실수로 다른 컨트롤러 재사용될 일이 없도록 하는것이죠. private 으로 지정함으로써 뜬금없이 다른 컨트롤러에서 사용하려는 상황이 발생해도, 사용을 막아버릴 수 있는것입니다.&lt;br&gt;
RegisterAccountController 에서 전용 클래스로 RegisterAccountResource 를 사용한다면, DeleteAccountResource 와 같은 다른 컨트롤러의 전용 모델 클래스를 사용할 수 없게 된다는 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;컨트롤러-리팩토링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81&quot; aria-label=&quot;컨트롤러 리팩토링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨트롤러 리팩토링&lt;/h2&gt;
&lt;p&gt;앞서 살펴봤던 개선법들을 적용해서, AccountController 를 리팩토링 해봅시다. 간단한 예시를 보이기위해 자세한 예외처리 및 응답방식은 간단히 구현했으며, 저희가 이번에 짚어야할 핵심은 어떻게 컨트롤러가 분할되었으며, 그 안의 메소드나 전용 모델 클래스가 생성되었는가 입니다.&lt;/p&gt;
&lt;h3 id=&quot;inquiryaccountcontroller&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#inquiryaccountcontroller&quot; aria-label=&quot;inquiryaccountcontroller permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;InquiryAccountController&lt;/h3&gt;
&lt;p&gt;우선 계좌 정보를 조회하는 InquiryAccountController 를 생성했습니다. 인커밍 포트 인터페이스를 2개 의존하고 있죠. 이 컨트롤러에 대해서 전용 모델 클래스로 InquiryAccountResource 를 지정해줬으며, 메소드 이름도 신경써서 get 이라는 키워드를 붙여줬습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InquiryAccountController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoadAccountQuery&lt;/span&gt; loadAccountQuery&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 인커밍 포트 인터페이스를 의존&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ListAccountQuery&lt;/span&gt; listAccountQuery&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/accounts/{accountId}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;InquiryAccountResource&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getAccount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@PathVariable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;accountId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; accountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; loadAccountQuery&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAccountInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/accounts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InquiryAccountResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getListAccounts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; listAccountQuery&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAccountListInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;registeraccountcontroller&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#registeraccountcontroller&quot; aria-label=&quot;registeraccountcontroller permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RegisterAccountController&lt;/h3&gt;
&lt;p&gt;또 다음으로 계좌 생성에 대한 컨트롤러를 별도로 세분화해서 정의해줬는데, 전용 모델 클래스로 RegisterAccountResource 를 만들어준것을 볼 수 있습니다. 앞서 말했듯이 컨트롤러는 세분화하는것이 좋기 때문에, &quot;게좌 관련&quot; 이라는 동일한 기능상에 있음에도 별도로 컨트롤러를 분리해줬습니다.&lt;/p&gt;
&lt;p&gt;또 앞서 설명한 방식대로 웹 어댑터 입력모델을 유즈케이스 입력모델로 변환후 유즈케이스를 호출해줬습니다. 호출된 유즈케이스는 이를 구현한 서비스 구현내용에 따라서 비즈니스 로직이 실행되며 영속성 어댑터(아웃고잉 어댑터) 를 통해 DB 에 계좌 정보를 생성후, 적절한 리턴값을 다시 유즈케이스에게 반환해주었을 겁니다. 마지막으로 반환값에 따른 유즈케이스의 출력(리턴값) 을 HTTP 로 변환후 클라이언트에게 응답해주는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterAccountController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterAccountUseCase&lt;/span&gt; registerAccountUseCase&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 인커밍 포트 인터페이스를 의존&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@PostMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/account&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerAccountResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestBody&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterAccountResource&lt;/span&gt; registerAccountResource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 웹 어댑터 입력모델(RegisterAccountResource) 을 유즈케이스 입력모델(RegisterAccountCommand) 로 변환&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RegisterAccountCommand&lt;/span&gt; registerAccountCommand &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterAccountCommand&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;registerAccountResource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAccountNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; registerAccountResource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUsername&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 유즈케이스 호출&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; responseName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; registerAccountUseCase&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerAccount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;registerAccountResource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 유스케이스의 출력을 HTTP 로 변환후 응답&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; responseName &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;님의 계좌 생성이 완료되었습니다!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;registeraccountresource&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#registeraccountresource&quot; aria-label=&quot;registeraccountresource permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RegisterAccountResource&lt;/h3&gt;
&lt;p&gt;추가적으로 계좌 생성 컨트롤러에 대한 전용 모델 클래스인 RegisterAccountResource 는 아래처럼 간단히 정의해줬습니다. 유저이름과 계좌번호를 입력받죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterAccountResource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; username&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; accountNumber&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;registeraccountcommand&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#registeraccountcommand&quot; aria-label=&quot;registeraccountcommand permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RegisterAccountCommand&lt;/h3&gt;
&lt;p&gt;바로 이어지는 유즈케이스 입력모델은 아래처럼 정의해줬습니다. initMoney 라는 필드가 추가된것을 볼 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Setter&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@AllArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterAccountCommand&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; username&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; accountNumber&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; initMoney&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 초기 잔액&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;이렇듯 애플리케이션 웹 어댑터를 구현할때, HTTP 요청을 애플리케이션의 유즈케이스에 대한 메소드 호출로 변환하고 결과를 다시 HTTP 로 변환하고, 어떤 도메인 로직도 수행하지 않는 어댑터를 만들고 있다는점을 염두에 둬야합니다.&lt;/p&gt;
&lt;p&gt;또 애플리케이션 계층은 HTTP 에 대한 상세정보를 노출시키지 않도록 HTTP 와 관련된 작업을 해서는 안됩니다. 이를 지켜야지 필요할 경우 웹 어댑터를 다른 어댑터로 쉽게 교채할 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;또 웹 컨트롤러를 세분화할시, 모델을 공유하지 않는 여러 작은 전용 모델 클래스로 만드는 것에 익숙해져야합니다. 작은 클래스들은 더 파악하기 쉽고 테스트하기 쉽기 떄문입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습할-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%A0-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습할 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습할 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;영속성 어댑터 구현하기&lt;/li&gt;
&lt;li&gt;전용 모델 클래스에 대한 pricate, public 접근 제한자 지정&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[헥사고날 아키텍처에서 유즈케이스 구현하기]]></title><description><![CDATA[학습배경 지난 [Hexagonal Architecture] 헥사고날 아키텍처로 어떻게 유지.보수 가능한 소프트웨어를 개발할까? 에서 다루었듯이, 에서는  를 할수도 있고 풍부하거나(rich) 빈약한(anemic…]]></description><link>https://haon.site/haon/architecture/hexagonal/usecase/</link><guid isPermaLink="false">https://haon.site/haon/architecture/hexagonal/usecase/</guid><pubDate>Sun, 07 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;지난 &lt;a href=&quot;https://velog.io/@msung99/Hexagonal-Architecture-%ED%97%A5%EC%82%AC%EA%B3%A0%EB%82%A0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EB%A1%9C-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%9C%A0%EC%A7%80.%EB%B3%B4%EC%88%98-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4%EB%A5%BC-%EA%B0%9C%EB%B0%9C%ED%95%A0%EA%B9%8C&quot;&gt;[Hexagonal Architecture] 헥사고날 아키텍처로 어떻게 유지.보수 가능한 소프트웨어를 개발할까?&lt;/a&gt; 에서 다루었듯이, &lt;code class=&quot;language-text&quot;&gt;헥사고날 아키텍처&lt;/code&gt;에서는 &lt;code class=&quot;language-text&quot;&gt;DDD(도메인 주도 설계)&lt;/code&gt; 를 할수도 있고 풍부하거나(rich) 빈약한(anemic) 도메인 모델을 구현하는등 다양한 우리만의 방식을 만들어 낼 수 있습니다. 이번에는 헥사고날 아키텍처의 스타일에서 유스케이스(UseCase) 를 구현하는 방법을 학습하는겸 포스팅을 진행해보고자 합니다.&lt;/p&gt;
&lt;p&gt;헥사고날 아키텍처는 DDD 아키텍처에 적합하기 때문에, 도메인 엔티티를 만드는것으로 시작한 후 해당 도메인 엔티티를 중심으로 유즈케이스를 구현해보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;유즈케이스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9C%A0%EC%A6%88%EC%BC%80%EC%9D%B4%EC%8A%A4&quot; aria-label=&quot;유즈케이스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;유즈케이스&lt;/h2&gt;
&lt;p&gt;먼저, 본격적인 구현을 하기전에 일반적인 유즈케이스는 어떤 일들을 담당하는지 알아봅시다. 아래와 같은 플로우로 유즈케이스는 본인의 역할을 수행합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/03c18692-c0bf-4cd5-ac58-9b9e4178afcc/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;입력을 받는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;비즈니스 규칙을 검증한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;모델 상태를 조작한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;출력을 반환한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;유즈케이스는 인커밍 어댑터로부터 입력을 받습니다. 이떄 유즈케이스 코드는 도메인 로직에만 신경써야하므로, &lt;code class=&quot;language-text&quot;&gt;입력 유효성 검증&lt;/code&gt;으로 오염되면 안됩니다. 즉, 입력값에 대한 유효성 검증은 다른 곳에서 처리하게 됩니다.&lt;/p&gt;
&lt;p&gt;그러나 유즈케이스는 &lt;code class=&quot;language-text&quot;&gt;비즈니스 규칙&lt;/code&gt; 을 검증할 책임은 지닙니다. 또 도메인 엔티티와 해당 책임을 공유하죠.&lt;/p&gt;
&lt;p&gt;비즈니스 규칙을 검증하고나면, 유즈케이스는 입력을 기반으로 어떤 방법으로든 &lt;code class=&quot;language-text&quot;&gt;모델의 상태를 변경&lt;/code&gt;합니다. 일반적으로 도메인 객체의 상태를 바꾸고 영속성 어댑터를 통해 구현된 포트로 해당 상태를 전달해서 저장될 수 있게합니다. 몰론 이때 유즈케이스는 또 다른 아웃고잉 어댑터를 호출할 수도 있습니다.&lt;/p&gt;
&lt;p&gt;마지막으로 아웃고잉 어댑터에서 온 출력값을, 유즈케이스를 호출한 어댑터로 반환할 출력 객체로 변환하게됩니다.&lt;/p&gt;
&lt;p&gt;이 단계들에 기반하여, &quot;송금하기&quot; 유스케이스을 구현하는 방법을 지금부터 알아봅시다. 이때 &lt;code class=&quot;language-text&quot;&gt;넓은 서비스 문제&lt;/code&gt; 를 피하기 위해, 모든 유스케이스를 한 서비스 클래스에 모두 넣지않고, 각 유즈케이스별로 분리된 각각의 서비스로 만들어야한다는 점을 유의하고 넘어갑시다. (단, 이번에는 &quot;송금하기&quot; 유즈케이스 1개만을 구현한다는 점을 알고갑시다.)&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;프로젝트-폴더-구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%8F%B4%EB%8D%94-%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;프로젝트 폴더 구성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프로젝트 폴더 구성&lt;/h2&gt;
&lt;p&gt;폴더 구성은 아래와 같이 진행해줬습니다. 애플리케이션 계층안에 포트를 위한 패키지를 따로 정의하고, 또 그안에 인고잉 포트와 아웃고잉 포트를 위한 폴더를 따로 구성해줬습니다. 또한 서비스 폴더를 따로 만들어서, 그 안에 SendMoneyService 를 만들었습니다. 마지막으로 도메인을 관리하기 위한 도메인 폴더를 정의한것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;이떄 어댑터를 위한 패키지를 따로 구성하지 않았습니다. 이번 포스팅의 주목적은 헥사고날의 코어인 유즈케이스와 엔티티를 구현하는 것을 중점으로 다룬다고 했으므로, 어댑터는 제외했습니다. 어댑터에 대한 구성 및 구현은 추후에 다룰 예정입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/590c9d76-9fca-4279-bf01-2730e726c546/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;프로젝트-도식화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%8F%84%EC%8B%9D%ED%99%94&quot; aria-label=&quot;프로젝트 도식화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프로젝트 도식화&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ee8817cf-a2e8-4124-8be9-4a8254b07c0f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위 구성을 도식화해보면 이렇습니다. SendMoneyService 라는 하나의 서비스는 &quot;송금하기&quot; 라는 하나의 유즈케이스를 구현합니다. 또 해당 서비스는 도메인 모델을 변경하고, 변경된 상태를 저장하기 위해 아웃고잉 포트를 출력하게 됩니다.&lt;/p&gt;
&lt;p&gt;추후 코드를 직접 보면 알겠지만, 서비스는 인커밍 포트 인터페이스인 SendMoneyUseCase 를 구현하고, 아웃고잉 포트 인터페이스인 LoadAccountPort, UpdateAccountPort 를 final 필드로 보유하고 있습니다.&lt;/p&gt;
&lt;p&gt;또 인고잉 포트 패키지(application.port.in) 을 보면 SendMoneyCommand 클래스가 있는것을 볼 수 있는데, 이는 사용자로부터 입력을 받는 입력 모델(input model) 입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;도메인-엔티티&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%97%94%ED%8B%B0%ED%8B%B0&quot; aria-label=&quot;도메인 엔티티 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도메인 엔티티&lt;/h2&gt;
&lt;p&gt;지금부터 &quot;송금하기&quot; 에 대한 유즈케이스를 구현해봅시다. 실제 현업에서는 아래처럼 간단한 엔티티 설계가 이루어지지 않겠지만, 현 포스팅은 학습을 위한것이므로 간단히 설계를 진행해봤습니다.&lt;/p&gt;
&lt;h3 id=&quot;account-entity&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#account-entity&quot; aria-label=&quot;account entity permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Account Entity&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Account&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 비즈니스 규칙 검증을 위한 메소드&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 비즈니스 규칙 : &quot;출금 계좌는 초과 인출되어서는 안된다&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;withdraw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; sendPrice&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; remainPrice&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; sendPrice &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; remainPrice&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 도메인 엔티티를 정의했고, 사용자가 UI 에서 송금하기 버튼을 누를때마다 Account 엔티티의 money 필드의 계좌 잔고량에서 일정 금액이 빠져나가는 로직이 헥사고날의 코어에 만들어질겁니다.
이때 주의해야 할점은 &lt;code class=&quot;language-text&quot;&gt;도메인 엔티티&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;영속성 엔티티&lt;/code&gt; 는 엄연히 절대 다른 개념이라는 점 입니다. 사실 Account 엔티티에서 일정 금액이 인출되는 필드가 존재한다고 방금 말씀드렸는데, 이는 영속성 계층의 엔티티를 말하는것겁니다. @Entity 어노테이션이 붙은 것을 영속성 계층의 엔티티라고 말하는 것이죠. &lt;strong&gt;도메인 엔티티는 위처럼 비즈니스 규칙과 관련된 필드 및 메소드를 포함하고 정의합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;service&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#service&quot; aria-label=&quot;service permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Service&lt;/h2&gt;
&lt;p&gt;다음으로 이번의 핵심이라 할 수 있는 서비스에 대해 어떻게 구현했는지 알아봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;usecase&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;lombok&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RequiredArgsConstructor&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;transaction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;annotation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Transactional&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;usecase&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;in&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SendMoneyCommand&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;usecase&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;in&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SendMoneyUseCase&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;usecase&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LoadAccountPort&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;usecase&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UpdateAccountStatePort&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;usecase&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Account&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 서비스(OrderService) 는 인커밍 포트 인터페이스인 OrderUseCase 를 구현했다. =&gt; 하나의 서비스가 &quot;송금하기&quot; 라는 하나의 유즈케이스(를 구현했다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SendMoneyService&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SendMoneyUseCase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoadAccountPort&lt;/span&gt; loadAccountPort&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 아웃고잉 포트 인터페이스 : 계좌 정보 불러오기 기능 포함&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UpdateAccountStatePort&lt;/span&gt; updateAccountStatePort&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 아웃고잉 포트 인터페이스 : 해당 유저의 계좌 잔액을 최신화하기 기능 포함&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// -&gt; 이러한 아웃고잉 포트들은 아웃고잉(영속성) 어댑터와 연결될 것이다.&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SendMoneyCommand&lt;/span&gt; sendMoneyCommand&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1. 입력을 받는다&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 2. 비즈니스 규칙을 검증한다 (도메인 엔티티에서 검증한다. 또는 유스케이스 코드에서 도메인 엔티티 사용전에 검증함)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 이 경우는 도메인 엔티티에서 검증하는 경우를 사용했다.&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Account&lt;/span&gt; account &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; loadAccountPort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAccountInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sendMoneyCommand&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;account&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withdraw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sendMoneyCommand&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSendPrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 도메인 엔티티 Account 에서 정의한 비즈니스 규칙을 검증&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;계좌의 잔액이 부족합니다. 따라서 송금이 정상처리되지 않았습니다!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// -&gt; 유효성 검증이 실패할 경우 유효성 검증 예외를 던진다. 이 부분은 추후에 어댑터가 구현되면 사용자에게 제대로된 예외 에러 메시지를 리턴해주는 방식으로 리팩토링될 예정&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// 모델 상태 조작&lt;/span&gt;
        updateAccountStatePort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateAccountRemainMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sendMoneyCommand&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sendMoneyCommand&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSendPrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// 출력을 반환&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; account&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUsername&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;님의 송금이 정상처리 되었습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;보듯이 SendMoneyService 서비스는 SendMoenyUseCase 라는 인커밍 포트 인터페이스를 구현하고 있습니다.&lt;/p&gt;
&lt;p&gt;또 그 아래에는 아웃고잉 포트 인터페이스에 대한 변수를 final 필드로 선언한 모습을 볼 수 있습니다. 지금은 어댑터 구현방법을 다루지 않겠지만, 각 아웃고잉 포트들은 추후 구현에 따라서 영속성 어댑터와 연결될겁니다. LoadAccountPort 는 계좌를 불러오기 위해 포트이며, UpdateAccountStatePort 는 데이터베이스의 계좌 상태를 업데이트하기 위한 포트임을 알고갑시다.&lt;/p&gt;
&lt;h3 id=&quot;sendmoney&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sendmoney&quot; aria-label=&quot;sendmoney permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;sendMoney&lt;/h3&gt;
&lt;p&gt;또 sendMoney 라는 메소드를 선언한 것을 볼 수 있습니다. 송금을 위한 메소드로써 우선 웹 계층으로부터 SendMoneyCommand 라는 &lt;code class=&quot;language-text&quot;&gt;입력을 받게&lt;/code&gt;됩니다. 그러고 나서 아까 말했듯이, &lt;code class=&quot;language-text&quot;&gt;비즈니스 규칙&lt;/code&gt;을 검증하고, &lt;code class=&quot;language-text&quot;&gt;모델의 상태를 조작&lt;/code&gt;하며, 마지막으로 &lt;code class=&quot;language-text&quot;&gt;출력을 리턴&lt;/code&gt;하는 과정을 수행하게 됩니다.&lt;/p&gt;
&lt;p&gt;추후 설명하겠지만, 지금 보듯이 입력 유효성과 비즈니스 규칙이라는 키워드가 계속 등장하고 있습니다. 가뜩이나 이들이 무엇인지도 햇갈리는데, 언제 입력 유효성을 검증하고 또 언제 비즈니스 규칙을 검증해야하는지도 알아야해서 더욱이 혼동이 올 수 있습니다. 이들에 대해서도 이어서 설명드리겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;비즈니스-규칙-vs-입력-유효성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EC%A6%88%EB%8B%88%EC%8A%A4-%EA%B7%9C%EC%B9%99-vs-%EC%9E%85%EB%A0%A5-%EC%9C%A0%ED%9A%A8%EC%84%B1&quot; aria-label=&quot;비즈니스 규칙 vs 입력 유효성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비즈니스 규칙 vs 입력 유효성&lt;/h2&gt;
&lt;p&gt;간단하게 차이를 짚고 넘어갑시다. 우선 입력 유효성이란 말 그대로 입력된 인풋에 대한 유효성을 검증하는 것입니다. 이어서 설명할 내용이지만, 입력 유효성은 보통 애플리케이션 계층의 &lt;code class=&quot;language-text&quot;&gt;입력모델(input model)&lt;/code&gt; 에서 검증을 수행합니다.
반면 비즈니스 규칙이란 유즈케이스의 로직의 일부로써, 보통 &lt;code class=&quot;language-text&quot;&gt;도메인 엔티티&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;유즈케이스 코드&lt;/code&gt; 에서 도메인 엔티티를 사용하기전에 규칙 검증이 구현됩니다.&lt;/p&gt;
&lt;p&gt;입력 유효성을 검증하는 것은 구문상의 유효성을 검증하는 것이라고도 할 수 있으며, 반면 비즈니스 규칙은 유스케이스의 맥락 속에서 의미적인 유효성을 검증하는 일이라고 할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;예시&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%88%EC%8B%9C&quot; aria-label=&quot;예시 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;예시&lt;/h3&gt;
&lt;p&gt;&quot;출금 계좌는 초과 출금되어서는 안된다&quot; 라는 규칙은, 정의에 따르면 이 규칙은 출금 계좌와 입금 계좌가 존재하는지 확인하기 위해 &lt;strong&gt;도메인 모델의 현재 상태에 접근해야 하므로 비즈니스 규칙입니다.&lt;/strong&gt; 반면 &quot;송금되는 금액은 0보다 커야한다&quot; 라는 규칙은 &lt;strong&gt;모델에 접근하지 않고도 검증될 수 있기 때문에&lt;/strong&gt; 입력 유효성 검증으로 구현할 수 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;비즈니스 규칙을 검증하는 것은 도메인 모델의 현재 상태에 접근해야하는 반면,&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;입력 유효성 검증은 도메인 모델에 접근할 필요가 없다!&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;입력-유효성-검증&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%85%EB%A0%A5-%EC%9C%A0%ED%9A%A8%EC%84%B1-%EA%B2%80%EC%A6%9D&quot; aria-label=&quot;입력 유효성 검증 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;입력 유효성 검증&lt;/h2&gt;
&lt;h3 id=&quot;어디서-검증할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%94%94%EC%84%9C-%EA%B2%80%EC%A6%9D%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;어디서 검증할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어디서 검증할까?&lt;/h3&gt;
&lt;p&gt;입력을 어디서 검증할지 생각해보면, 결론부터 말하자면 애플리케이션 계층에서 검증해야 합니다. 더 정확하는 애플리케이션 계층의 &lt;code class=&quot;language-text&quot;&gt;입력 모델(input model)&lt;/code&gt; 에서 검증하는게 좋습니다. 애플리케이션 계층에서 입력 유효성을 검증해야 하는 이유는, 그렇지 하지 않을경우 애플리케이션 코어의 바깥쪽으로부터 유효하지 않은 입력값을 받게되고, 모델의 상태를 해칠 수 있기 때문입니다.&lt;/p&gt;
&lt;p&gt;가령 인커밍 어댑터에서 유스케이스에 입력을 전달하기 전에 유효성 검증을 구현하는 경우를 생각해봅시다. &lt;strong&gt;유스케이스는 하나 이상의 여러 어댑터에서 호출될텐데, 그러면 유효성 검증을 각 어댑터에서 전부 구현해야 하는 번거로움이 발생합니다.&lt;/strong&gt; 그 과정에서 실수를 범할 가능성이 굉장히 커지죠.&lt;/p&gt;
&lt;h3 id=&quot;입력-모델&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%85%EB%A0%A5-%EB%AA%A8%EB%8D%B8&quot; aria-label=&quot;입력 모델 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;입력 모델&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 입력 모델(Input Model)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SendMoneyCommand&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@NonNull&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 유효성 검증 어노테이션 및 코드를 통해 유스케이스 구현체 주위에 사실상 오류 방지 계층을 만들었다.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// (= 잘못된 입력을 호출자에게 돌려주는 유즈케이스 보호막 역할)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@NonNull&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; sendPrice&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;


    &lt;span class=&quot;token comment&quot;&gt;// 생성자 내에서 유효성 규칙을 검증한다.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SendMoneyCommand&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; orderPrice&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sendPrice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; orderPrice&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;requireGreaterThan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;orderPrice&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Bean의 유효성 검증 외에도 송금액이 0보다 큰지 검사하는 로직을 직접 구현함&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;requireGreaterThan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; sendPrice&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; sendPrice &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와같이 SendMoneyCommand 라는 입력모델을 구현하고, 입력모델 안의 생성자에서 유효성 검증을 진행할 수 있습니다. SendMoneyCommand 는 유즈케이스 API 의 일부이기 떄문에 인커밍 포트 패키지에 위치합니다. 따라서 유효성 검증이 애플리케이션 코어에 남아있지만, 선성한 유즈케이스 코드를 오염시키지는 않습니다.&lt;/p&gt;
&lt;p&gt;또 @NonNull 을 통해 유효성 검증을 편리하게 사용하고 있습니다. 이런 Bean Validation 을 활용한 방식만으로 특정 유효성 검증 규칙을 표현하기 충분치 않다면, 송금액이 0보다 큰 검사하는 등의 규칙은 위처럼 requireGreaterThan 과 같이 직접 만들어주면 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;유스케이스-보호막-역할&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9C%A0%EC%8A%A4%EC%BC%80%EC%9D%B4%EC%8A%A4-%EB%B3%B4%ED%98%B8%EB%A7%89-%EC%97%AD%ED%95%A0&quot; aria-label=&quot;유스케이스 보호막 역할 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;유스케이스 보호막 역할&lt;/h3&gt;
&lt;p&gt;이렇듯 입력 모델은 그 안의 유효성 검증 코드를 통해 유스케이스 구현체 주위에 사실상 오류 방지 계층을 만들어낸것입니다. 잘못된 입력을 호출자에게 돌려주는 유스케이스 보호막으로써 작용한다는 것이죠.&lt;/p&gt;
&lt;h3 id=&quot;각-유스케이스별로-전용-입력모델을-만들자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%81-%EC%9C%A0%EC%8A%A4%EC%BC%80%EC%9D%B4%EC%8A%A4%EB%B3%84%EB%A1%9C-%EC%A0%84%EC%9A%A9-%EC%9E%85%EB%A0%A5%EB%AA%A8%EB%8D%B8%EC%9D%84-%EB%A7%8C%EB%93%A4%EC%9E%90&quot; aria-label=&quot;각 유스케이스별로 전용 입력모델을 만들자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;각 유스케이스별로 전용 입력모델을 만들자&lt;/h3&gt;
&lt;p&gt;각기 다른 유스케이스에 동일한 입력모델을 사용하고 싶은 생각이 들때가 있습니다. 예를들어 &quot;계좌 등록하기&quot; 와 &quot;계좌 정보 업데이트하기&quot; 라는 2가지 유즈케이스를 보면, 둘다 거의 똑같은 계좌 상세 정보가 필요하게됩니다.&lt;/p&gt;
&lt;h4&gt;null 값을 허용해야하는 상황&lt;/h4&gt;
&lt;p&gt;하지만 엄밀히 둘의 차이점은 분명 존재합니다. &quot;계좌 등록하기&quot; 유즈케이스는 계좌를 귀속시킬 소유자의 ID 가 필요한 반면, &quot;게좌 정보 업데이트하기&quot; 유즈케이스는 업데이트할 계좌를 특정하기 위해 계좌 ID 정보를 필요로하죠. 따라서 두 유스케이스에서 동일한 모델을 공유할 경우 각각 소유자 ID 와 계좌 ID 에 null 값을 허용해야합니다.
이는 **불변 커맨트 객체의 필드에 대해서 null 을 유효한 상태로 받아들이는 것이므로 바람직하지 못합니다. **&lt;/p&gt;
&lt;p&gt;따라서 &lt;strong&gt;각 유스케이스 전용 입력모델을 만드는 것이 좋습니다.&lt;/strong&gt; 이는 유스케이스를 훨씬 명확하게 만들고 다른 유스케이스와의 결합도 제거해서 불필요한 부수효과가 발생하지 않게하죠.&lt;/p&gt;
&lt;h3 id=&quot;각-유스케이스별로-전용-출력모델을-만들자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%81-%EC%9C%A0%EC%8A%A4%EC%BC%80%EC%9D%B4%EC%8A%A4%EB%B3%84%EB%A1%9C-%EC%A0%84%EC%9A%A9-%EC%B6%9C%EB%A0%A5%EB%AA%A8%EB%8D%B8%EC%9D%84-%EB%A7%8C%EB%93%A4%EC%9E%90&quot; aria-label=&quot;각 유스케이스별로 전용 출력모델을 만들자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;각 유스케이스별로 전용 출력모델을 만들자&lt;/h3&gt;
&lt;p&gt;입력과 마찬가지로, 각 유스케이스별로 전용 출력모델을 만드는것도 중요합니다. 출력은 호출자에게 꼭 필요한 최소한의 데이터만 들고있는게 좋습니다.&lt;br&gt;
만약 여러 유스케이스들간에 같은 출력 모델을 공유하게되면 유스케이스들도 강하게 결합됩니다. 한 유스케이스에서 출력 모델에 새로운 필드가 필요해지면 이 값과 관련없는 다른 유스케이스에서도 이 필드를 처리해야합니다.&lt;br&gt;
즉, &lt;code class=&quot;language-text&quot;&gt;단일 책임원칙(SRP)&lt;/code&gt; 를 적용하고 모델을 분리해서 유지하는 것은 유스케이스의 결합을 제거하는데 큰 도움이 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;비즈니스-규칙-검증&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EC%A6%88%EB%8B%88%EC%8A%A4-%EA%B7%9C%EC%B9%99-%EA%B2%80%EC%A6%9D&quot; aria-label=&quot;비즈니스 규칙 검증 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비즈니스 규칙 검증&lt;/h2&gt;
&lt;h3 id=&quot;도메인-엔티티에서-규칙-정의&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%97%94%ED%8B%B0%ED%8B%B0%EC%97%90%EC%84%9C-%EA%B7%9C%EC%B9%99-%EC%A0%95%EC%9D%98&quot; aria-label=&quot;도메인 엔티티에서 규칙 정의 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도메인 엔티티에서 규칙 정의&lt;/h3&gt;
&lt;p&gt;그렇다면 비즈니스 규칙 검증은 어떻게 구현할까요? 가장 좋은 방법은, 앞서 살펴본 &quot;출금 계좌는 초과 인출되어서는 안된다&quot; 규칙에서처럼 &lt;strong&gt;비즈니스 규칙을 도메인 엔티티 안에 넣는것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Account&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GeneratedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenerationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; username&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 현 계좌의 유저이름&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; remainPrice&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 계좌 잔액 잔여량&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 비즈니스 규칙 검증을 위한 메소드&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 비즈니스 규칙 : &quot;출금 계좌는 초과 인출되어서는 안된다&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;withdraw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; sendPrice&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; sendPrice &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; remainPrice&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 Account 엔티티는 앞서 기존에 설명드렸던 엔티티에 withdraw() 라는 메소드를 추가했습니다. 이렇게 하면 이 규칙을 지켜야하는 비즈니스 로직 바로 옆에 규칙이 위치하기 때문에 위치를 정하는 것도 쉽고, 추론하기도 쉽습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// SendMoneyService 의 sendMoney() 메소드 일부 코드 추출&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Account&lt;/span&gt; account &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; loadAccountPort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAccountInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sendMoneyCommand&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;account&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withdraw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sendMoneyCommand&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSendPrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;계좌의 잔액이 부족합니다. 따라서 송금이 정상처리되지 않았습니다!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러고 정의한 withdraw() 를 위처럼 서비스에서 활용하면 되는것입니다.&lt;/p&gt;
&lt;h3 id=&quot;유스케이스-코드에서-규칙-정의&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9C%A0%EC%8A%A4%EC%BC%80%EC%9D%B4%EC%8A%A4-%EC%BD%94%EB%93%9C%EC%97%90%EC%84%9C-%EA%B7%9C%EC%B9%99-%EC%A0%95%EC%9D%98&quot; aria-label=&quot;유스케이스 코드에서 규칙 정의 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;유스케이스 코드에서 규칙 정의&lt;/h3&gt;
&lt;p&gt;위처럼 도메인 엔티티에서 비즈니스 규칙 검증을 정의해도 좋지만, &lt;strong&gt;유스케이스 코드에서 도메인 엔티티를 사용하기전에 검증을 시도&lt;/strong&gt;해도 좋습니다. 유효성을 거검증하는 코드를 호출하고, 유효성 검증이 실패할 경우 예외를 던집니다. 사용자와 통신하는 웹 어댑터는 이 예외를 에러 메시지로 사용자에게 보여주거나 적절한 다른 방법으로 처리합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Account&lt;/span&gt; account &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; loadAccountPort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAccountInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sendMoneyCommand&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;account&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRemainPrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; sendMoneyCommand&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSendPrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;계좌의 잔액이 부족합니다. 따라서 송금이 정상처리되지 않았습니다!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;읽기전용-유스케이스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%BD%EA%B8%B0%EC%A0%84%EC%9A%A9-%EC%9C%A0%EC%8A%A4%EC%BC%80%EC%9D%B4%EC%8A%A4&quot; aria-label=&quot;읽기전용 유스케이스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;읽기전용 유스케이스&lt;/h2&gt;
&lt;p&gt;앞서 살펴본 유스케이스 방식은 모델의 상태를 변경하는 과정이 포함되어있습니다. 그러나 읽기 전용 유스케이스를 구현하고 싶은 경우, 해당 과정이 생략될 것입니다.&lt;/p&gt;
&lt;p&gt;그런데 UI 에 계좌잔액을 표시하는 등의 작업을 진행하기위해 새로운 유스케이스를 구현하는 일은 꽤나 애매해집니다. 애플리케이션 코어의 관점에서 봤을떄, 그 작업은 정말 간단한 데이터 쿼리이므로 프로젝트 맥락에서 유스케이스로 간주되지 않는다면 실제 유스케이스와 구분하기위해 쿼리로 구현할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;쿼리-서비스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BF%BC%EB%A6%AC-%EC%84%9C%EB%B9%84%EC%8A%A4&quot; aria-label=&quot;쿼리 서비스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쿼리 서비스&lt;/h3&gt;
&lt;p&gt;이를 구현하는 방법은 여러가지가 있겠지만, 그 중 한가지 방법은 &lt;strong&gt;쿼리를 위한 인커밍 포트 인터페이스를 만들고, 그를 쿼리 서비스에 구현하는 것입니다.&lt;/strong&gt;
쿼리 서비스는 유스케이스 서비스와 동일한 방식으로 동작합니다. 인커밍 포트를 구현하면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GetAccountService&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GetAccountBalanceQuery&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoadAccountPort&lt;/span&gt; loadAccountPort&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Account&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getAccount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AccountId&lt;/span&gt; accountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; loadAccountPort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAccount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;어댑터 구현하는 방법&lt;/li&gt;
&lt;li&gt;builder 패턴을 어떻게 생성자에서 적용하는가?&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[헥사고날 아키텍처로 어떻게 유지.보수 가능한 소프트웨어를 개발할까?]]></title><description><![CDATA[헥사고날 아키텍처는 "의존성 역전"에서 부터 시작한다. 전통적 계층형 아키텍처 [Clean Architecture…]]></description><link>https://haon.site/haon/architecture/hexagonal/basic/</link><guid isPermaLink="false">https://haon.site/haon/architecture/hexagonal/basic/</guid><pubDate>Sat, 29 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;헥사고날 아키텍처는 &quot;의존성 역전&quot;에서 부터 시작한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;전통적-계층형-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%ED%86%B5%EC%A0%81-%EA%B3%84%EC%B8%B5%ED%98%95-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;전통적 계층형 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전통적 계층형 아키텍처&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Clean-Architecture-%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%97%90%EC%84%9C%EB%8A%94-%EC%9D%98%EC%A1%B4%EC%84%B1-%EB%AC%B8%EC%A0%9C%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%B4%EA%B2%B0%ED%95%A0%EA%B9%8C&quot;&gt;[Clean Architecture] 클린 아키텍처에서는 전통적 계층구조의 의존성 문제를 어떻게 해결했을까?&lt;/a&gt; 에서도 다루었듯이, 기존 전통적 구조는 많은 문제점을 지니고 있습니다.
&lt;code class=&quot;language-text&quot;&gt;도메인 중심 개발&lt;/code&gt; 이 먼저 선행되어야 의존성 문제를 벗어날 수 있으나, 아키텍처를 고려하지 않은 대부분의 프로젝트는 &lt;code class=&quot;language-text&quot;&gt;데이터베이스 중심적인 아키텍처&lt;/code&gt;를 구성한다는 것이죠.&lt;/p&gt;
&lt;h3 id=&quot;orm-에-의한-지름길-선택하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#orm-%EC%97%90-%EC%9D%98%ED%95%9C-%EC%A7%80%EB%A6%84%EA%B8%B8-%EC%84%A0%ED%83%9D%ED%95%98%EA%B8%B0&quot; aria-label=&quot;orm 에 의한 지름길 선택하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ORM 에 의한 지름길 선택하기&lt;/h3&gt;
&lt;p&gt;특히 ORM 프레임워크를 계층형 아키텍처와 결합하면, 비즈니스 규칙을 영속성 관점과 섞고 싶은 유혹을 쉽게 받게됩니다. ORM 에 의해 관리되는 엔티티들은 일반적으로 영속성 계층에 둡니다. 게층은 아래 방향으로만 접근 가능하기 떄문에, 도메인 계층에서는 이러한 엔티티에 접근할 수 있습니다.&lt;/p&gt;
&lt;p&gt;하지만 이렇게 되면 &lt;strong&gt;영속성 계층과 도메인 계층 사이에 강한 결합이 생기게 됩니다.&lt;/strong&gt; 영속성 코드가 사실상 도메인 코드아 녹아들어가서 둘 중 하나만 바꾸는 것이 어려워지죠.&lt;/p&gt;
&lt;h3 id=&quot;비대해지는-영속성-계층&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EB%8C%80%ED%95%B4%EC%A7%80%EB%8A%94-%EC%98%81%EC%86%8D%EC%84%B1-%EA%B3%84%EC%B8%B5&quot; aria-label=&quot;비대해지는 영속성 계층 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비대해지는 영속성 계층&lt;/h3&gt;
&lt;p&gt;전통적 계층형 아키텍처에서 허용되는 유일한 규칙은, 특정 계층에서는 같은 계층 또는 아래있는 계층에만 접근 가능하다는 것입니다. 따라서 상위 계층에 대한 컴포넌트를 접근하고 싶은경우, 해당 컴포넌트 계층을 한 단계 낮춰서 설계하고 당장의 개발을 쉽게하는 &lt;code class=&quot;language-text&quot;&gt;지름길은 택하는 상황&lt;/code&gt; 이 발생하게 됩니다.&lt;/p&gt;
&lt;p&gt;결국 수년에 걸쳐 개발된 프로젝트는, 대부분의 코드가 영속성 계층에 위치하면서 해당 계층에 비대해지는 현상이 발생합니다. 그러나, 이런 지름길 모드는 적어도 아키텍처에서는 좋은 모습이 아닐겁니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;의존성-역전&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%97%AD%EC%A0%84&quot; aria-label=&quot;의존성 역전 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;의존성 역전&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B3%A0-%EB%B0%94%EB%9D%BC%EB%B3%B4%EB%8A%94-SOLID-5%EB%8C%80-%EC%84%A4%EA%B3%84%EC%9B%90%EC%B9%99&quot;&gt;예제로 이해하는 SOLID 설계원칙, 그리고 스프링 DI 컨테이너의 등장&lt;/a&gt; 에서 다루었던 내용이지만, 모든 객체지향은 &lt;code class=&quot;language-text&quot;&gt;SOILD&lt;/code&gt; 로 귀결되는듯 합니다. 클린 아키텍처와 헥사고날 아키텍처도 결국 객체지향에 기반하며, SOLID 가 빠질 수 없습니다.&lt;/p&gt;
&lt;h3 id=&quot;srp-단일책임원칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#srp-%EB%8B%A8%EC%9D%BC%EC%B1%85%EC%9E%84%EC%9B%90%EC%B9%99&quot; aria-label=&quot;srp 단일책임원칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SRP 단일책임원칙&lt;/h3&gt;
&lt;p&gt;특히 아키텍처에서는 &lt;code class=&quot;language-text&quot;&gt;SRP(단일 책임원칙)&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;DIP(의존성 역전 원칙)&lt;/code&gt; 을 빼놓을 수 없습니다. SRP 단일 책임 원칙에 따라서, 특정 컴포넌트의 변경이 다른 컴포넌트에 영향을 미치는 파급력이 적어야한다는 뜻입니다. 하지만 아, 많은 코드, 특히 전통적 아키텍처 에서는 SRP 를 위반하기 때문에 시간이 갈수록 컴포넌트를 변경할 많은 이유가 쌓이게 되고, 유지.보수면에서 최악의 설계 구조를 가지게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;dip-의존성-역전-원칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dip-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%97%AD%EC%A0%84-%EC%9B%90%EC%B9%99&quot; aria-label=&quot;dip 의존성 역전 원칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DIP 의존성 역전 원칙&lt;/h3&gt;
&lt;p&gt;또 전통적 아키텍처 에서는 계층간의 의존성 방향이 &lt;strong&gt;상위계층에서 하위계층으로 향하고 있습니다.&lt;/strong&gt; 이는 도메인 계층이 영속성 계층을 의존하기 때문에, 영속성 계층에 변화가 일어날때마다 &lt;strong&gt;도메인 계층도 매번 변경되어야 일어나야 한다&lt;/strong&gt;는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ca994d89-62a9-456b-9c04-1d2f61f9d596/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이 상황을 방지하도록, DIP 는 해결책을 제공합니다. 도메인 계층에 인터페이스를 도입함으로써 의존성을 역전시킬 수 있고, 그 덕분에 영속성 영속성 계층이 도메인이 계층에 의존하게 됩니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;DIP : 코드상의 어떤 의존성이든 그 방향을 반대로 바꿀 수 (역전시킬 수) 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5f8b2872-b870-4739-a98c-4a046fbd310a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;반대로 위 케이스는 도메인 계층에 인터페이스를 도입함으로써 의존성을 역전시킨 경우입니다. 그 덕분에 영속성 계층이 도메인 계층에 의존하게 되죠. 이 묘수로 영속성 코드에 있는 숨막히는 의존성으로부터 도메인 로직을 해방시키게 된 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;클린-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;클린 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클린 아키텍처&lt;/h2&gt;
&lt;p&gt;로버트 C. 마틴의 &apos;클린 아키텍처&apos; 에서는 &lt;strong&gt;도메인 코드가 바깥으로 향하는 어떤 의존성도 없어야함을 강조&lt;/strong&gt;하고 있습니다. 대신 DIP, 즉 의존성을 역전시켜서 모든 의존성이 도메인 코드를 향하고 있게 합니다. 클린 아키텍처에서는 도메인 계층이 영속성이나 UI 같은 외부 계층과 철저히 분리돼야 하므로, &lt;strong&gt;애플리케이션의 엔티티에 대한 모델을 각 계층에서 유지보수해야합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;도메인-계층의-다른-계층과의-상호작용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EB%A9%94%EC%9D%B8-%EA%B3%84%EC%B8%B5%EC%9D%98-%EB%8B%A4%EB%A5%B8-%EA%B3%84%EC%B8%B5%EA%B3%BC%EC%9D%98-%EC%83%81%ED%98%B8%EC%9E%91%EC%9A%A9&quot; aria-label=&quot;도메인 계층의 다른 계층과의 상호작용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도메인 계층의 다른 계층과의 상호작용&lt;/h3&gt;
&lt;p&gt;가령 영속성 계층에서 ORM 프레임워크를 사용한다고 가정 해봅시다. 도메인 계층은 영속성 계층을 모르기 때문에, 도메인 계층에서 사용한 엔티티 클래스를 영속성 계층에서 함께 사용할 수 없고, 두 계층에서 각각 엔티티를 만들어야 합니다. &lt;strong&gt;즉, 도메인 계층과 영속성 계층이 데이터를 주고받을 때, 두 엔티티를 서로 변환해야 한다는 뜻입니다.&lt;/strong&gt; 이는 도메인 코드를 프레임워크에 특화된 의존성 문제로 부터 해방시킬 수 있는 해결책이되죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;헥사고날-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%97%A5%EC%82%AC%EA%B3%A0%EB%82%A0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;헥사고날 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;헥사고날 아키텍처&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4c3a2341-0f52-4fad-ac3d-4f894bbc3a58/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;앞서 살펴봤던 &lt;code class=&quot;language-text&quot;&gt;클린 아키텍처&lt;/code&gt;는 꽤나 추상적이고 모호한 표현들이 많습니다. 이런 아키텍처를 구체화해서 설명하는 &quot;육각형 아키텍처&quot; 에 대해 살펴봅시다.&lt;/p&gt;
&lt;p&gt;보면 알겠지마, &lt;a href=&quot;https://velog.io/@msung99/Clean-Architecture-%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%97%90%EC%84%9C%EB%8A%94-%EC%9D%98%EC%A1%B4%EC%84%B1-%EB%AC%B8%EC%A0%9C%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%B4%EA%B2%B0%ED%95%A0%EA%B9%8C&quot;&gt;[Clean Architecture] 클린 아키텍처에서는 전통적 계층구조의 의존성 문제를 어떻게 해결했을까?&lt;/a&gt; 에서도 다루었던 단위들이 대거 등장합니다. 다시 한번 되짚고 갑시다. 자세한 설명은 지난 포스팅을 먼저 참고하거 오시면 이해가 더 잘될겁니다.&lt;/p&gt;
&lt;h3 id=&quot;엔티티--유즈케이스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%94%ED%8B%B0%ED%8B%B0--%EC%9C%A0%EC%A6%88%EC%BC%80%EC%9D%B4%EC%8A%A4&quot; aria-label=&quot;엔티티  유즈케이스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;엔티티 &amp;#x26; 유즈케이스&lt;/h3&gt;
&lt;p&gt;육각형 안에는 도메인 &lt;code class=&quot;language-text&quot;&gt;엔티티&lt;/code&gt;와, 이와 상호작용하는 &lt;code class=&quot;language-text&quot;&gt;유즈케이스&lt;/code&gt;가 있습니다. 육각형에서 외부로 향하는 의존성이 없기 떄문에, 클린 아키텍처에서 제시한 &lt;code class=&quot;language-text&quot;&gt;의존성 규칙&lt;/code&gt;이 그대로 적용됩니다.&lt;/p&gt;
&lt;h3 id=&quot;어댑터&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%8C%91%ED%84%B0&quot; aria-label=&quot;어댑터 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어댑터&lt;/h3&gt;
&lt;p&gt;육각형 바깥에는 애플리케이션과 상호작용하는 다양한 어댑터들이 존재합니다.
웹 브라우저와 상호작용하는 &lt;code class=&quot;language-text&quot;&gt;웹 어뎁터&lt;/code&gt;도 있고, 반면 반대 위치에서 DB 와 상호작용하는 어댑터도 존재합니다. 이러한 어댑터는 크게 2종류로 구분됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Driving Adapter : 애플리케이션의 코어(도메인, 엔티티등) 을 호출하는 엔티티. 즉 웹 어댑터가 이에 해당합니다.&lt;/li&gt;
&lt;li&gt;Driven Adapter : 애플리케이션에 의해 주도되는 어댑터들입니다. 즉 영속성 어댑터가 이에 해당하죠.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;포트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8F%AC%ED%8A%B8&quot; aria-label=&quot;포트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;포트&lt;/h3&gt;
&lt;p&gt;코어와 어댑터들 간의 통신이 가능하려면 애플리케이션 코어가 포트를 제공해야합니다. &lt;code class=&quot;language-text&quot;&gt;주도하는 어댑터(driving adapter)&lt;/code&gt; 에게는 그러한 포트가 코어에 있는 유스케이스 클래스 중 하나에 구현되고 어댑터에 의해 호출되는 &lt;code class=&quot;language-text&quot;&gt;인터페이스&lt;/code&gt;가 될 것입니다.
또 &lt;code class=&quot;language-text&quot;&gt;주도되는 어댑터(driven adapter)&lt;/code&gt; 에게는 그러한 포트가 어댑터에 의해 구현되고 코어에 의해 호출되는 &lt;code class=&quot;language-text&quot;&gt;인터페이스&lt;/code&gt;가 될 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;가독성있는-헥사고날-패키지-구성하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%80%EB%8F%85%EC%84%B1%EC%9E%88%EB%8A%94-%ED%97%A5%EC%82%AC%EA%B3%A0%EB%82%A0-%ED%8C%A8%ED%82%A4%EC%A7%80-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0&quot; aria-label=&quot;가독성있는 헥사고날 패키지 구성하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;가독성있는 헥사고날 패키지 구성하기&lt;/h2&gt;
&lt;p&gt;당연한 말이지만, 코드를 보는 것만으로도 어떤 아키텍처인지 파악하고, 누구던지 어떤 한눈에 패키지 구조를 파악할 수 있는 구조를 지향하는게 좋을겁니다. 지금부터 헥사고날 아키텍처를 직접적으로 반영하는 표현력있는 패키지 구조를 소개하겠습니다. 그 전에 안좋게 패키지를 구성한 케이스 2가지를 먼저 살펴본후, 아키텍처를 표현력있게 살린 패키지 구조를 살펴보도록 합시다.&lt;/p&gt;
&lt;p&gt;이를 위해, 사용자가 본인의 계좌에서 다른 계좌로 돈을 송검할 수 있는 &apos;송금하기&apos; 유즈케이스를 살펴보겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;계층별로-구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%84%EC%B8%B5%EB%B3%84%EB%A1%9C-%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;계층별로 구성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;계층별로 구성&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Money
|-- domain
|	|- Account
|	|- Activity
|	|- AccountRepository
|	|- AccountService
|
|---persistence
|	|-- AccountRepositoryImpl
|
|---web
|	|- AccountController&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 웹 계층, 도메인 계층, 영속성 계층 배치했습니다. DIP 원칙을 적용해서 영속석 계층의 AccountRepositoryImpl, 웹 계층의 AccountController 는 의존성이 domain 패키지의 도메인 코드만을 향하게 되어있습니다. 이때 의존성 역전을 위해 domain 패키지에는 AccountRepository 인터페이스를 위치시키고, 영속성 계층에 해당 인터페이스의 구현채로 AccountRepositoryImpl 를 배치시켰죠.&lt;/p&gt;
&lt;h3 id=&quot;문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;문제점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문제점&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;계층별로 패키지,코드를 구성하면 기능적인 측면들이 섞이기 쉽다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이런 패키지 구조는 애플리케이션이 어떤 유스케이스들을 제공하는지 알 수 없습니다. AccountService 와 AccountController 가 어떤 유스케이스를 구현했는지 파악하기 힘들죠. &lt;strong&gt;특정 기능을 찾기 위해선 어떤 서비스가 이를 구현했는지 추측해야하며, 해당 서비스 내의 어떤 메소드가 그에대한 책임을 수행하는지 찾아야합니다.&lt;/strong&gt; 또한 어떤 기능이 웹 어댑터에서 호출되는지, 영속성 어댑터가 도메인 계층에 어떤 기능을 제공하는지 한눈에 알아볼 수 없게됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;기능별로-구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EB%8A%A5%EB%B3%84%EB%A1%9C-%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;기능별로 구성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기능별로 구성&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Money
|--- account
	 |--- Account
	 |--- AccountController
	 |--- AccountRepository
	 |--- AccountRepositoryImpl
     |--- SendMoneyService&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 &quot;계좌 관련&quot; 모든 코드를 최상위 account 패키지내에 구성하면, 사실 계층별 패키징 구성 방식보다 가시성을 훨씬 떨어뜨립니다. 어댑터를 나타내는 패키지명이 없고, 인커밍(in-coming) 포트, 아웃고잉(out-going) 포트를 확인할 수 없게됩니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;기능을 기준으로 코드를 구성하면 어떤 아키텍처 기반인지 명확히 보이지 않는다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;헥사고날-아키텍처를-패키지에-표현하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%97%A5%EC%82%AC%EA%B3%A0%EB%82%A0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EB%A5%BC-%ED%8C%A8%ED%82%A4%EC%A7%80%EC%97%90-%ED%91%9C%ED%98%84%ED%95%98%EA%B8%B0&quot; aria-label=&quot;헥사고날 아키텍처를 패키지에 표현하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;헥사고날 아키텍처를 패키지에 표현하기&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Money
|--- account
	 |--- adapter
     |	  |--- in
     |	  |	   |--- web
     |	  |.        |--- AccountController
     |	  |--- out
     	       |--- persistence
               	    |--- AccountPersistenceAdatper
                    |--- SrpingDataAccountRepository
     |---domain
     |   |--- Account
     |   |--- Activity
     |
     |--- application
     	   |--- SendMoneyService
           |--- port
           		|--- in
                |	 |--- SendMoneyUseCase
                |--- out
                	 |--- LoadAccountPort
                     |--- UpdateAccountStatePort&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;헥사고날 아키텍처에서 구조적으로 핵심적인 요소는 &lt;code class=&quot;language-text&quot;&gt;엔티티&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;유스케이스&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;인커밍/아웃코잉 포트&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;인커밍/아웃고잉 어댑터&lt;/code&gt; 입니다. 이 요소들을 어떻게 패키지에 녹여낼 수 있을지 살펴봅시다.&lt;/p&gt;
&lt;p&gt;최상위에는 계좌와 관련된 유스케이스를 구현한 모듈임을 나타내는 account 패키지가 있습니다. application 패키지는 도메인을 둘러싼 서비스 계층을 포함하고있죠. SendMoneyService 는 인커밍 포트 인터페이스인 SendMoneyUseCase 를 구현하며, 아웃고잉 포트 인터페이스이자 영속성 어댑터에 의해 구현된 LoadAccountPort 와 UpdateAccountStatePort 를 사용합니다.&lt;/p&gt;
&lt;p&gt;또 adatper 패지키는 보시듯이 web 어댑터와 persistence 어댑터를 포함하고있습니다.&lt;/p&gt;
&lt;h3 id=&quot;이걸-어떻게-쓰지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EA%B1%B8-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%93%B0%EC%A7%80&quot; aria-label=&quot;이걸 어떻게 쓰지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이걸 어떻게 쓰지?&lt;/h3&gt;
&lt;p&gt;이러한 헥사고날 패키지를 사용하는 경우를 떠올려봅시다. 헥사고날 구조를 보면서 팀원들이 현재 사용중인 third party API 를 변경하는 작업에 대해 회의를 진행하는 상황이죠. 그러면 작업을 진행하는 부분을 찾기위해, 바로 아웃고잉 어댑터를 바로 뒤져보게 될겁니다. 왜냐하면 작업을 진행하고자 하는 API 코드는 adapter/out/&amp;#x3C;어댑터명&gt; 패키지에 존재하고, 이를 바로 찾아낼 수 있기 때문이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;핵심은-의존성-주입--역전&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B5%EC%8B%AC%EC%9D%80-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85--%EC%97%AD%EC%A0%84&quot; aria-label=&quot;핵심은 의존성 주입  역전 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;핵심은 의존성 주입 &amp;#x26; 역전&lt;/h2&gt;
&lt;p&gt;앞서 클린 아키텍처를 언급했을떄, 애플리케이션 계층이 인커밍/아웃고잉 어댑터에 의존성을 가지면 안됩니다. 이떄 영속성 어댑터와 같은 아웃고잉 어댑터에 대해서는 제어 흐름을 반대 방향으로 의존성을 돌리기위해, &lt;code class=&quot;language-text&quot;&gt;의존성 역전 원칙&lt;/code&gt;을 이용해야 한다는점을 다시 되짚고 넘어갑시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/21e4f036-e8f2-4203-b219-57a881e4fb06/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이를 구현하기 위해선, 애플리켄이션 계층에 인터페이스를 만들고 어댑터에 해당 인터페이스를 구현한 클래스를 두면됩니다. 헥사고날 아키텍처에서는 이 인터페이스가 포트입니다. 그러고 애플리케이션 계층은 어댑터의 기능을 실행하기 위해 이 포트 인터페이스를 호출하는 방식으로 돌아갑니다.
위 그림은 웹 컨트롤러가 서비스에 의해 구현된 인커밍 포트를 호출합니다. 서비스는 어댑터에 의해 구현된 아웃고잉 포트를 호출하고요.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;다음 포스팅에서는 헥사고날 아키텍처의 어떻게 스프링부트에서 구현할지에 대해 알아보겠습니다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[클린 아키텍처는 의존성 문제를 어떻게 해결했는가?]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/architecture/clean/</link><guid isPermaLink="false">https://haon.site/haon/architecture/clean/</guid><pubDate>Tue, 25 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;아키텍처란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EB%9E%80&quot; aria-label=&quot;아키텍처란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;아키텍처란&lt;/h2&gt;
&lt;p&gt;본 아키텍처에 대해 알아보기전에, 저희는 아키텍처의 주목적에 대해 다시 되짚어봐야합니다. 아키텍처는 소프트웨어가 &lt;code class=&quot;language-text&quot;&gt;확장과 변경&lt;/code&gt; 에 용이하도록 설계해서 쉽게 유지.보수를 할 수 있도록 만드는 기법이죠. 하지만 프로젝트를 이루는 각 구성단위는 서로에게 지나친 &lt;code class=&quot;language-text&quot;&gt;의존성&lt;/code&gt; 을 지니게 되어서, 확장과 변경에 닫혀있게 될 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;소프트웨어의-구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4%EC%9D%98-%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;소프트웨어의 구성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;소프트웨어의 구성&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/faa2c726-8d2e-47a5-88ac-d818c1ed1eee/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;한 소프트웨어, 즉 서비스는 크게 &lt;code class=&quot;language-text&quot;&gt;도메인&lt;/code&gt; 영역과 &lt;code class=&quot;language-text&quot;&gt;인프라스트럭처(InfraStructure)&lt;/code&gt; 영역으로 구분됩니다. 도메인이란 시스템의 핵심 데이터를 나타내는 것이며, 인프라스트럭처란 도메인을 뒷받침해서 사용자들에게 서비스를 제공하기 위한 서브 요소입니다. 데이터를 저장할 DB, 외부 서비스 API, 도메인을 화면에 표시할 UI 등이 이에 해당되죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;전통적-계층형-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%ED%86%B5%EC%A0%81-%EA%B3%84%EC%B8%B5%ED%98%95-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;전통적 계층형 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전통적 계층형 아키텍처&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5141442e-28e2-48b8-8211-d591d3fd73da/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;계층적 아키텍처는 &lt;strong&gt;같은 목적의 코드들을 한 계층에 모아놓음&lt;/strong&gt;으로써, 코드들의 관심사를 분리하는 아키텍처입니다. 가령 위처럼 UI 를 표현하기 위한 코드들은 프레젠테이션 계층에 모아놓고, 비즈니스 로직들은 도메인 계층에 놓는 방식인것입니다. 저도 최근까지만 해도 이 방식으로 단순한 계층구조를 만들고 개발을 진행했었습니다.&lt;/p&gt;
&lt;h3 id=&quot;계층간의-의존성-문제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%84%EC%B8%B5%EA%B0%84%EC%9D%98-%EC%9D%98%EC%A1%B4%EC%84%B1-%EB%AC%B8%EC%A0%9C&quot; aria-label=&quot;계층간의 의존성 문제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;계층간의 의존성 문제&lt;/h3&gt;
&lt;p&gt;그러나 위와 같은 전통적인 계층적 아키텍처 구조는 &lt;code class=&quot;language-text&quot;&gt;연쇄적인 참조관계&lt;/code&gt; 를 가진다는 문제점이 있습니다. 프레젠테이션은 도메인 계층의 비즈니스 로직을 참조하고, 이 비즈니스 로직은 또 영속성 계층의 데이터를 참조하므로, 결국 프레젠테이션의 UI 가 영속성 계층의 데이터를 직접 참조하는 꼴이 되버리죠.&lt;/p&gt;
&lt;p&gt;결국 한 계층의 변경사항이 일어나면 그 변경의 여파가 모든 계층에 전파될 수 있으며, &lt;code class=&quot;language-text&quot;&gt;확장과 변경&lt;/code&gt;에 닫혀있다고 할 수 있습니다. &lt;strong&gt;즉, 각 계층은 서로에게 의존성을 지니고 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;특히 비즈니스 로직이 흐르고 있는 도메인 영역은 철저지 보호되어야 하는데, 의존성 문제로 인해 다른 계층에서 빈번한 수정이 일어나면 도메인 영역에 손상을 입히게 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;클린-아키텍처의-등장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%9D%98-%EB%93%B1%EC%9E%A5&quot; aria-label=&quot;클린 아키텍처의 등장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클린 아키텍처의 등장&lt;/h2&gt;
&lt;p&gt;기존의 계층적 구조는 보듯이 &lt;code class=&quot;language-text&quot;&gt;의존성&lt;/code&gt; 이라는 것 떄문에 꽤나 번거로움을 지니고 있었죠. 의존성 이슈를 보완하고자 고안한 방법이 바로 클린 아키텍처입니다.
클린 아키텍처에서는 &lt;code class=&quot;language-text&quot;&gt;의존성 규칙&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;경계성 규칙&lt;/code&gt; 을 준수하며 시스템을 구성할 것을 강조하고 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;의존성-규칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%98%EC%A1%B4%EC%84%B1-%EA%B7%9C%EC%B9%99&quot; aria-label=&quot;의존성 규칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;의존성 규칙&lt;/h3&gt;
&lt;p&gt;의존성 규칙이란, 모든 소스코드의 의존성은 반드시 외부에서 내부로, 즉 고수준 정책을 향해야 한다는 것입니다. 즉, 비즈니스 로직을 담당하는 코드들이 DB 또는 UI 같이 구체적인 세부사항을 의존하지 않아야 한다는 것입니다.&lt;/p&gt;
&lt;p&gt;추후 서술하겠지만, 클린 아키텍처의 내부 구성요소는 추상화된 인퍼페이스(포트) 를 톤해 외부 계층의 구성요소와 통신합니다. 이를통해 &lt;strong&gt;비즈니즈 로직(고수준 정책)은 세부 사항들(저수준 정책)의 변경에 영향을 받지 않도록 할 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;고수준 정책 : 상위 수준의 개념, 추상화된 개념
(ex. 데이터를 저장한다, 주문을 처리한다)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;저수준 정책 : 추상화된 개념을 실제로 어떻게 구현할지에 대한 세부적인 개념
(ex. MySQL 에 데이터를 저장한다, 유저 A 에 대한 주문을 처리한다)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;경계성-규칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%BD%EA%B3%84%EC%84%B1-%EA%B7%9C%EC%B9%99&quot; aria-label=&quot;경계성 규칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;경계성 규칙&lt;/h3&gt;
&lt;p&gt;경계성 규칙이란 서비스(소프트웨어)를 &lt;strong&gt;데이터가 오가는 외부-내부의 경계(Bounary) 를 명확히 그어두어서,&lt;/strong&gt; 데이터의 흐름을 효율적으로 제어하자는 규칙입니다. 이것도 뒤 이어서 서술하겠지만, &lt;strong&gt;경계성 규칙은 어댑터를 통해 구현&lt;/strong&gt;됩니다.
가령 DB, UI 와 같은것들을 시스템의 외부로 구분짓고, 그 안의 엔티티, 유즈케이스등을 내부 시스템으로 구분지은후, 이 외부와 내부를 어댑터로 확실히 경계를 긋고 분리시키는 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;클린-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;클린 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클린 아키텍처&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/665f2255-7737-470f-92dc-812806d5e3fc/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위 그림은 클린 아키텍처에서 제시하는 계층구조입니다. 화살표를 볼 수 있는데, 이는 의존성의 방향을 나타내는 것입니다. 여기서 중요한 것은, &lt;strong&gt;하위 계층은 상위 계층을 단방향으로 의존하고 있다는 것입니다.&lt;/strong&gt; 즉, 엔티티와 유즈케이스와 같은 상위 계층은 하위 계층을 의존할 수 없습니다.
추후 계속 설명하겠지만, 이렇게 의존성의 방향을 가지면서 바깥 계층에 변화가 일어나더라도, 내부 계층의 &lt;code class=&quot;language-text&quot;&gt;고수준 정책&lt;/code&gt;은 영향을 받지 않게 된다는 점을 다시 강조하고 싶습니다. 또 클린 아키텍처의 구성요소를 설명해보자면 다음과 같습니다.&lt;/p&gt;
&lt;h3 id=&quot;entity--유즈케이스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#entity--%EC%9C%A0%EC%A6%88%EC%BC%80%EC%9D%B4%EC%8A%A4&quot; aria-label=&quot;entity  유즈케이스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Entity &amp;#x26; 유즈케이스&lt;/h3&gt;
&lt;p&gt;Entity(엔티티) 와 유즈케이스는 내부 계층에 위치한 가장 중요한 요소들입니다. 엔티티는 도메인 계층에 속하며, 유즈케이스는 보통 애플리케이션 계층에 위치해 있습니다.
&lt;code class=&quot;language-text&quot;&gt;Entity&lt;/code&gt; 는 시스템 내부의 핵심 데이터를 표현하는 것입니다. 반면 &lt;code class=&quot;language-text&quot;&gt;유즈케이스(Use Case)&lt;/code&gt; 는 애플리케이션의 기능을 구현하는 역할을 담당하며, Entity 를 참조해서 구현합니다. 이 유즈케이스에는 비즈니스 로직이 흐르고 있죠. 또 유즈케이스는 양쪽에 &lt;code class=&quot;language-text&quot;&gt;어뎁터(Adapter)&lt;/code&gt; 를 통해 외부(저수준 정책)와 연결되는 구조를 지닙니다.&lt;/p&gt;
&lt;h3 id=&quot;어댑터adatper&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%8C%91%ED%84%B0adatper&quot; aria-label=&quot;어댑터adatper permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어댑터(Adatper)&lt;/h3&gt;
&lt;p&gt;어댑터는 &lt;strong&gt;외부의 데이터 흐름을 내부 도메인에서 사용할 수 있는 데이터 형식으로 변환&lt;/strong&gt;해주는 역할을 수행합니다. 어댑터의 종류로는 크게 &apos;컨트롤러(Controller)&apos;, &apos;Presenter(프레젠터)&apos;, &apos;Gateway&apos;, &apos;Repository&apos; 등이 있습니다. 위 그림에서는 녹색 계층에 해당됩니다.&lt;/p&gt;
&lt;p&gt;예를들어 Presenter 는 화면 UI 의 입력을 도메인에 맞는 데이터로 변환하는 역할을 수행하며, Repository 는 외부의 DB, 또는 API 데이터를 도메인에 맞는 데이터로 변환하는 역할을 수행합니다.&lt;/p&gt;
&lt;h3 id=&quot;포트-인터페이스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8F%AC%ED%8A%B8-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4&quot; aria-label=&quot;포트 인터페이스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;포트 (인터페이스)&lt;/h3&gt;
&lt;p&gt;포트(Port) 는 인터페이스로 구현되는 개념입니다. 포트는 특정한 계층 내부에서 어떤 역할을 수행하기 위한 인터페이스를 정의하죠. 포트는 유스케이스와 어댑터 사이에 위치해서, 유스케이스가 제공하는 기능과 필요한 기능을 제공하는 역할을 수행합니다. 위 그림에서는 보이지 않지만, 적색 계층(애플리케이션 계층) 과 녹색 계층 사이에 위치해서 서로를 연결하는 징검다리 역할을 수행하게 됩니다.&lt;/p&gt;
&lt;p&gt;유스케이스와 어댑터는 서로 직접 연결된 것이 아닌, 사전에 정의된 포트라는 인터페이스로 연결되기만 하면 됩니다. 때문에 유스케이스와 어댑터는 서로 구체적인 명세 없이도 &lt;code class=&quot;language-text&quot;&gt;추상화&lt;/code&gt; 개념을 도입해서, 서로에게 변경사항이 있어도 영향을 미치지 않을 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;기존-계층구조에-클린-아키텍처를-적용해볼까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EC%A1%B4-%EA%B3%84%EC%B8%B5%EA%B5%AC%EC%A1%B0%EC%97%90-%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EB%A5%BC-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%BC%EA%B9%8C&quot; aria-label=&quot;기존 계층구조에 클린 아키텍처를 적용해볼까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기존 계층구조에 클린 아키텍처를 적용해볼까?&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7bfb58b9-a633-49d8-8ac8-40accd3693c6/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;앞서 살펴봤던 기존 계층적 구조로 구성된 클린 아키텍처를 도입해보면, 위와 같은 리팩토링 구조가 나타나게 될것입니다. 화살표는 참조의 방향, 즉 의존성의 방향을 뜻합니다.&lt;/p&gt;
&lt;p&gt;우선 외부의 변경으로부터 보호되어야할 엔티티가 가운대에 위치해있으며, 유스케이스는 엔티티를 참조하여 애플리케이션이 제공해야할 기능을 구현할 것입니다.&lt;/p&gt;
&lt;p&gt;또 유스케이스의 양쪽에는 Presenter, Repository 어댑터가 달려있는데, 이 어댑터들로 UI, DB, API 와 같은 외부의 존재로부터 유입되는 데이터 흐름을 내부 도메인에서 사용할 수 있는 데이터 형식으로 변환해주는 역할을 수행합니다.
앞서 말한 내용이지만, Presenter 는 화면 UI 의 입력을 도메인에 맞는 데이터로 변환하는 역할을 수행하며, Repository 는 외부의 DB, 또는 API 데이터를 도메인에 맞는 데이터로 변환하는 역할을 수행합니다.&lt;/p&gt;
&lt;p&gt;인터페이스(포트)는 일종의 약속으로써, 유즈케이스가 제공하는 기능과 필요한 기능을 포트에다 추상화해놓으면, 각 어댑터는 인터페이스만을 보고 구현하면 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;객체지향으로-귀결되는-클린-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9C%BC%EB%A1%9C-%EA%B7%80%EA%B2%B0%EB%90%98%EB%8A%94-%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;객체지향으로 귀결되는 클린 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;객체지향으로 귀결되는 클린 아키텍처&lt;/h2&gt;
&lt;p&gt;클린 아키텍처의 컨셉을 처음접하며 느낀바는, 결국 클린 아키텍처의 내용도 객체지향으로 귀결되는듯 하다는걸 느꼈습니다. 포트(Port) 라는 추상화 인터페이스를 제공하는 것은 의존성 주입, 즉 &lt;code class=&quot;language-text&quot;&gt;DI(Dependency Injection)&lt;/code&gt; 를 위한 것입니다. 또 DI 를 통해 SOLID 원칙의 DIP 의존관계 원칙도 지킬 수 있게 되는 것이겠죠. &lt;a href=&quot;https://velog.io/@msung99/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B3%A0-%EB%B0%94%EB%9D%BC%EB%B3%B4%EB%8A%94-SOLID-5%EB%8C%80-%EC%84%A4%EA%B3%84%EC%9B%90%EC%B9%99#dip-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%97%AD%EC%A0%84-%EC%9B%90%EC%B9%99&quot;&gt;예제로 이해하는 SOLID 설계원칙, 그리고 스프링 DI 컨테이너의 등장&lt;/a&gt; 에서 다루었던 내용을 다시 자연스래 복기해보게 되었네요.&lt;/p&gt;
&lt;p&gt;또한 객체지향의 기초이자 핵심이라 할 수 있는 &lt;code class=&quot;language-text&quot;&gt;변경과 확장성&lt;/code&gt; 측면에서 바라봐도, 클린 아키텍처를 도입시 용이하게 설계를 고려해볼 수 있을것이란 생각이듭니다. 조만간 학습할 &lt;code class=&quot;language-text&quot;&gt;헥사고날 아키텍처&lt;/code&gt; 의 경우도 클린 아키텍처에서 파생된 것이므로, 이 또한 객체지향으로 귀결되는 내용이라고 생각합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html&quot;&gt;The Clean Code Blog : by Robert C. Martin (Uncle Bob)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=saxHxoUeeSw&amp;#x26;t=349s&quot;&gt;기획자님들! 개발자가 아키텍처에 집착하는 이유, 쉽게 알려드립니다&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://techblog.woowahan.com/2647/&quot;&gt;주니어 개발자의 클린 아키텍처 맛보기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@justfaceit/clean-architecture%EB%8A%94-%EB%AA%A8%EB%B0%94%EC%9D%BC-%EA%B0%9C%EB%B0%9C%EC%9D%84-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%84%EC%99%80%EC%A3%BC%EB%8A%94%EA%B0%80-1-%EA%B2%BD%EA%B3%84%EC%84%A0-%EA%B3%84%EC%B8%B5%EC%9D%84-%EC%A0%95%EC%9D%98%ED%95%B4%EC%A4%80%EB%8B%A4-b77496744616&quot;&gt;Clean Architecture는 모바일 개발을 어떻게 도와주는가? - (1) 경계선: 계층 나누기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@___pepper/%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B5%AC%EC%A1%B0-%EB%B0%8F-%EC%A3%BC%EC%9A%94-%EA%B0%9C%EB%85%90&quot;&gt;[클린 아키텍처] 구조 및 주요 개념&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@bluejoyq/%EB%B2%88%EC%97%AD-Clean-Architecture-on-Frontend#architecture-and-design&quot;&gt;(번역) Clean Architecture on Frontend&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[쿠버네티스(Kubernetes)의 라이브니스 기반 헬스체킹]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/kubernetes/liveness-probe/</link><guid isPermaLink="false">https://haon.site/haon/kubernetes/liveness-probe/</guid><pubDate>Sat, 01 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;현재까지 학습을 진행한바에 따르면 쿠버네티스에서 파드는 배포 가능한 기본 단위입니다. 파드를 수동으로 생성, 감독, 관리하는 방법을 배웠지만 실제 환경에서는 배포한 애플리케이션이 자동으로 실행되고 수동적인 개입 없이도 안정적인 상태로 유지되길 원할겁니다.&lt;/p&gt;
&lt;p&gt;대신 ReplicationController 나 Deployment 와 같은 유형의 리소스를 생성해서 실제 파드를 생성하고 관리하죠. &lt;a href=&quot;https://velog.io/@msung99/Kubernetes-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EC%9D%98-%ED%8C%8C%EB%93%9CPod-%EB%B0%B0%EC%B9%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%99%80-%EB%94%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%84%B0%EB%A1%9C-%ED%8C%8C%EB%93%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0&quot;&gt;[Kubernetes] 클러스터의 파드(Pod) 배치 아키텍처와 YAML 디스크립터로 파드 관리하기&lt;/a&gt; 에서 만든것과 같은 관리되지 않는 파드를 생성하면 파드를 실행할 클러스터 노드가 선택되고, 파드의 컨테이너가 해당 노드에서 실행됩니다.&lt;/p&gt;
&lt;p&gt;이번에는 쿠버네티스가 해당 컨테이너를 모니터링하고 실패하면 자동으로 다시 시작하는 방법을 알아보겠습니다. 또 쿠버네티스가 어떻게 컨테이너가 살아있는지 체크하고, 프로세스가 kill 상태인 경우 어떻게 다시 시작하는지 알아보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;왜-라이브니스-프로브&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%8B%88%EC%8A%A4-%ED%94%84%EB%A1%9C%EB%B8%8C&quot; aria-label=&quot;왜 라이브니스 프로브 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 라이브니스 프로브?&lt;/h2&gt;
&lt;p&gt;쿠버네티스를 적용하다가, 파드의 컨테이너 중 하나가 죽으면 어떻게 될까요? 또는 파드 안의 모든 컨테이너가 죽으면 어떻게될까요?&lt;/p&gt;
&lt;h3 id=&quot;kubelet-에-의한-상태체크-및-자가치유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#kubelet-%EC%97%90-%EC%9D%98%ED%95%9C-%EC%83%81%ED%83%9C%EC%B2%B4%ED%81%AC-%EB%B0%8F-%EC%9E%90%EA%B0%80%EC%B9%98%EC%9C%A0&quot; aria-label=&quot;kubelet 에 의한 상태체크 및 자가치유 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Kubelet 에 의한 상태체크 및 자가치유&lt;/h3&gt;
&lt;p&gt;파드가 노드에 스캐줄링되는 즉시, 해당 노드의 &lt;code class=&quot;language-text&quot;&gt;Kubelet&lt;/code&gt; 은 파드의 컨테이너를 실행하고 파드가 존재하는 한 컨테이너가 계속 실행되도록 합니다. 컨테이너의 주 프로세스에 &lt;code class=&quot;language-text&quot;&gt;크래시(Crash)&lt;/code&gt; 가 발생하면 Kubelet 은 컨테이너를 다시 시작합니다.&lt;/p&gt;
&lt;p&gt;만약 이렇게 애플리케이션에 장애가 발생시 K8S 는 자동으로 애플리케이션을 다시 시작하므로, 개발자는 특별한 작업없이도 &lt;code class=&quot;language-text&quot;&gt;자가 치유&lt;/code&gt;할 수 있는 모습을 볼 수 있게됩니다.&lt;/p&gt;
&lt;h3 id=&quot;외부에서-애플리케이션-상태를-체크해보자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%B8%EB%B6%80%EC%97%90%EC%84%9C-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%83%81%ED%83%9C%EB%A5%BC-%EC%B2%B4%ED%81%AC%ED%95%B4%EB%B3%B4%EC%9E%90&quot; aria-label=&quot;외부에서 애플리케이션 상태를 체크해보자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;외부에서 애플리케이션 상태를 체크해보자&lt;/h3&gt;
&lt;p&gt;그런데 &lt;strong&gt;때때로 애플리케이션은 프로세스의 크래시(Crash) 가 없이도 작동이 중단되는 상황이 발생하는 경우도 있습니다.&lt;/strong&gt; 일례로 자바 애플리케이션이 메모리 누수가 있어서 OutofMemoryErrors 를 발생시키기 시작하더라도 JVM 프로세스는 계속 실행됩니다.&lt;/p&gt;
&lt;p&gt;이런 상황이 발생하지 않도록, &lt;strong&gt;애플리케이션이 더 이상 동작하지 않는다는 신호를 쿠버네티스에 보내서, 쿠버네티스가 애플리케이션을 다시 시작하도록 하는 방법이 있다면 좋을것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;예를들어 애플리케이션이 무한 루프나 &lt;code class=&quot;language-text&quot;&gt;데드락(DeadLock)&lt;/code&gt; 상황에 빠져서 응답을 하지 않는 상황이라면 어떨까요? 이런경우 애플리케이션이 다시 시작되도록 하려면 애플리케이션 내부의 기능에 의존하지 말고 외부에서 애플리케이션의 상태를 체크해야 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;라이브니스-프로브&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%8B%88%EC%8A%A4-%ED%94%84%EB%A1%9C%EB%B8%8C&quot; aria-label=&quot;라이브니스 프로브 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;라이브니스 프로브&lt;/h2&gt;
&lt;p&gt;쿠버네티스는 라이브니스 프로브(liveness probe) 를 통해 &lt;strong&gt;컨테이너가 살아있는지 확인할 수 있습니다.&lt;/strong&gt; 파드의 명세(specification) 에 각 컨테이너의 라이브니스 프로브를 지정할 수 있죠. &lt;strong&gt;쿠버네티스는 주기적으로 프로브라는것을 실행하고 프로브가 실패할경우 컨테이너를 다시 시작합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;쿠버네티스는 3가지 메커니즘으로 컨테이너에 프로브를 실행합니다.&lt;/p&gt;
&lt;h3 id=&quot;http-get-프로브&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-get-%ED%94%84%EB%A1%9C%EB%B8%8C&quot; aria-label=&quot;http get 프로브 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP GET 프로브&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;지정한 IP 주소, 포트, 경로에 HTTP GET 요청을 보냅니다.&lt;/strong&gt; 프로브가 응답을 수신하고, Status Code 값이 정상이라고 판단되는 경우(2xx, 3xx 번대 코드값) 프로브가 성공됐다고 간주합니다. 반대로 &lt;strong&gt;비정상 코드값(4xx, 5xx 번대) 을 리턴하거나 전혀 응답하지 않으면(ex. DeadLock) 프로브가 실패한 것으로 간주돼어 컨테이너를 다시 시작합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;tcp-소켓-프로브&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tcp-%EC%86%8C%EC%BC%93-%ED%94%84%EB%A1%9C%EB%B8%8C&quot; aria-label=&quot;tcp 소켓 프로브 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TCP 소켓 프로브&lt;/h3&gt;
&lt;p&gt;컨테이너를 지정된 포트에 TCP 연결을 시도하고, &lt;strong&gt;TCP 연결 성공여부에 따라 프로브 성공여부가 결정&lt;/strong&gt;되는 방식입니다. 즉, TCP 연결에 성공하면 프로브가 성공한 것이고, 그렇지 않으면 컨테이너가 다시 시작됩니다.&lt;/p&gt;
&lt;h3 id=&quot;exec-프로브&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#exec-%ED%94%84%EB%A1%9C%EB%B8%8C&quot; aria-label=&quot;exec 프로브 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Exec 프로브&lt;/h3&gt;
&lt;p&gt;컨테이너 내의 임의의 명령을 수행하고 명령의 종료상태 코드를 확인합니다.
&lt;strong&gt;상태 코드가 0이면 프로브가 성공한 것이고, 다른 모든 코드들은 실패로 간주&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;http-기반-라이브니스-프로브-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-%EA%B8%B0%EB%B0%98-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%8B%88%EC%8A%A4-%ED%94%84%EB%A1%9C%EB%B8%8C-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;http 기반 라이브니스 프로브 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP 기반 라이브니스 프로브 생성&lt;/h2&gt;
&lt;p&gt;이제부터 라이브니스 프로브에 기반하여 주기적으로 &lt;code class=&quot;language-text&quot;&gt;헬스 체킹(Health Checking)&lt;/code&gt; 을 시도하고, 스프링부트 애플리케이션에 라이브니스 프로브니스를 추가하는 방법을 살펴봅시다.&lt;/p&gt;
&lt;p&gt;저희는 프로브를 테스트하기 위해, 스프링부트 애플리케이션 코드를 인위적으로 만들겁니다. 5번째 이후의 요청부터는 500 Status Code (Internal Server Error) 를 리턴하도록 만들겠습니다.
즉, 처음 5번째 요청까지는 적절히 처리하고 이후의 모든 요청은 모두 오류를 발생시키도록 500 상태코드 리턴하도록 하겠습니다. 이 상황에서 라이브니스 프로브를 활용하면 컨테이너가 다시 시작돼 클라이언트의 요청을 다시 적절히 처리하게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;파드-yaml-디스크립터-작성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EB%93%9C-yaml-%EB%94%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%84%B0-%EC%9E%91%EC%84%B1&quot; aria-label=&quot;파드 yaml 디스크립터 작성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파드 YAML 디스크립터 작성&lt;/h3&gt;
&lt;p&gt;HTTP GET 라이브니스 프로브가 포함된 새 파드를 생성합니다. 아래는 파드 YAML 을 보여줍니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;apiVersion&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; v1
kind&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pod&lt;/span&gt;
metadata&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;liveness
spec&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  containers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; image&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; luksa&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;unhealthy  &lt;span class=&quot;token comment&quot;&gt;// 5번이상 요청시 500 상태코드를 리턴하는 애플리케이션 이미지&lt;/span&gt;
    name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; kubia
    livenessProbe&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// HTTP GET 을 수행하는 라이브니스 프로브&lt;/span&gt;
      httpGet&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
        path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// HTTP 요청 경로&lt;/span&gt;
        port&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 프로브가 연결해야하는 네트워크 포트&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 파드 디스크립터는 쿠버네티스가 주기적으로 &quot;/&quot; 경로와 8080포트에 HTTP GET 을 요청해서 컨테이너가 정상 작동하는지 확인하도록 httpGet 라이브니스 프로브를 정의합니다. 이런 요청은 컨테이너가 실행되는 즉시 시작됩니다.&lt;/p&gt;
&lt;p&gt;5번의 요청 후에 애플리케이션은 HTTP 상태 코드 500을 리턴하기 시작하고, 쿠버네티스는 프로브를 실패한 것으로 간주하고 컨테이너를 재시작합니다.
그러고 아래와 같이 파드를 생성해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ docker pull luksa&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;unhealthy
$ kubectl create &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;liveness&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;probe&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;파드를 조회해보면, 아래와 같이 RESTARTS 열이 0이 아니라 컨테이너가 무려 4번이나 재시작 되었음을 볼 수 있습니다. 약 90초의 주기로 다시 재시작을하죠.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4b3029c6-8a6c-4998-9b47-963e899979b2/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;sigkill-시그널-번호&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sigkill-%EC%8B%9C%EA%B7%B8%EB%84%90-%EB%B2%88%ED%98%B8&quot; aria-label=&quot;sigkill 시그널 번호 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SIGKILL 시그널 번호&lt;/h3&gt;
&lt;p&gt;그리고 kubectl describe 로 출력되는 내용을 조회해보면, 컨테이너가 재시작된 이유를 확인할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl describe po kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;liveness&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;로그를 출력시 컨테이너가 현재 실행중이지만 오류로 인해 이전에 종료된것을 알 수 있습니다. Containers - Last State - Exit Code 란을 보면 &lt;code class=&quot;language-text&quot;&gt;종료코드값 137&lt;/code&gt; 을 확인할 수 있는데, 이는 &lt;strong&gt;프로세스가 외부신호에 의해 종료되었음을 의미&lt;/strong&gt;합니다. 숫자 137은 두 숫자를 합한것, 즉 128+0 인데 이때 9는 프로세스에 전송된 시그널 번호이며 이 시그널 번호에 의해 컨테이너가 종료되었음을 의미합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;즉, 9 는 SIGKILL 시그널 번호로, 프로세스가 강제로 종료되었음을 의미한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;initialdelayseconds&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#initialdelayseconds&quot; aria-label=&quot;initialdelayseconds permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;initialDelaySeconds&lt;/h2&gt;
&lt;p&gt;또 프로브를 정의할때 추가적인 매개변수로 &lt;code class=&quot;language-text&quot;&gt;초기 지연&lt;/code&gt; 에 대한 속성도 지정할 수 있습니다. 바로 &lt;code class=&quot;language-text&quot;&gt;initialDelaySeconds&lt;/code&gt; 속성을 라이브니스 프로브에 추가하면 됩니다.&lt;/p&gt;
&lt;p&gt;만약에 &lt;strong&gt;초기 지연을 설정하지 않으면 컨테이너가 시작되자마자 프로브를 시작합니다.&lt;/strong&gt; 이 경우 대부분의 애플리케이션이 요청을 아직 받을 준비가 돼 있지 않기때문에, &lt;strong&gt;프로브가 실패&lt;/strong&gt;합니다. 프로브의 실패 횟수가 임계값을 초과하면 요청을 올바르게 응답하게 전에 컨테이너가 다시 시작됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;apiVersion&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; v1
kind&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pod&lt;/span&gt;
metadata&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;liveness
spec&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  containers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; image&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; luksa&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;unhealthy
    name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; kubia
    livenessProbe&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      httpGet&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
        path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;
        port&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;
    initialDelaySeconds&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// K8S는 1번째 프로브 실행까지 15초를 대기한다.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;애플리케이션 프로세스 시작 시간을 고려해 초기지연을 설정해주자!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;만약 이 사실을 모른다면 개발자들은 왜 컨테이너가 재시작됐는지 혼란스러울 겁니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;효과적인-라이브니스-프로브-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%9A%A8%EA%B3%BC%EC%A0%81%EC%9D%B8-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%8B%88%EC%8A%A4-%ED%94%84%EB%A1%9C%EB%B8%8C-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;효과적인 라이브니스 프로브 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;효과적인 라이브니스 프로브 생성&lt;/h2&gt;
&lt;p&gt;운영환경에서 실행중인 파드는 반드시 라이브니스 프로브를 정의해야 합니다. &lt;strong&gt;프로브를 정의하지 않으면 쿠버네티스가 애플리케이션이 살아있는지를 알 수 있는 방법이 없습니다.&lt;/strong&gt; 프로세스가 실행되는 한 쿠버네티스는 컨테이너가 정상적이라고 간주할것이죠.&lt;/p&gt;
&lt;h3 id=&quot;라이브니스-프로브가-health-check-해야할-목록들&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%8B%88%EC%8A%A4-%ED%94%84%EB%A1%9C%EB%B8%8C%EA%B0%80-health-check-%ED%95%B4%EC%95%BC%ED%95%A0-%EB%AA%A9%EB%A1%9D%EB%93%A4&quot; aria-label=&quot;라이브니스 프로브가 health check 해야할 목록들 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;라이브니스 프로브가 Health Check 해야할 목록들&lt;/h3&gt;
&lt;p&gt;앞서 정의한 라이브니스 프로스브는 간단히 정의해둔것이라서, 단순히 서버가 응답하는지를 검사합니다. 이 기능으로 &lt;strong&gt;컨테이너 내에서 실행중인 웹서버가 HTTP 요청에 응답하지 않으면 컨테이너가 다시 시작되는&lt;/strong&gt; 엄청난 기능을 가지고 있습니다.&lt;/p&gt;
&lt;h4&gt;특정 URL로 헬스체킹 하도록 프로브를 정의하자&lt;/h4&gt;
&lt;p&gt;여기에 더 나아가서, 괜찮은 프로브를 구성하기 위해 &lt;strong&gt;특정 URL 경로(예를들어 &quot;/health&quot;) 에 요청하도록 프로브를 구성&lt;/strong&gt;하여 애플리케이션 내에서 실행중인 모든 주요 구성요소가 살아있는지, 또는 응답이 없는지 확인하도록 구성할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;프로브를 가볍게 유지하자&lt;/h4&gt;
&lt;p&gt;라이브니스 프로브는 너무 많은 연산 리소스를 사용하지말고, 헬스체킹을 완료하는데도 너무 오래걸려서도 안됩니다. 기본적으로 프로브는 자주 실행되며 1초 내에 완료되는 것이 좋습니다. 프로브가 너무 많은 체킹을 수행하면 컨테이너의 속도를 저하시킬 수 있기 떄문이죠.&lt;/p&gt;
&lt;h4&gt;자바 애플리케이션은 HTTP GET 라이브니스 프로브를 사용하자&lt;/h4&gt;
&lt;p&gt;컨테이너에서 자바 애플리케이션을 실헹하는 경우, 라이브니스 정보를 얻기위해 &lt;code class=&quot;language-text&quot;&gt;Exec 프로브&lt;/code&gt; 로 전체 JVM 을 기동하는 대신에 &lt;code class=&quot;language-text&quot;&gt;HTTP GET&lt;/code&gt; 라이브니스 프로브를 사용합시다.&lt;/p&gt;
&lt;h4&gt;프로브에 재시도 루프를 구현하지 말자&lt;/h4&gt;
&lt;p&gt;앞서 언급했기를, 애플리케이션 코드에서 설정한 프로스의 실패 임계값를 초과했을때 컨테이너가 재시작된다고 했었습니다. 그런데 이때 실패 임계값을 고작 1로 설정하면 딱 1번만 요청을 보내고 컨테이너를 재시작할 것 같지만, 프로브를 여러번 재시도합니다.&lt;/p&gt;
&lt;p&gt;따라서 프로브에 자체적인 재시도 루프를 구현하는것은 헛수고이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;이렇게 컨테이너에 크래시(Crash) 가 발생하거나 라이브니스 프로브가 실패한 경우, 쿠버네티스가 컨테이너를 재시작해 컨테이너가 계속 실행되도록 한다는 점을 기억합시다. 이 작업은 파드를 호스팅하는 노드의 &lt;code class=&quot;language-text&quot;&gt;Kubelet&lt;/code&gt; 에서 시도합니다. 마스터 노드에서 실행중인 컨트롤 플래인(Control Plane) 은 이 프로세스에 관여하지 않습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.manning.com/books/kubernetes-in-action-second-edition&quot;&gt;Kubernetes in Action, Second Edition&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[JPA의 영속성 컨텍스트 핵심 개념]]></title><description><![CDATA[학습배경 평소 Spring Data JPA 에 기반해서 개발을 진행하다보니, 순수 JPA 에 기반한 Entity…]]></description><link>https://haon.site/haon/jpa/persistence-context/</link><guid isPermaLink="false">https://haon.site/haon/jpa/persistence-context/</guid><pubDate>Thu, 30 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;평소 Spring Data JPA 에 기반해서 개발을 진행하다보니, 순수 JPA 에 기반한 Entity 가 어떻게 매니지 되는지 잘 이해못한 상태로 코드 개발을 진행해왔습니다. 그렇다보니 코드 리팩토링을 계속 진행하며 최적화, 각종 트러블 이슈로 인한 한계에 막히게 되어서, 어떻게 엔티티가 매니지 되는지 메커니즘을 다루어보고자 합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;현 포스팅은 JPA 애 대한 기초적인 지식이 요구됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;entity-와-rdb-테이블을-매핑하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#entity-%EC%99%80-rdb-%ED%85%8C%EC%9D%B4%EB%B8%94%EC%9D%84-%EB%A7%A4%ED%95%91%ED%95%98%EC%9E%90&quot; aria-label=&quot;entity 와 rdb 테이블을 매핑하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Entity 와 RDB 테이블을 매핑하자&lt;/h2&gt;
&lt;p&gt;JPA 는 xml 에 있는 설정정보에 기반하여 EntityManagerFactory 를 만들어냅니다. 또 이 공장으로부터 필요할때마다 EntityManager 를 만들어냅니다. 클라이언트의 요청이 들어올때마다 DB 작업을 해야하는 경우, EntityManagerFactory 로 부터 하나의 EntityManager 를 만들어내서 작업을 진행하죠.&lt;/p&gt;
&lt;p&gt;또 JPA 의 모든 작업은 한 트랜잭션 단위 내에서 일어나며, 이 안에서 자바의 객체와 RDB 의 데이터를 매핑하면서 작업을 수행하게 됩니다. 아래처럼 하나의 트랜잭션 단위 내에서 객체가 생성되고, 생성한 객체에 대해 CRUD 작업을 진행하여 실제 RDB 테이블에 반영시키는 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JpaMain&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;EntityManagerFactory&lt;/span&gt; emf &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Persistence&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createEntityManagerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;info-in-xml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;EntityManager&lt;/span&gt; em &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; emf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createEntityManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;EntityTransaction&lt;/span&gt; tx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; em&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTransaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 트랜잭션 생성&lt;/span&gt;
        tx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 트랜잭션 시작&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; member &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            member&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            member&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;HelloB&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            em&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;member&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 저장&lt;/span&gt;

            tx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 트랜잭션 커밋&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            tx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;rollback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            em&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        emf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;persist-context-영속성-컨텍스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#persist-context-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;persist context 영속성 컨텍스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Persist Context (영속성 컨텍스트)&lt;/h2&gt;
&lt;p&gt;지금부터 JPA 의 내부구조가 어떻게 돌아가는지 알아봅시다. 이를 위해선 영속성 구조라는 것에 대해 이해해야합니다.
우선 애플리케이션을 개발하면, EntityManagerFactory 를 통해서 클라이언트의 요청이 들어올때마다 EntityManager 를 생성합니다. 또 EntityManager 를 통해서 내부적으로 DB 커넥션을 사용해서 DB 를 사용하게 되는 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;영속성-컨텍스트-내부-메커니즘&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-%EB%82%B4%EB%B6%80-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98&quot; aria-label=&quot;영속성 컨텍스트 내부 메커니즘 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;영속성 컨텍스트 내부 메커니즘&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/8ee6836d-8da3-43ca-a5d2-fa2e8a19f31c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;JPA 를 학습하신 분들이라면 &lt;code class=&quot;language-text&quot;&gt;persist()&lt;/code&gt; 에 대해 알고 계실겁니다. 이 메소드는 DB 에 데이터를 저장하기 위한 함수이죠. 이 함수가 호출되었을떄 JPA 내부는 어떻게 동작할지 알아봅시다.&lt;/p&gt;
&lt;h3 id=&quot;자바-객체-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EB%B0%94-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;자바 객체 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자바 객체 생성&lt;/h3&gt;
&lt;p&gt;우선 EntityManager 를 통해 persist() 를 호출하게 될겁니다. 또 persist() 호출시 매개변수로 JAVA 객체 userA 라는 것을 저장하는 상황이라고 가정해보죠.&lt;/p&gt;
&lt;h3 id=&quot;영속성-컨텍스트-진입--1차캐시--sql-저장소&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-%EC%A7%84%EC%9E%85--1%EC%B0%A8%EC%BA%90%EC%8B%9C--sql-%EC%A0%80%EC%9E%A5%EC%86%8C&quot; aria-label=&quot;영속성 컨텍스트 진입  1차캐시  sql 저장소 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;영속성 컨텍스트 진입 : 1차캐시 + SQL 저장소&lt;/h3&gt;
&lt;p&gt;persist() 호출시 userA 라는 자바 객체는 우선 영속성 컨텍스트 영역이라는 곳에 진입하게 됩니다. 또 이 영역에 진입시 Entity 라는 단위로 변환되어 진입하게 됩니다. 이렇게 영속성 컨텍스트에 진입한 엔티티는 key-value 쌍으로써 &lt;code class=&quot;language-text&quot;&gt;1차 캐시&lt;/code&gt;라는 영역에 저장됩니다. 또한 동시에 userA 를 DB 에 저장하기 위한 insert 쿼리가 &lt;code class=&quot;language-text&quot;&gt;쓰기지연 SQL 저장소&lt;/code&gt; 에 저장됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DB-User&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; userIdx&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; phoneNumber&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이때 1차캐시에 key-value 포맷으로써 데이터가 저장된다고 했는데, key 값으로는 @Id 가 저장되며, value 에는 엔티티가 저장됩니다. 예를들어 위와 같이 User 엔티티가 정의되었다면, @Id 컬럼인 userIdx 값이 key 로 저장되며, value 에는 userA 이라는 엔티티 자체가 저장되는 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;트랜잭션-커밋시-쿼리문이-db-에-전송된다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%BB%A4%EB%B0%8B%EC%8B%9C-%EC%BF%BC%EB%A6%AC%EB%AC%B8%EC%9D%B4-db-%EC%97%90-%EC%A0%84%EC%86%A1%EB%90%9C%EB%8B%A4&quot; aria-label=&quot;트랜잭션 커밋시 쿼리문이 db 에 전송된다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 커밋시 쿼리문이 DB 에 전송된다&lt;/h3&gt;
&lt;p&gt;앞서 말씀드렸듯이, JPA 는 한 트랜잭션 단위를 기반으로 DB 에 작업을 처리한다고 했습니다. 현재 트랜잭션 단위내에 있는 모든 비즈니스 로직을 처리한후, 커밋 시점에 다가오면 트랜잭션에서 제공해주는 commit() 함수가 호출되면서 앞서 쓰기지연 SQL 저장소에 저장되었던 모든 쿼리문들이 DB 에 날라가게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;정확히는-flush-호출시-전송된다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%ED%99%95%ED%9E%88%EB%8A%94-flush-%ED%98%B8%EC%B6%9C%EC%8B%9C-%EC%A0%84%EC%86%A1%EB%90%9C%EB%8B%A4&quot; aria-label=&quot;정확히는 flush 호출시 전송된다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정확히는 flush() 호출시 전송된다.&lt;/h3&gt;
&lt;p&gt;더 엄밀히 말하자면, commit() 이 호출되는 시점에 쿼리문이 날라가는게 아니라, flush() 가 호출될때 쿼리문이 전송되는 것입니다. commit() 을 호출할때 commit 자체에서 내부적으로 flush() 를 호출하기 때문에 DB 에 SQL 쿼리가 전송되는 것이죠.&lt;/p&gt;
&lt;p&gt;그렇다면 만일 아래와 같이 비즈니스 로직이 수행된다면, &quot;hihi&quot; 라는 출력문을 결과로 얻게 되기전에는 아직 DB 에 쿼리문이 전송되지 않고 영속성 컨텍스트의 SQL 저장소 및 1차 캐시에만 반영되어 있는 상태입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JpaMain&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;EntityManagerFactory&lt;/span&gt; emf &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Persistence&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createEntityManagerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;info-in-xml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;EntityManager&lt;/span&gt; em &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; emf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createEntityManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;EntityTransaction&lt;/span&gt; tx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; em&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTransaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 트랜잭션 생성&lt;/span&gt;
        tx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 트랜잭션 시작&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; member &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hi&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;010-1111-1111&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            em&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;member&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 아직 DB 에 쿼리가 날라가지 않았다.&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hihi&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            tx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 트랜잭션 커밋 =&gt; 앞서 flush() 가 호출되지 않았다면 현재 commit 호출시 저장됐을것이다.&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            tx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;rollback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            em&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        emf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;entity-의-생명주기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#entity-%EC%9D%98-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0&quot; aria-label=&quot;entity 의 생명주기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Entity 의 생명주기&lt;/h2&gt;
&lt;p&gt;또 아셔야할게 있습니다. 바로 Entity 의 생명주기입니다. 엔티티에는 생명주기가 있는데, 크게 4가지 상태 속성으로 구분됩니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;영속 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;비영속 상태&lt;/li&gt;
&lt;li&gt;준영속 상태&lt;/li&gt;
&lt;li&gt;삭제 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;영속-상태-비영속-상태&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%81%EC%86%8D-%EC%83%81%ED%83%9C-%EB%B9%84%EC%98%81%EC%86%8D-%EC%83%81%ED%83%9C&quot; aria-label=&quot;영속 상태 비영속 상태 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;영속 상태, 비영속 상태&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; member &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
member&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
member&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;helloJPA&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 여기까지는 비영속 상태&lt;/span&gt;

em&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;member&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 영속 상태&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// =&gt; But, 아직 DB 에 저장되진 않았다. 즉 SQL 쿼리가 아직 안날라감&lt;/span&gt;

em&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;detach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;member&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 준영속 상태&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// =&gt; member 를 영속성 컨텍스트에서 지운다&lt;/span&gt;

em&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;member&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 실제 DB 에 영구 저장된 상태인 객체를 지우겠다는 요청&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위처럼 member 객체를 생성하고 엔티티 매니저 팩토리로부터 엔티티 매니저를 얻어와서, 엔티티 매니저의 persist() 를 호출해서 member 객체를 집어넣으면, entityManager 안에 있는 영속성 컨텍스트라는 곳에 member 객체가 들어가면서 영속 상태가 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;준영속-상태&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A4%80%EC%98%81%EC%86%8D-%EC%83%81%ED%83%9C&quot; aria-label=&quot;준영속 상태 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;준영속 상태&lt;/h3&gt;
&lt;p&gt;앞서 말씀드렸듯이 &lt;code class=&quot;language-text&quot;&gt;persist()&lt;/code&gt; 를 호출하면 영속상태가 됩니다. 이런 상황 외에도 영속 상태가 되는 상황이 1가지 더 있습니다. 준영속 상태란 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 상태입니다. 이렇게 되면 영속성 컨텍스트가 제공하는 기능인 &lt;code class=&quot;language-text&quot;&gt;Dirty Checking&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;업데이트&lt;/code&gt; 기능들을 사용하지 못합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;dirty-checking&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dirty-checking&quot; aria-label=&quot;dirty checking permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dirty Checking&lt;/h2&gt;
&lt;p&gt;그러면 저희가 JPA 를 통해 엔티티를 관리하면서 얻을 수 있는 이점은 뭘까요? 우선 더티체킹(Dirty Checking) 이 가능합니다. 엔티티 수정시 변경감지, 즉 Dirty Checking(변경감지) 라는 것을 할 수 있습니다. 아래처럼 persist() 를 하지 않아도 setName() 만 호출해도 DB 에 반영되죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;memberA&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setNamge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 영속 Entity 데이터 수정&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// =&gt; 더티 체킹으로 인해, persist() 를 호출하지 않아도 자동으로 데이터가 수정된다.&lt;/span&gt;

transaction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 트랜잭션 커밋&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;저희는 위 처럼 set() 함수를 호출시 update 와 관련한 메소드도 작성해야 할 것 같습니다. 그렇지않으면 DB 에 정상적으로 수정 변경내용이 반영되지 않을 것 같기 때문이죠. 하지만 JPA 는 자체적으로 변경내용을 체크하여 commit 시점에 내용을 반영해줍니다.&lt;/p&gt;
&lt;h2 id=&quot;동일성-보장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%9D%BC%EC%84%B1-%EB%B3%B4%EC%9E%A5&quot; aria-label=&quot;동일성 보장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동일성 보장&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%98-%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80isolation-level-ACID-%EC%84%B1%EC%A7%88%EC%97%90-%EB%8C%80%ED%95%B4&quot;&gt;트랜잭션의 격리수준(Transcation isolation level) 4단계, ACID 성질&lt;/a&gt; 에서도 살펴봤듯이, MySQL, PostgreSQL 와 같은 RDB 는 자체적으로 트랜잭션의 격리수준에 기반하여 격리 등급을 보장해주고 있습니다.&lt;/p&gt;
&lt;p&gt;JPA 에서는 1차 캐시의 REPEATABLE_READ 등급의 트랜잭션 격리수준을 DB 가 아닌 애플리케이션 차원에서도 제공해주고 있습니다. 예를들어 MySQL 을 사용시 스프링부트 애플리케이션 단에서도 REPEATABLE_READ 등급의 격리수준을 보장해주는 것이죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entityManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;member1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entityManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;member2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만일 위와 같은 코드를 진행시 출력결과는 true 가 나오는 것을 보장해주는 것입니다. 자바 컬렉션에서 똑같은 객체를 가져와서 == 연산자로 비교연산을 진행하면 똑같다는 결과를 얻을 수 있듯이, JPA 도 영속 엔티티의 동일성을 보장해줍니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.inflearn.com/course/ORM-JPA-Basic/dashboard&quot;&gt;김영한 자바 ORM 표준 JPA 프로그래밍 - 기본편&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[쿠버네티스(Kubernetes) 클러스터의 파드 배치 아키텍처]]></title><description><![CDATA[파드(Pod…]]></description><link>https://haon.site/haon/kubernetes/pad-yaml/</link><guid isPermaLink="false">https://haon.site/haon/kubernetes/pad-yaml/</guid><pubDate>Sun, 26 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;파드pod-란-무엇이고-왜-써야할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EB%93%9Cpod-%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EA%B3%A0-%EC%99%9C-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;파드pod 란 무엇이고 왜 써야할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파드(Pod) 란 무엇이고, 왜 써야할까?&lt;/h2&gt;
&lt;p&gt;이전에 설명했듯이 파드는 쿠버네티스의 기본 빌딩 블록입니다. 일반적으로 파드는 하나의 컨테이너만 포함합니다. 또 파드가 여러 컨테이너를 가지고 있을 경우에, 모든 컨테이너는 항상 하나의 워커 노드에서 실행되며 여러 워커 노드에 걸쳐 실행되지 않습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;파드안에 있는 모든 컨테이너는 동일한 1개의 노드에서 실행된다. 절대로 두 노드에 걸쳐서 배포되지 않는다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;연관된-프로세스-묶음-단위&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%B0%EA%B4%80%EB%90%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%AC%B6%EC%9D%8C-%EB%8B%A8%EC%9C%84&quot; aria-label=&quot;연관된 프로세스 묶음 단위 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;연관된 프로세스 묶음 단위&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Docker-%EB%8F%99%EC%9D%BC%ED%95%9C-%ED%98%B8%EC%8A%A4%ED%8A%B8%EC%97%90%EC%84%9C-%EA%B0%81-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B2%A9%EB%A6%AC%EB%8A%94-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%A5%BC-%ED%86%B5%ED%95%B4-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B0%80%EB%8A%A5%ED%95%9C%EA%B1%B8%EA%B9%8C&quot;&gt;[Docker] 도커는 어떻게 동일한 호스트에서 여러 프로세스를 컨테이너 단위로 격리시킬 수 있는걸까?&lt;/a&gt; 에서 도커는 컨테이너 별로 격리된 프로세스를 보장합니다. 컨테이너 모음을 사용해서 &lt;strong&gt;밀접하게 연관된 프로세스를 함께 실행&lt;/strong&gt;하면서 마치 단일 컨테이너 안에서 여러개의 프로세스가 함께 실행되는 것처럼 보이게 할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;같은-파드안의-컨테이너들의-격리수준을-다소-완화한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%99%EC%9D%80-%ED%8C%8C%EB%93%9C%EC%95%88%EC%9D%98-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%93%A4%EC%9D%98-%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80%EC%9D%84-%EB%8B%A4%EC%86%8C-%EC%99%84%ED%99%94%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;같은 파드안의 컨테이너들의 격리수준을 다소 완화한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;같은 파드안의 컨테이너들의 격리수준을 다소 완화한다&lt;/h3&gt;
&lt;p&gt;또 컨테이너 별로 서로 완벽히 격라된 환경이 구성된다고 했었으나, 쿠버네티스에서는 각 컨테이너 단위로 격리되는 것이 아닌 &lt;strong&gt;파드(Pod) 라는 컨테이너 그룹별로 격리된 환경을 구성합니다.&lt;/strong&gt; 즉, 쿠버네티스에서는 파드 안에 있는 모든 컨테이너가 자체 네임스페이스가 아닌 &lt;strong&gt;동일한 리눅스 네임스페이스를 공유&lt;/strong&gt;하도록 도커가 설정되는 것이죠.&lt;/p&gt;
&lt;p&gt;그에 따라서 파드 안에있는 컨테이너들이 특정 리소스들을 함께 공유하기위해 각 컨테이너가 완벽히 격리되지 않도록 하고, 격리수준이 다소 완화되도록 하는 것입니다.
파드의 모든 컨테이너는 같은 호스트 이름과 네트워크 인터페이스를 공유하게 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;동일한-ip-와-포트-공간을-공유하는-방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%9D%BC%ED%95%9C-ip-%EC%99%80-%ED%8F%AC%ED%8A%B8-%EA%B3%B5%EA%B0%84%EC%9D%84-%EA%B3%B5%EC%9C%A0%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;동일한 ip 와 포트 공간을 공유하는 방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동일한 IP 와 포트 공간을 공유하는 방법&lt;/h2&gt;
&lt;p&gt;다시 한번 되짚고 넘어갈 사항은, 파드 안의 컨테이너들은 동일한 네트워크 네임스페이스에서 실행되기 때문에 &lt;strong&gt;동일한 IP 주소와 포트 공간을 공유한다&lt;/strong&gt;는 것입니다.&lt;/p&gt;
&lt;p&gt;따라서 동일한 파드 안의 컨테이너게서 실행중인 프로세스들은 같은 포트 번호를 사용하지 않도록 주의해야 합니다. 그렇지 않으면 &lt;code class=&quot;language-text&quot;&gt;포트 충돌&lt;/code&gt; 이 발생하겠죠. 해당 파드의 모든 컨테이너들은 동일한 루프백 네트워크 인터페이스를 갖기 때문에, &lt;strong&gt;컨테이너들이 로컬호스트를 통해 통신할 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;파드-사이의-플랫-네트워크&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EB%93%9C-%EC%82%AC%EC%9D%B4%EC%9D%98-%ED%94%8C%EB%9E%AB-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC&quot; aria-label=&quot;파드 사이의 플랫 네트워크 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파드 사이의 플랫 네트워크&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/853f58ac-0647-4d57-a9ce-9da14748cfb6/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;또 클러스터의 모든 파드는 하나의 플랫한 공유 네트워크 주소 공간에 상주하기 때문에, &lt;strong&gt;모든 파드는 다른 파드의 IP 주소를 사용해서 접근하는 것이 가능합니다.&lt;/strong&gt; 동일한 워커노드에 있는 파드 간에 통신이 가능하죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;파드의-컨테이너들을-어떻게-구성해야하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EB%93%9C%EC%9D%98-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%93%A4%EC%9D%84-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B5%AC%EC%84%B1%ED%95%B4%EC%95%BC%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;파드의 컨테이너들을 어떻게 구성해야하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파드의 컨테이너들을 어떻게 구성해야하는가?&lt;/h2&gt;
&lt;p&gt;파드는 특정한 애플리케이션들을 호스팅합니다. &lt;strong&gt;여러 종류의 애플리케이션들을 모두 파드 하나에 넣는 대신에 여러 파드로 구성 및 분배시키고, 각 파드에는 밀접하게 관련있는 구성요소나 프로세스만 포함해야합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;기능-및-서비스별로-애플리케이션들을-여러-파드에-분할하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EB%8A%A5-%EB%B0%8F-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%B3%84%EB%A1%9C-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EB%93%A4%EC%9D%84-%EC%97%AC%EB%9F%AC-%ED%8C%8C%EB%93%9C%EC%97%90-%EB%B6%84%ED%95%A0%ED%95%98%EC%9E%90&quot; aria-label=&quot;기능 및 서비스별로 애플리케이션들을 여러 파드에 분할하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기능 및 서비스별로 애플리케이션들을 여러 파드에 분할하자&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e69664f8-e25c-413f-ad9c-f0ddb8d412cc/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;예를들어 위처럼 프론트, 백엔드 서버 컨테이너가 있을떄 이 둘을 한 파드에 놓는다면 다소 아쉬운 부분이 생깁니다. 백엔드 서버에서만 필요한 기능을 위해 파드를 건드릴때, 동일한 파드에 존재하는 프론트 서버 컨테이너도 함께 같은 워커노드에서 실행되어야 하기 때문이죠.&lt;/p&gt;
&lt;p&gt;또 클러스터에 2개의 워커노드가 있다고해보죠. 그 중에 1번째 워커노드안의 단일 파드에서 모든 애플리케이션이 실행된다면 워커 노드 1개만 사용해버리고 2번째 노드에서는 가만히 놀고있는 상태라서 이용할 수 있는 컴퓨팅 리소스(CPU 와 메모리) 를 활용하지 못하고 낭비하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/49facb8e-9cde-415d-b59d-e9b3b1badb04/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;따라서 파드를 세분화해서 여러 에플리케이션 프로세스를 분산시킬경우, 각 애플리케이션은 각기 다른 노드 여러개에 스케줄링되어서 infra structure 의 활용도를 향상시킬 수 있게됩니다.&lt;/p&gt;
&lt;h3 id=&quot;개별-확장이-가능하도록-여러-파드로-분할하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%9C%EB%B3%84-%ED%99%95%EC%9E%A5%EC%9D%B4-%EA%B0%80%EB%8A%A5%ED%95%98%EB%8F%84%EB%A1%9D-%EC%97%AC%EB%9F%AC-%ED%8C%8C%EB%93%9C%EB%A1%9C-%EB%B6%84%ED%95%A0%ED%95%98%EC%9E%90&quot; aria-label=&quot;개별 확장이 가능하도록 여러 파드로 분할하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;개별 확장이 가능하도록 여러 파드로 분할하자&lt;/h3&gt;
&lt;p&gt;또 여러 컨테이너를 단일 파드에 마구잡이로 넣으면 안되는 이뉴는 &lt;code class=&quot;language-text&quot;&gt;스케일링&lt;/code&gt; 때문입니다. 수평확장(Scale Out) 의 기본단위는 파드인데, 각 애플리케이션 특성별로 확장해야하는 정도가 다릅니다.&lt;/p&gt;
&lt;h3 id=&quot;여러-컨테이너를-파드로-묶을때-고려해야할-요소들&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%AC%EB%9F%AC-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%A5%BC-%ED%8C%8C%EB%93%9C%EB%A1%9C-%EB%AC%B6%EC%9D%84%EB%95%8C-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC%ED%95%A0-%EC%9A%94%EC%86%8C%EB%93%A4&quot; aria-label=&quot;여러 컨테이너를 파드로 묶을때 고려해야할 요소들 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;여러 컨테이너를 파드로 묶을때 고려해야할 요소들&lt;/h3&gt;
&lt;p&gt;따라서 컨테이너를 파드로 묶어서 그룹을 만들때는, 단일 파드로 넣을지 또는 2개이상의 여러 파드에 세분화해서 넣을지 결정하도록 아래와 같은 요소들을 고려해야합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;여러 컨테이너를 함께 실행해야 하는가, 또는 서로 다른 호스트에서 실행할 수 있는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;여러 컨테이너가 모여서 하나의 구성 요소를 나타내는가, 또는 개별적인 구성 요소인가?&lt;/li&gt;
&lt;li&gt;컨테이너가 함께, 혹은 개별적으로 스케일링돼야 하는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;yaml-디스크립터로-파드-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#yaml-%EB%94%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%84%B0%EB%A1%9C-%ED%8C%8C%EB%93%9C-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;yaml 디스크립터로 파드 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;YAML 디스크립터로 파드 생성&lt;/h2&gt;
&lt;p&gt;이제부터 YAML 매니페스트를 작성해서 파드를 생성해봅시다. 파드를 포함한 쿠버네티스의 리소스들은 일반적으로 쿠버네티스 Rest API 엔드포인트에 JSON 또는 YAML 매니페시트를 전송하여 생성합니다.
&lt;code class=&quot;language-text&quot;&gt;kubectl run&lt;/code&gt; 명령어로도 리소스 생성은 가능하지만, 제한된 속성 집합만 설정할 수 있어서 자세한 속성 정보를 가진 리소스 생성을 위해선 디스크립터 작성이 필수입니다.&lt;/p&gt;
&lt;h3 id=&quot;기본적인-파드의-yaml-디스크립터-조회&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9D%B8-%ED%8C%8C%EB%93%9C%EC%9D%98-yaml-%EB%94%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%84%B0-%EC%A1%B0%ED%9A%8C&quot; aria-label=&quot;기본적인 파드의 yaml 디스크립터 조회 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기본적인 파드의 YAML 디스크립터 조회&lt;/h3&gt;
&lt;p&gt;우선 지금까지 간편히 구축했던 (YAML 을 사용하지 않고 구축했던) 모든 파드를 조회해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;kubectl get pods -o wide&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/784deaa8-2e6f-4ce8-86e2-3ff0c09c914a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;기본적인 명령어로 생성한 파드중에 하나를 택해서, YAML 정의가 어떻게 이루어지는지 살펴봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;kubectl get po &quot;파드이름&quot; -o yaml
ex) kubectl get po kubia-59c9558478-4bfgf -o yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러면 아래와 같이 파드의 전체 YAML 정의를 조회할 수 있게됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;apiVersion&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; v1
kind&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pod&lt;/span&gt;
metadata&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  annotations&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    kubernetes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;created_by&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
  labels&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    run&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; kubia
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;59&lt;/span&gt;c9558478&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;bfgf
  namespace&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ... (중간 생략)&lt;/span&gt;
spec&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  containers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; image&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; msung99&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;redis&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;lock&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.3&lt;/span&gt;
    imagePullPolicy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IfNotPresent&lt;/span&gt;
    name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; kubia
    ports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; containerPort&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;
    protocol&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;TCP&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ... (중간 생략)&lt;/span&gt;
status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  conditions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; lastProbeTime&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
    lastTransitionTime&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
    statis&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;True&quot;&lt;/span&gt;
    type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ready&lt;/span&gt;
  containerStatuses&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; containerID&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; docker&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;f028123901203ba&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
    image&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; msung99&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;redis&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;lock&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.3&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ... (이하 생략)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;복잡해보이지만 핵심적인 구조 틀만 알아보고 넘어갑시다. 우선 위와같이 배포된 파드의 YAML 의 구성요소는 아래와 같은 키워드를 중점으로 알고계셔야합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;apiVersion : 현재 YAML 디스크립터에서 사용한 쿠버네티스 API 버전&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;kind : 쿠버네티스 오브젝트(리소스) 유형&lt;/li&gt;
&lt;li&gt;metadata: 파드 메타데이터(이름, 레이블, 어노테이션 등등) 에 관한 정보&lt;/li&gt;
&lt;li&gt;spec: 파드 정의내용 (파드 컨테이너 목록, 볼륨 등등)&lt;/li&gt;
&lt;li&gt;status: 파드와 그 안의 여러 컨테이너들의 상태 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그런데 위와 같은 YAML 디스크립터는 기본적으로 생성될 때 자동으로 만들어지는 디스크립터이며, 위안을 삼아보자면 저희가 새로운 파드를 만들때 직접 작성해야하는 YAML 내용은 훨씬 적으니 큰 걱정안하셔도 됩니다. &lt;strong&gt;YAML 파일을 간단히 정의해놓고 그에 기반해 파드를 생성하는 명령어를 사용하면, 상세정보가 자동으로 YAML 파일에 채워지고 파드가 생성됩니다.&lt;/strong&gt; 따라서 위의 핵심 키워드들만 잘 숙지하고 넘어갑시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;새로운-파드를-정의할-yaml-작성하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%88%EB%A1%9C%EC%9A%B4-%ED%8C%8C%EB%93%9C%EB%A5%BC-%EC%A0%95%EC%9D%98%ED%95%A0-yaml-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0&quot; aria-label=&quot;새로운 파드를 정의할 yaml 작성하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;새로운 파드를 정의할 YAML 작성하기&lt;/h2&gt;
&lt;p&gt;저희는 위에서 알게된 키워드들로 YAML 을 작성하면 새로운 파드를 생성할 수 있습니다. vim 편집기를 열어서 YAML 파일을 새롭게 생성하시고, 그 파일 안에다 아래와 같이 작성해줍시다. 이떄 YAML 파일명은 kubia-manual.yaml 로 하겠습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;apiVersion&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; v1   &lt;span class=&quot;token comment&quot;&gt;// 디스크립터는 쿠버네티스 API 버전 v1 를 준수한다.&lt;/span&gt;
kind&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pod&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;// 오브젝트 종류를 파드(Pod)로 명시&lt;/span&gt;
metadata&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;manual  &lt;span class=&quot;token comment&quot;&gt;// 파드 이름&lt;/span&gt;
spec&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  containers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; image&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; msung99&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;redis&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;lock&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.3&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 컨테이너를 만드는 컨테이너 이미지&lt;/span&gt;
    name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; kubia     &lt;span class=&quot;token comment&quot;&gt;// 파드에 생성된 컨테이너의 이름을 kubia 로 지정&lt;/span&gt;
    ports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; containerPort&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 애플리케이션이 수신하는 포트&lt;/span&gt;
      protocol&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;TCP&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;보시듯이 앞서 살펴봤던 기본 YAML 파일보다 훨씬 간단하게 작성해도 새로운 파드를 생성할 수 있게됩니다. 위 파일을 해석해봅시다. 우선 이 정의에서는 쿠버테니스 API v1 버전을 중시하며, 리소스 유형은 파드이고, 파드이름은 kubia-manual 입니ㅏ다. 또 msung99/redis-lock:0.1.3 이라는 임지에 기반해 컨테이너가 생성되며 8080포트에서 연결을 기다리는 상태가됩니다.&lt;/p&gt;
&lt;p&gt;이때 컨테이너 포트를 지정하지 않아도 (생략해도) 클라이언트에서 포트를 통해 파드에 연결할 수 있는 여부에 영향을 미치지 않습니다. 컨테이너가 0.0.0.0 주소에 열어둔 포트를 통해 접속을 허용할 경우, 파드 스펙에 포트를 명시적으로 나열하지 않아도 다른 파드에서 항상 해당 파드에 접근 가능합니다.&lt;/p&gt;
&lt;h3 id=&quot;파드-생성-및-조회&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EB%93%9C-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EC%A1%B0%ED%9A%8C&quot; aria-label=&quot;파드 생성 및 조회 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파드 생성 및 조회&lt;/h3&gt;
&lt;p&gt;앞서 만든 YAML 파일에 기반해 새로운 파드를 생성해봅시다. 또 생성한 파드의 정의 내용도 조회해봅시다. YAML 을 조회해보면, 저희는 분명히 YAML 의 정의문을 간단히 작성했음에도 불구하고 자동으로 상세 정보들이 알아서 채워진 모습을 확인할 수 있을 겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl create &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;manual&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yaml &lt;span class=&quot;token comment&quot;&gt;// 파드 생성&lt;/span&gt;

$ kubectl get po kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;manual &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;o yaml  &lt;span class=&quot;token comment&quot;&gt;// 파드 정의내용 YAML 파일 조회&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 또는 kubectl get po kubia-manual -o json&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// =&gt; YAML 말고 JSON 파일 포맷으로 파드 정의내용 조회하는 것&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또 파드 목록을 조회해보면, 아래와 같이 kubia-manual 이라는 새로운 파드가 생성된 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c4491ba7-fe86-46a6-9ef0-8e23ec9ef78b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;파드에-요청-보내기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EB%93%9C%EC%97%90-%EC%9A%94%EC%B2%AD-%EB%B3%B4%EB%82%B4%EA%B8%B0&quot; aria-label=&quot;파드에 요청 보내기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파드에 요청 보내기&lt;/h3&gt;
&lt;p&gt;위와 같이 여러개의 파드가 실행중일때 어떻게해야 파드의 실제 동작을 볼 수 있을까요? 이전에는 &lt;code class=&quot;language-text&quot;&gt;kubectl expose&lt;/code&gt; 로 외부에 로드밸런서 IP 를 유출시켜서, 외부에서 파드에 접속할 수 있는 서비스 오브젝트를 만들었습니다.&lt;/p&gt;
&lt;p&gt;이번에는 간단한 테스트를 진행하기위해 &lt;code class=&quot;language-text&quot;&gt;포트 포워딩&lt;/code&gt; 을 구성해서, 서비스를 거치지 않고 특정 파드와 통신해보겠습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ kubectl port-forward kubia-manual 8888:8080&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5c4b5647-2990-49ea-9d2f-8415f7fec7df/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위와 같이 머신의 로컬포트 8888을 kubia-manual 파드의 8080포트로 향하게 설정해줍시다. 그러면 포트 포워딩이 실행돼 이제 로컬 포트로 파드에 연결할 수 있게됩니다. 앞으로 다른 터미널에서 localhost:8888 에서 실행되고 있는 kubectl port-forward 프록시를 통해 HTTP 요청을 해당 파드에 보낼 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금까지 파드를 어떻게 배치하고 관리하며, YAML 디스크립터를 직접 작성해서 새로운 파드를 작성하는 방법에 대해 다루어봤습니다. 이어지는 포스팅에서는 레이블, 네임스페이스를 통해 파드를 구성하는 기법에 대해 자세히 다루어볼까합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.manning.com/books/kubernetes-in-action-second-edition&quot;&gt;Kubernetes in Action, Second Edition&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[쿠버네티스(Kubernetes) 파드, 워커노드를 활용한 스캐줄링 방법]]></title><description><![CDATA[[Kubernetes] 클러스터의 파드(Pod) 배치 아키텍처와 YAML 디스크립터로 파드 관리하기 에서 살펴봤듯이, YAML 또는 JSON…]]></description><link>https://haon.site/haon/kubernetes/scheduling/</link><guid isPermaLink="false">https://haon.site/haon/kubernetes/scheduling/</guid><pubDate>Sun, 26 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Kubernetes-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EC%9D%98-%ED%8C%8C%EB%93%9CPod-%EB%B0%B0%EC%B9%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%99%80-%EB%94%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%84%B0%EB%A1%9C-%ED%8C%8C%EB%93%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0&quot;&gt;[Kubernetes] 클러스터의 파드(Pod) 배치 아키텍처와 YAML 디스크립터로 파드 관리하기&lt;/a&gt; 에서 살펴봤듯이, YAML 또는 JSON 디스크립터를 통해 새로운 파드를 생성하고 스캐줄링 할 수 있습니다. 이 내용에 이이서 파드에 대해 레이블, 레이블 셀렉터, 네임스페이스를 활용하여 어떻게 파드 관리를 효율적으로 할 수 있는지에 대해 다루어보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;클러스터의-파드의-수가-너무-많아졌다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EC%9D%98-%ED%8C%8C%EB%93%9C%EC%9D%98-%EC%88%98%EA%B0%80-%EB%84%88%EB%AC%B4-%EB%A7%8E%EC%95%84%EC%A1%8C%EB%8B%A4&quot; aria-label=&quot;클러스터의 파드의 수가 너무 많아졌다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클러스터의 파드의 수가 너무 많아졌다!&lt;/h2&gt;
&lt;p&gt;클러스터에서 애플리케이션을 배포할 때 그 규모가 커질수록 파드 수가 증가하고 세부적인 여러 부분 집합으로 나눌 필요가 있습니다. 특히 마이크로서비스 아키텍처의 경우 파드의 수만 무려 20개 이상을 넘게됩니다.&lt;/p&gt;
&lt;p&gt;따라서 모든 개발자와 관리자는 어떤 파드가 어떤 것인지 손쉽게 식별할 수 있도록 임의의 기준에 따라 작은 그룹으로 조직화하는 방법이 필요합니다. 각 파드에 대해 개별적으로 작업을 수행하기보다, 특정 그룹에 속한 모든 파드에 관해 한번에 작업하기를 원할 것이죠.&lt;/p&gt;
&lt;p&gt;이를위해 등장한 것이 레이블, 레이블 셀렉터, 네임스페이스입니다. 이들로 파드와 기타 쿠버네티스 오브젝트에 대한 조직화를 수행할 수 있죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;레이블-labels&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%EC%9D%B4%EB%B8%94-labels&quot; aria-label=&quot;레이블 labels permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레이블 (labels)&lt;/h2&gt;
&lt;p&gt;레이블은 K8S 오브젝트에 첨부하는 Key-value 값으로, 마치 파드에 표딱지처럼 붙여놓는 식별자로 이해하면 쉽습니다. 또 key-value 쌍은 레이블 셀렉터를 사용해 원하는 리소스를 추출해낼 때 사용됩니다.
클러스터에 접속하는 개발자 및 운영자는 각 파드의 레이블을 보고 시스템 구조와 각 파드가 무슨 기능을 하는지를 파악하고, 어떤 적합한 위치에 존재하는지 확인할 수 있게됩니다.&lt;/p&gt;
&lt;p&gt;예를들어 아래와 같이 여러 종류의 파드가 클러스터 내에 존재한다고 해봅시다. 이들은 마이크로서비스 아키텍처 안에서 분류되지 않은 상태입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/40b2fde3-f1b3-4aaa-a599-b1597eb05880/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이 상황에 각 파드에 레이블을 붙여서 누구나 쉽게 이해할 수 있는 체계적인 시스템을 구성해봅시다. 각 파드에는 아래처럼 2개의 레이블을 임의로 붙여봤습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/413f30f3-dab8-4851-9991-d8d2c249d47f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;우선 app 이라는 레이블을 정의해줬습니다. 파드가 속한 애플리케이션, 구성요소를 설명해주는 레이블이죠. 또 rel 이라는 레이블로 정의해줬는데, 이는 해당 파드에서 실행중인 애플리케이션이 출시 버전인지, 베타 버전인지를 설명해주는 레이블입니다. 즉 rel 레이블이 붙은 파드는 key 값이 rel 이며, value 값에는 beta, release 와 같은 값들이 할당될겁니다.&lt;/p&gt;
&lt;h3 id=&quot;파드-생성시-레이블-지정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EB%93%9C-%EC%83%9D%EC%84%B1%EC%8B%9C-%EB%A0%88%EC%9D%B4%EB%B8%94-%EC%A7%80%EC%A0%95&quot; aria-label=&quot;파드 생성시 레이블 지정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파드 생성시 레이블 지정&lt;/h3&gt;
&lt;p&gt;이제 파드에 레이블을 붙이는 방법을 알아봅시다. 우선 YAML 파일로 파드 생성시 lables 옵션으로 레이블을 생성 및 지정해줄 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// kubia-manual-with-labels.yaml 파일&lt;/span&gt;
apiVersion&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; v1
kind&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pod&lt;/span&gt;
metadata&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;manual&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ver2
  labels&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;token comment&quot;&gt;// 레이블 생성 =&gt; creation_method, env 라는 2개의 레이블을 생성함&lt;/span&gt;
    creation_method&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; manual
    env&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; prod
spec&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ... (이하 생략)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞서 생성한 yaml 파일에 기반하여 파드를 생성합시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl create &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;manual&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;labels&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이어서 파드를 조회하는데, 그냥 get pods 명령어는 레이블을 표시하지 않으므로
&lt;code class=&quot;language-text&quot;&gt;--show-labels&lt;/code&gt; 를 사용해서 레이블 필드까지 함께 조회해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl get po &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;show&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;labels&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;출력결과&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;NAME&lt;/span&gt;			&lt;span class=&quot;token constant&quot;&gt;READY&lt;/span&gt;	&lt;span class=&quot;token constant&quot;&gt;STATUS&lt;/span&gt;	  &lt;span class=&quot;token constant&quot;&gt;RESTARTS&lt;/span&gt;	&lt;span class=&quot;token constant&quot;&gt;LABELS&lt;/span&gt;
kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;manual    &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;	    &lt;span class=&quot;token class-name&quot;&gt;Running&lt;/span&gt;    &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;        &lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;none&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;manual2	&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;     &lt;span class=&quot;token class-name&quot;&gt;Running&lt;/span&gt;    &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;        create_method&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;manual&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;env&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;prod&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 특정 레이블에만 관심있을 경우 -L 옵션을 부여해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl get po &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;L&lt;/span&gt; creation_method&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;env&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;기존-파드-레이블-수정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EC%A1%B4-%ED%8C%8C%EB%93%9C-%EB%A0%88%EC%9D%B4%EB%B8%94-%EC%88%98%EC%A0%95&quot; aria-label=&quot;기존 파드 레이블 수정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기존 파드 레이블 수정&lt;/h3&gt;
&lt;p&gt;또 기존 파드에 레이블을 추가하거나 수정할 수 있습니다.
아래는 creation_method=manual 레이블을 추가하는 과정입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ kubectl label po kubia-manual creation_method=manual&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또 기존 레이블을 변경시에는 &lt;code class=&quot;language-text&quot;&gt;--overwrite&lt;/code&gt; 옵션을 사용하면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ kubectl label po kubia-manual-v2 env=debug --overwrite&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;실행결과&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl get po &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;show&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;labels

&lt;span class=&quot;token constant&quot;&gt;NAME&lt;/span&gt;			&lt;span class=&quot;token constant&quot;&gt;READY&lt;/span&gt;	&lt;span class=&quot;token constant&quot;&gt;STATUS&lt;/span&gt;	  &lt;span class=&quot;token constant&quot;&gt;RESTARTS&lt;/span&gt;	&lt;span class=&quot;token constant&quot;&gt;LABELS&lt;/span&gt;
kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;manual    &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;	    &lt;span class=&quot;token class-name&quot;&gt;Running&lt;/span&gt;    &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;        creation_method&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;manual
kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;manual2	&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;     &lt;span class=&quot;token class-name&quot;&gt;Running&lt;/span&gt;    &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;        create_method&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;manual&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;env&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;prod&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;레이블-셀렉터를-이용한-파드-부분집합-나열&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%EC%9D%B4%EB%B8%94-%EC%85%80%EB%A0%89%ED%84%B0%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%8C%8C%EB%93%9C-%EB%B6%80%EB%B6%84%EC%A7%91%ED%95%A9-%EB%82%98%EC%97%B4&quot; aria-label=&quot;레이블 셀렉터를 이용한 파드 부분집합 나열 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레이블 셀렉터를 이용한 파드 부분집합 나열&lt;/h2&gt;
&lt;p&gt;앞서 레이블을 각 파드에 부착시킨후, 레이블 셀렉터를 활용하면 특정 레이블로 태그된 파드의 부분 집합을 선택해 원하는 작업을 수행할 수 있습니다.
&lt;strong&gt;레이블 셀렉터는 특정 값과 레이블을 갖는지 여부에 따라 리소스를 필터링하는 기준이 됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;레이블 셀렉터는 리소스 중에서 다음 기준에 따라서 리소스를 선택합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;특정한 키를 포함하거나 포함하지 않는 레이블&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;특정한 키와 값을 가지는 레이블&lt;/li&gt;
&lt;li&gt;특정한 키를 갖고 있지만, 다른 값을 가진 레이블&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl get po &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;l creation_method&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;manual  &lt;span class=&quot;token comment&quot;&gt;// creation_method=manual 레이블을 가지는 모든 파드조회&lt;/span&gt;
$ kubectl get po &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;l env         &lt;span class=&quot;token comment&quot;&gt;// env 레이블 key 를 가지는 모든 파드조회&lt;/span&gt;
$ kubectl get po &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;l &lt;span class=&quot;token char&quot;&gt;&apos;!env&apos;&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;// env 레이블을 가지고있지 않은 모든 파드조회&lt;/span&gt;
$ kubectl get po &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;l env&lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt;manual  &lt;span class=&quot;token comment&quot;&gt;// env 레이블을 가지는 파드중에 value 가 manual 이 아닌 모든 파드조회&lt;/span&gt;
$ kubectl get po &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;l env in &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prod&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;devel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//  env 레이블 value 값이 prod 또는 devel 인 파드&lt;/span&gt;
$ kubectl get po &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;l evn notin &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prod&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;devel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;워커-노드에-레이블을-적용해-파드-스캐줄링-제한하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%8C%EC%BB%A4-%EB%85%B8%EB%93%9C%EC%97%90-%EB%A0%88%EC%9D%B4%EB%B8%94%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%B4-%ED%8C%8C%EB%93%9C-%EC%8A%A4%EC%BA%90%EC%A4%84%EB%A7%81-%EC%A0%9C%ED%95%9C%ED%95%98%EA%B8%B0&quot; aria-label=&quot;워커 노드에 레이블을 적용해 파드 스캐줄링 제한하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;워커 노드에 레이블을 적용해 파드 스캐줄링 제한하기&lt;/h2&gt;
&lt;p&gt;지금까지 생성한 모든 파드는 워커 노드 전반에 걸쳐서 무작위로 스캐줄링 되었습니다. 쿠버네티스에서 파드가 어떤 노드에 스캐줄링 되었는지는 중요하지 않기 떄문이죠.&lt;/p&gt;
&lt;p&gt;하지만 &lt;strong&gt;아주 간혹 파드를 스캐줄링할 위치를 정해야 할때&lt;/strong&gt;가 있습니다. 에를들어 각 워커노드의 하드웨어 구성이 동일하지 않은경우를 생각해볼 수 있습니다. 한 워커노드는 HDD 를 가지고 있고, 나머지 모든 노드들은 SSD 를 가지고있는 경우, 한 파드를 한 그룹에만 스캐줄링하고 나머지 파드들은 다른 그룹에다 스캐줄링되도록 할 수있죠.&lt;/p&gt;
&lt;p&gt;앞서 언급했듯이, 노드를 포함한 모든 쿠버네티스 오브젝트에 레이블을 부착할 수 있습니다. 예를들어 GPU 를 가지고 있는 노드에 레이블을 표기할때 아래처럼 하면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl label node my&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;kubia gpu&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 노드에 레이블 표기&lt;/span&gt;
$ kubectl get nodes &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;l gpu&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 레이블 셀렉터로 원하는 노드 추출&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;특정-노드에-파드-스케줄링하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B9%EC%A0%95-%EB%85%B8%EB%93%9C%EC%97%90-%ED%8C%8C%EB%93%9C-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81%ED%95%98%EA%B8%B0&quot; aria-label=&quot;특정 노드에 파드 스케줄링하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;특정 노드에 파드 스케줄링하기&lt;/h3&gt;
&lt;p&gt;앞서 GPU 에 존재하는 워커노드에 레이블 표기를 했다면, 이를 활용해 YAML 파일을 작성해서 해당 워커노드에 특정 파드를 스캐줄링 할 수 있습니다.
아래처럼 spec 섹션안에 nodeSelector 필드에다 정보를 추가해주면 됩니다. 파드를 생성할 때 스케줄러는 gpu=true 레이블을 가지고 있는 노드중에서 선택해서 스캐줄링합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;apiVersion&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; v1
kind&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pod&lt;/span&gt;
metadata&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;gpu
spec&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  nodeSelector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    gpu&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt;
  containers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; image&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; msung99&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;redis&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;lock&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.3&lt;/span&gt;
    name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; kubia&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;네임스페이스를-사용한-그룹화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%84%A4%EC%9E%84%EC%8A%A4%ED%8E%98%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EA%B7%B8%EB%A3%B9%ED%99%94&quot; aria-label=&quot;네임스페이스를 사용한 그룹화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;네임스페이스를 사용한 그룹화&lt;/h2&gt;
&lt;p&gt;그런데 레이블을 활용하다보면 한계점이 있습니다. 각 파드에 레이블을 표기하고 그룹화할 수 있는데, 각 오브젝트는 여러 레이블을 가질 수 있기 때문에, &lt;strong&gt;오브젝트 그룹은 서로 겹쳐질 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;오브젝트를-겹치지-않는-그룹으로-분할하고-싶을때&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8%EB%A5%BC-%EA%B2%B9%EC%B9%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EA%B7%B8%EB%A3%B9%EC%9C%BC%EB%A1%9C-%EB%B6%84%ED%95%A0%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%9D%84%EB%95%8C&quot; aria-label=&quot;오브젝트를 겹치지 않는 그룹으로 분할하고 싶을때 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;오브젝트를 겹치지 않는 그룹으로 분할하고 싶을때?&lt;/h3&gt;
&lt;p&gt;그렇다면 오브젝트를 겹치지 않는 그룹으로 분할하고 싶을때는 어떻게할까요? 한 번에 하나의 그룹 안에서만 작업하고 싶을겁니다. 이런 이유로 오브젝트를 네임 스페이스로 그룹화합니다. 이때 네임스페이스란 프로세스를 격리하는 리눅스 네임스페이스가 아님에 주의합시다.&lt;/p&gt;
&lt;p&gt;네임스페이스는 모든 리소스를 하나의 단일 네임스페이스에 두는 대신에 여러 네임스페이스로 분할할 수 있고, 이렇게 분리된 네임스페이스는 같은 리소스 이름을 다른 네임스페이스에 걸쳐 여러 번 사용할 수 있게 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl get ns  &lt;span class=&quot;token comment&quot;&gt;// 모든 네임스페이스 조회&lt;/span&gt;

&lt;span class=&quot;token constant&quot;&gt;NAME&lt;/span&gt;			&lt;span class=&quot;token constant&quot;&gt;LABELS&lt;/span&gt; 		&lt;span class=&quot;token constant&quot;&gt;STATUS&lt;/span&gt;		&lt;span class=&quot;token constant&quot;&gt;AGE&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; 		&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;none&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;		&lt;span class=&quot;token class-name&quot;&gt;Active&lt;/span&gt;		&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;h
kube&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt;		&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;none&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;		&lt;span class=&quot;token class-name&quot;&gt;Active&lt;/span&gt;		&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;h
kube&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;system		&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;none&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;		&lt;span class=&quot;token class-name&quot;&gt;Active&lt;/span&gt;		&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;h&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 네임스페이스를 조회해봅시다. 저희는 지금까지 kubectl get 명령어를 이용하여 리소스를 나열할때 네임스페이스를 명시한 적이 없기때문에, default 네임스페이스에서만 작업을 진행한 것입니다.
즉, &lt;strong&gt;kubectl 명령어는 항상 기본적으로 default 네임스페이스에 속해있는 오브젝트들만 표시한 것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl get po &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;namespace kube&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;system&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또 위 명령어를 사용시 여러 파드들이 출력되는데, 이 파드들은 추후 다루겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;네임스페이스를-왜-사용해야할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%84%A4%EC%9E%84%EC%8A%A4%ED%8E%98%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;네임스페이스를 왜 사용해야할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;네임스페이스를 왜 사용해야할까?&lt;/h3&gt;
&lt;p&gt;네임스페이스를 사용해 서로 관계없는 리소스를 겹치지 않는 그룹으로 분리할 수 있습니다. 여러 사용자 또는 그룹이 동일한 쿠버네티스 클러스터를 사용하고 있고, 각자 자신의 리소스를 관리한다면 각각 고유한 네임스페이스를 사용해야합니다.&lt;/p&gt;
&lt;p&gt;이렇게 하면 &lt;strong&gt;다른 사용자의 리소스를 사용하거나 수정 및 삭제하지 않도록 주의를 기울일 필요가 없습니다.&lt;/strong&gt; 또 네임스페이스가 리소스 이름에 관한 접근범위를 제공해주므로 &lt;strong&gt;리소스 이름이 충돌하는 경우를 걱정할 필요가 없습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;네임스페이스-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%84%A4%EC%9E%84%EC%8A%A4%ED%8E%98%EC%9D%B4%EC%8A%A4-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;네임스페이스 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;네임스페이스 생성&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;YAML 으로 아래처럼 생성 가능합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;apiVersion&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; v1
kind&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Namespace&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// 오브젝트 타입을 네임스페이스로 명시&lt;/span&gt;
metadata&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; custom&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;namespace   &lt;span class=&quot;token comment&quot;&gt;// 네임스페이스 이름&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;또 명령어 한줄로도 생성 가능합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl create namespace my&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;namespace&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;리소스오브젝트-생성시-네임스페이스-부여&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%EC%86%8C%EC%8A%A4%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%EC%8B%9C-%EB%84%A4%EC%9E%84%EC%8A%A4%ED%8E%98%EC%9D%B4%EC%8A%A4-%EB%B6%80%EC%97%AC&quot; aria-label=&quot;리소스오브젝트 생성시 네임스페이스 부여 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리소스(오브젝트) 생성시 네임스페이스 부여&lt;/h3&gt;
&lt;p&gt;기존에 생성해둔 네임스페이스가 있을때, 파드와 같은 오브젝트를 생성할때 네임스페이스를 할당 가능합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl create &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f kubia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;manual &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;n my&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;namespace
&lt;span class=&quot;token comment&quot;&gt;// my-namespace 네임스페이스는 예전에 만들어 둔것. kubia-manual 라는&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 새로운 파드 오브젝트 생성시 네임스페이스를 할당해줌&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;파드-중지와-제거&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EB%93%9C-%EC%A4%91%EC%A7%80%EC%99%80-%EC%A0%9C%EA%B1%B0&quot; aria-label=&quot;파드 중지와 제거 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파드 중지와 제거&lt;/h2&gt;
&lt;p&gt;마지막으로 파드를 중지하고 제거하는 방법에 대해 자세히 다루어보겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;이름으로-파드-삭제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%A6%84%EC%9C%BC%EB%A1%9C-%ED%8C%8C%EB%93%9C-%EC%82%AD%EC%A0%9C&quot; aria-label=&quot;이름으로 파드 삭제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이름으로 파드 삭제&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl delete po my&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;pods&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;파드를 삭제하면 그 안의 모든 컨테이너는 종료됩니다. 쿠버네티스는 &lt;code class=&quot;language-text&quot;&gt;SIGTERM&lt;/code&gt; 신호를 프로세스에 보내고 지정된 시간(기본값 30초) 동안 기다리다가, 시간내에 종료되지 않으면 &lt;code class=&quot;language-text&quot;&gt;SIGKILL&lt;/code&gt; 신호를 통해 종료시킵니다.&lt;/p&gt;
&lt;h3 id=&quot;레이블-셀렉터로-파드-삭제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%EC%9D%B4%EB%B8%94-%EC%85%80%EB%A0%89%ED%84%B0%EB%A1%9C-%ED%8C%8C%EB%93%9C-%EC%82%AD%EC%A0%9C&quot; aria-label=&quot;레이블 셀렉터로 파드 삭제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레이블 셀렉터로 파드 삭제&lt;/h3&gt;
&lt;p&gt;레이블 셀렉터로 파드를 모두 중지 및 삭제할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl delete po &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;l creation_method&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;manual&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;네임스페이스로-파드-삭제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%84%A4%EC%9E%84%EC%8A%A4%ED%8E%98%EC%9D%B4%EC%8A%A4%EB%A1%9C-%ED%8C%8C%EB%93%9C-%EC%82%AD%EC%A0%9C&quot; aria-label=&quot;네임스페이스로 파드 삭제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;네임스페이스로 파드 삭제&lt;/h3&gt;
&lt;p&gt;마찬가지로 네임 스페이스로 파드를 모두 중지 및 삭제할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl delete ns my&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;namespace&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또 특정 파드를 삭제하는 대신 &lt;code class=&quot;language-text&quot;&gt;--all&lt;/code&gt; 옵션으로 현재 네임스페이스에 있는 모든 파드를 삭제하도록 할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl delete po &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;all&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;모든-리소스-삭제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A8%EB%93%A0-%EB%A6%AC%EC%86%8C%EC%8A%A4-%EC%82%AD%EC%A0%9C&quot; aria-label=&quot;모든 리소스 삭제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;모든 리소스 삭제&lt;/h3&gt;
&lt;p&gt;마지막으로, 하나의 명령으로 현재 네임스페이스에 있는 모든 종류의 오브젝트(Deployment Controller, 파드, 서비스) 를 모두 삭제시킬 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl delete all &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;all&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.manning.com/books/kubernetes-in-action-second-edition&quot;&gt;Kubernetes in Action, Second Edition&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[쿠버네티스(Kubernetes) 단일 환경에서 애플리케이션 배포 및 수평확장(Scale Out) 하기]]></title><description><![CDATA[시작에 앞서 쿠버네티스 위에서 애플리케이션을 실행할 수 있는 방법은 다양합니다. 보통 배포하고자 하는 구성요소를 기술한 JSON 이나 YAML…]]></description><link>https://haon.site/haon/kubernetes/scale-out/</link><guid isPermaLink="false">https://haon.site/haon/kubernetes/scale-out/</guid><pubDate>Fri, 24 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;시작에-앞서&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%9E%91%EC%97%90-%EC%95%9E%EC%84%9C&quot; aria-label=&quot;시작에 앞서 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시작에 앞서&lt;/h2&gt;
&lt;p&gt;쿠버네티스 위에서 애플리케이션을 실행할 수 있는 방법은 다양합니다. 보통 배포하고자 하는 구성요소를 기술한 JSON 이나 YAML 매니페스트를 구성해야하지만, 간단한 명령어 한 줄로 애플리케이션을 실행해봅시다. 또 그 과정에서 파드(Pods), 서비스 오브젝트, 레플리카의 유지적인 메커니즘에 대해 알아봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;애플리케이션-구동-환경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EA%B5%AC%EB%8F%99-%ED%99%98%EA%B2%BD&quot; aria-label=&quot;애플리케이션 구동 환경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;애플리케이션 구동 환경&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Kubernetes-Minikube-%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%8B%A8%EC%9D%BC-%EB%85%B8%EB%93%9C-%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EA%B5%AC%EC%B6%95&quot;&gt;[Kubernetes] Minikube 를 활용한 단일 노드 쿠버네티스 클러스터 환경 구축&lt;/a&gt; 에서도 봤듯이, 쿠버네티스 실습을 위한 단일 클러스터 환경을 구축하고 싶다면 Minikube 를 활용하는 것이 적절합니다. Minikube 에 기반하여 클러스터를 구축할 것이며, 파드수를 늘리거나 인스턴스를 자동으로 늘리는 환경을 구성하늗 등 다양한 메커니즘을 알아보겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;이미지-실행&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%8B%A4%ED%96%89&quot; aria-label=&quot;이미지 실행 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이미지 실행&lt;/h3&gt;
&lt;p&gt;애플리케이션을 실행하는 가장 간단한 방법은 kubectl run 명령어를 사용해서, JSON 이나 YAML 을 사용하지 않고 필요한 모든 구성요소를 생성하는 방법입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;kubectl create deployment kubia &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;image&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;msung99&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;maestro&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.0&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// kubectl run kubia --image=msung99/maestro:0.1.0 --port=8080&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;쿠버네티스에서 클러스터의 한 단위인 파드(Pods) 라는것을 생성 및 관리하는 컨트롤러가 존재합니다. 그 컨트롤러에는 크게 &lt;code class=&quot;language-text&quot;&gt;replication controller&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;deloyment controller&lt;/code&gt; 가 존재하죠. 저희는 이 중에 depolyment 를 기반으로 클러스터를 구성할 것입니다. 참고로 이때 레플리케이션 컨트롤러는 deprecated 되었고, 추후에 사라질 예정이라고합니다.&lt;/p&gt;
&lt;p&gt;예전에는 &lt;code class=&quot;language-text&quot;&gt;--genertaor&lt;/code&gt; 옵션을 통해 레플리케이션 컨트롤러를 생성했지만, 말했듯이 deprecated 되었기 때문에 현재는 사용 불가합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// --generator 사용 불가&lt;/span&gt;
kubectl create deployment kubia &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;image&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;msung99&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;maestro&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;generator&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;run&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;v1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;파드pod&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EB%93%9Cpod&quot; aria-label=&quot;파드pod permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파드(Pod)&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e78f1598-5268-469f-9032-cbf3dad54744/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;쿠버네티스에서는 개별 컨테이너를 직접적으로 일일이 다루지 않습니다. 대신 &lt;strong&gt;함께 배치된 다수의 컨테이너라는 개념&lt;/strong&gt;을 사용하죠. 이 컨테이너의 그룹을 파드(Pod) 라고 합니다.
즉, 파드란 쿠버네티스 환경의 가장 작은 하나의 단위로써, 하나 이상의 밀접하게 연관된 여러 컨테이너의 집합을 의미합니다.&lt;/p&gt;
&lt;p&gt;여러 파드는 동일한 워커 노드에서 같은 리눅스 네임스페이스로 함께 실행됩니다. 각 파드는 자체 IP, 호스트 이름, 프로세스 등이 있는 논리적으로 분리된 머신입니다.&lt;/p&gt;
&lt;p&gt;또 동일한 파드에 있는 여러 컨테이너는 서로 완벽히 격리된 프로세스를 보장합니다.&lt;/p&gt;
&lt;h3 id=&quot;파드-조회&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EB%93%9C-%EC%A1%B0%ED%9A%8C&quot; aria-label=&quot;파드 조회 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파드 조회&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;kubectl get pods&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞서 &lt;code class=&quot;language-text&quot;&gt;create&lt;/code&gt; 명령어로 만든 클러스터의 모든 파드를 조회해봅시다. 쿠버네티스에서 가장 기본적인 단위는 컨테이너가 아닌 파드라고 했습니다. 따라서 개별 컨테이너를 조회할 수 없는대신, 위 명령어처럼 파드를 조회해야 합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl get pods
&lt;span class=&quot;token constant&quot;&gt;NAME&lt;/span&gt;                     &lt;span class=&quot;token constant&quot;&gt;READY&lt;/span&gt;     &lt;span class=&quot;token constant&quot;&gt;STATUS&lt;/span&gt;    &lt;span class=&quot;token constant&quot;&gt;RESTARTS&lt;/span&gt;   &lt;span class=&quot;token constant&quot;&gt;AGE&lt;/span&gt;
kiada&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9d&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;785&lt;/span&gt;b578&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p449x    &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;       &lt;span class=&quot;token class-name&quot;&gt;Pending&lt;/span&gt;   &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;          &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;m&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 이때 위처럼 파드의 status 가 Pending 라면 파드에 아직 컨테이너가 준비되지 않은 상태입니다. 워커 노드가 컨테이너를 실행하기전에 컨테이너 이미지를 다운로드하는 중이기 때문이죠. 다운로드 완료후 파드에 컨테이너가 생성되면 status 가 Running 으로 바뀌게됩니다.&lt;/p&gt;
&lt;h3 id=&quot;백그라운드에서-발생한-동작-메커니즘&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C%EC%97%90%EC%84%9C-%EB%B0%9C%EC%83%9D%ED%95%9C-%EB%8F%99%EC%9E%91-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98&quot; aria-label=&quot;백그라운드에서 발생한 동작 메커니즘 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;백그라운드에서 발생한 동작 메커니즘&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/245667dd-b69c-4e65-8168-e1cba90a1e6a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4&gt;1. Deloyment Controller 생성&lt;/h4&gt;
&lt;p&gt;앞서 &lt;code class=&quot;language-text&quot;&gt;create&lt;/code&gt; 로 만든 클러스터 생성 메커니즘을 자세히 알아봅시다. 우선 &lt;code class=&quot;language-text&quot;&gt;kubectl&lt;/code&gt; 클라이언트 명령어를 실행하면 쿠버네티스의 API 서버로 REST HTTP 요청을 전달하고, 클러스터에 배포 객체인 &lt;strong&gt;Deloyment Controller 를 마스터 노드(Control Plane) 에 생성&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;h4&gt;2. 새로운 파드를 생성하고, 해당 파드를 특정 워커노드에 스캐줄링한다&lt;/h4&gt;
&lt;p&gt;Deloyment 컨트롤러는 새로운 파드를 생성하고 스캐줄러(Scheduler) 에 의해 워커 노드 중 하나에 &lt;code class=&quot;language-text&quot;&gt;스케줄링(Scheduling)&lt;/code&gt; 이 됩니다. 이때 주의할점은, &lt;strong&gt;스케줄링이란 파드가 특정 노드에 할당됨을 뜻하는 것&lt;/strong&gt;입니다. (기존에 저희가 알던 스캐줄링과 다른것입니다!)&lt;/p&gt;
&lt;h4&gt;3. Kubelet : 이미지 pull 명령&lt;/h4&gt;
&lt;p&gt;해당 워커 노드의 Kubelet 은 파드가 스케줄링됐다는 것을 보고 이미지가 로컬에 없기 때문에, 도커에게 레지스트리(DockerHub) 로부터 이미지를 pull 받으라고 명령을 지시합니다.&lt;/p&gt;
&lt;h4&gt;4. 컨테이너 실행&lt;/h4&gt;
&lt;p&gt;직전에 Kubelet 은 도커한테 이미지를 pull 받도록 했었죠? 그렇게 pull 받은 이미지에 기반하여 컨테이너를 실행합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;서비스-객체--로드밸런서--public-ip-노출시키기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B0%9D%EC%B2%B4--%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%84%9C--public-ip-%EB%85%B8%EC%B6%9C%EC%8B%9C%ED%82%A4%EA%B8%B0&quot; aria-label=&quot;서비스 객체  로드밸런서  public ip 노출시키기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서비스 객체 : 로드밸런서 &amp;#x26; Public IP 노출시키기&lt;/h2&gt;
&lt;p&gt;위 과정까지 따라했다면 클러스터에 워커노드가 생성되고, 그 안에 파드가 생성되며 또 그 안에는 애플리케이션이 도커 컨테이너에 의해 격리된 프로세스로써 실행되고 있을겁니다. 그렇다면 실행중인 여러 파드에는 어떻게 접근할 수 있을까요?&lt;/p&gt;
&lt;h3 id=&quot;특정-파드를-어떻게-외부에서-접근하지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B9%EC%A0%95-%ED%8C%8C%EB%93%9C%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%99%B8%EB%B6%80%EC%97%90%EC%84%9C-%EC%A0%91%EA%B7%BC%ED%95%98%EC%A7%80&quot; aria-label=&quot;특정 파드를 어떻게 외부에서 접근하지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;특정 파드를 어떻게 외부에서 접근하지?&lt;/h3&gt;
&lt;h4&gt;그냥 서비스(Cluster IP)를 생성해버린다면?&lt;/h4&gt;
&lt;p&gt;각 파드는 자체 IP 주소를 가지고 있지만, 이 주소는 클러스터 내부에 있으며 외부에서 접근이 불가능합니다. 외부에서 파드에 접근할 수 있게하려면 서비스 오브젝트를 통해 노출해야합니다. 하지만 서비스 오브젝트로 일반적인 서비스(Cluster IP) 를 생성해버리면 이것은 클러스터 내부에서만 접근 가능하기 때문에 특별한 서비스 유형인 &lt;code class=&quot;language-text&quot;&gt;로드밸런서&lt;/code&gt; 를 생성해야합니다.&lt;/p&gt;
&lt;h4&gt;로드밸런서로 퍼블릭 IP 를 유출시키자.&lt;/h4&gt;
&lt;p&gt;외부에서도 access 가능하도록 로드밸런서 유형의 서비스를 생성하고, 퍼블릭 IP 를 유출시켜서 파드에 연결합시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;kubectl expose deployment kubia &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LoadBalancer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;port &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;기존에 &lt;code class=&quot;language-text&quot;&gt;create&lt;/code&gt; 명령어로 deployment 배포 객체를 생성했다면, 위와 같이
&lt;code class=&quot;language-text&quot;&gt;expose&lt;/code&gt; 은 서비스 객체를 생성하는 것입니다. 또 서비스 객체의 타입을 로드밸런서로 명시해준 것이죠.&lt;/p&gt;
&lt;h3 id=&quot;서비스-조회하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%A1%B0%ED%9A%8C%ED%95%98%EA%B8%B0&quot; aria-label=&quot;서비스 조회하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서비스 조회하기&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;kubectl get svc
&lt;span class=&quot;token comment&quot;&gt;// 또는&lt;/span&gt;
kubectl get services&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞서 생성한 서비스 객체의 상세내역을 조회할떄 사용하먄됩니다. 아래와 같이 서비스의 타입과 클러스터 내부에서 접근할때 활용가능한 Cluster IP, 그리고 외부에 유출된 Exteranl IP (퍼블릭 IP) 를 조회 가능하죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;NAME&lt;/span&gt;         &lt;span class=&quot;token constant&quot;&gt;TYPE&lt;/span&gt;          &lt;span class=&quot;token constant&quot;&gt;CLUSTER&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IP&lt;/span&gt;     &lt;span class=&quot;token constant&quot;&gt;EXTERNAL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IP&lt;/span&gt;   &lt;span class=&quot;token function&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;         &lt;span class=&quot;token constant&quot;&gt;AGE&lt;/span&gt;
kubernetes   &lt;span class=&quot;token class-name&quot;&gt;ClusterIP&lt;/span&gt;     &lt;span class=&quot;token number&quot;&gt;11.11&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;    &lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;none&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;        &lt;span class=&quot;token number&quot;&gt;443&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TCP&lt;/span&gt;         &lt;span class=&quot;token number&quot;&gt;34&lt;/span&gt;m
kudia        &lt;span class=&quot;token class-name&quot;&gt;LoadBalancer&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;2.22&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.22&lt;/span&gt;   &lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;pending&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;     &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;30838&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TCP&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;s
복사&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;로드밸런서-서비스-제공받는-메커니즘&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%84%9C-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%A0%9C%EA%B3%B5%EB%B0%9B%EB%8A%94-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98&quot; aria-label=&quot;로드밸런서 서비스 제공받는 메커니즘 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로드밸런서 서비스 제공받는 메커니즘&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/78859bcb-c66b-47d3-b386-70103e240f5c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이때 유의할점은 로드밸런서 타입의 서비스 객체가 생성되지만, 로드 밸런서 자체를 제공하지 않습니다. 클러스터가 클라우드에 배포된 경우 쿠버네티스는 &lt;strong&gt;클라우드 인프라한테 로드밸런서를 제공해달라하고, 트래픽을 클러스터로 전달하도록 요청하는 것&lt;/strong&gt;입니다. 만약 get 명령어로 서비스를 조회했을 때 External IP 가 &amp;#x3C; pending &gt; 상태라는 것은, 아직 인프라로 부터 로드밸런서를 제공받지 못해서 기다리고 있는 상태라는 뜻이 되겠죠.&lt;/p&gt;
&lt;h3 id=&quot;핵심-명령어-2가지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B5%EC%8B%AC-%EB%AA%85%EB%A0%B9%EC%96%B4-2%EA%B0%80%EC%A7%80&quot; aria-label=&quot;핵심 명령어 2가지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;핵심 명령어 2가지&lt;/h3&gt;
&lt;p&gt;지금까지 진행한 과정중 핵심적인 명령어를 2개만 정리해보자면 다음과 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;kubectl create deployment : depolyment 컨트롤러 생성 및 컨테이너 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;kubectl expose deployment : 로드밸런서 &amp;#x26; External IP 유출&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;minikube-에서-로드밸런서-ip-를-어떻게-유출시키는거야-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#minikube-%EC%97%90%EC%84%9C-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%84%9C-ip-%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%9C%A0%EC%B6%9C%EC%8B%9C%ED%82%A4%EB%8A%94%EA%B1%B0%EC%95%BC-&quot; aria-label=&quot;minikube 에서 로드밸런서 ip 를 어떻게 유출시키는거야  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Minikube 에서 로드밸런서 IP 를 어떻게 유출시키는거야 ?!&lt;/h3&gt;
&lt;p&gt;또 로드밸런서와 관련해 한가지 더 말씀드리면, Minikube 로컬에서 클러스터를 구성한경우 LoadBalancer 타입의 서비스 자체가 존재하지 않아서, 외부 IP 를 유출시키는 것이 불가능합니다.
따라서 MetaLIB 를 활용하면 해결되기 때문에 아예 방법이 없는것은 아니지만, 가급적이면 AWS, GLB 와 같은 클라우드 서비스 자체에서 제공해주는 로드밸런서 기능을 활용합시다. 즉 클라우드 인프라에 Minikube 에 기반한 클러스터를 구축하는 것이 좋다는 것이죠.&lt;/p&gt;
&lt;p&gt;또한 계속 로드밸런서 서비스 객체가 계속 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;Pending&gt;&lt;/code&gt; 상태에 빠질 수 있는데, 이 경우 minikube 가 제공하는 ssh 터널링 기능을 사용하면 외부 IP 가 정상적으로 할당 및 유출됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;minikube tunnel&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 터미널에 접속한후 새로운 터미널 창을 실행시켜서 인프라 서버에 접속후, minikube 를 실행시킨 유저 계정으로 로그인하시면 외부 IP 가 아래처럼 할당되는 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/11771e80-0c56-4022-9710-e81646eb56b5/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;애플리케이션-수평확장scale-out&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%88%98%ED%8F%89%ED%99%95%EC%9E%A5scale-out&quot; aria-label=&quot;애플리케이션 수평확장scale out permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;애플리케이션 수평확장(Scale Out)&lt;/h2&gt;
&lt;p&gt;마지막으로 수평 확장을 시켜봅시다. 기존 클러스터에 존재하는 파드(Pod)를 조회해보면 1개가 존재합니다. 하지만 파드 1개로는 대규모 트래픽을 감담하기 힘들며, 트래픽을 분산하고 감당하도록 추가 인스턴스(파드)를 실행해야합니다. 이를 수평확장(Scale Out) 이라고 하죠.&lt;/p&gt;
&lt;h3 id=&quot;파드-개수-확장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EB%93%9C-%EA%B0%9C%EC%88%98-%ED%99%95%EC%9E%A5&quot; aria-label=&quot;파드 개수 확장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파드 개수 확장&lt;/h3&gt;
&lt;p&gt;아무 설정이 없다면, 기본적으로 애플리케이션의 단일 인스턴스(파드) 를 실행합니다. 추가 확장을 원하면 아래처럼 배포 객체를 확장하면 끝입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl scale deployment kiada &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;replicas&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와같이 명령어 딱 1줄로 파드의 복사본을 5개 실행하도록 지정했습니다. 각 파드에는 단일 컨테이너가 할당 및 실행되고 있는 상태가됩니다.
이렇듯 언제든 확장이 필요할때 새로운 복제본을 수동으로 설치하고 실행하지 않더라도 명령어 하나로 파드를 추가할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;파드-조회-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EB%93%9C-%EC%A1%B0%ED%9A%8C-1&quot; aria-label=&quot;파드 조회 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파드 조회&lt;/h3&gt;
&lt;p&gt;앞서 확장시킨 파드들을 모두 조회해봅시다. 우선 &lt;code class=&quot;language-text&quot;&gt;deploy&lt;/code&gt; 를 사용시 파드가 READY 상태에 있는 파드가 5개 존재합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ kubectl get deploy&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/457905dc-1821-4073-95d3-1b092655937f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;동일한-워커-노드에서-실행되고-있을까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%9D%BC%ED%95%9C-%EC%9B%8C%EC%BB%A4-%EB%85%B8%EB%93%9C%EC%97%90%EC%84%9C-%EC%8B%A4%ED%96%89%EB%90%98%EA%B3%A0-%EC%9E%88%EC%9D%84%EA%B9%8C&quot; aria-label=&quot;동일한 워커 노드에서 실행되고 있을까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동일한 워커 노드에서 실행되고 있을까?&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl get pods &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;o wide&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또한 단일 노드 클러스터를 사용하느 경우, 모든 파드가 동일한 노드에서 실행됩니다. 그러나 다중 노드 클러스터에서는 5개의 파드가 모두 클러스터의 노드들에 골구로 분산되어야합니다. 이를 위해 자세한 파드 목록을 조회해봅시다.&lt;/p&gt;
&lt;p&gt;우선 저희는 Minikube 에 기반한 단일 노드 클러스터를 구축했기 떄문에 5개의 파드는 모두 동일한 노드에 존재하는 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6f735f0f-5e33-44c9-b25e-ad68ca28bbfc/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;만약 다중 노드 클러스터였다면 아래와 같은 모습이였겠죠?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ kubectl get pods &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;o wide
&lt;span class=&quot;token constant&quot;&gt;NAME&lt;/span&gt;                   &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;  &lt;span class=&quot;token constant&quot;&gt;IP&lt;/span&gt;          &lt;span class=&quot;token constant&quot;&gt;NODE&lt;/span&gt;
kiada&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9d&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;785&lt;/span&gt;b578&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;58&lt;/span&gt;vhc  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;10.244&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.5&lt;/span&gt;  kind&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;worker
kiada&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9d&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;785&lt;/span&gt;b578&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;jmnj8  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;10.244&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.4&lt;/span&gt;  kind&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;worker2
kiada&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9d&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;785&lt;/span&gt;b578&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p449x  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;10.244&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.3&lt;/span&gt;  kind&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;worker2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;클라이언트의-요청은-여러-파드에-어떻게-분산될까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%EC%9D%98-%EC%9A%94%EC%B2%AD%EC%9D%80-%EC%97%AC%EB%9F%AC-%ED%8C%8C%EB%93%9C%EC%97%90-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%B6%84%EC%82%B0%EB%90%A0%EA%B9%8C&quot; aria-label=&quot;클라이언트의 요청은 여러 파드에 어떻게 분산될까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클라이언트의 요청은 여러 파드에 어떻게 분산될까?&lt;/h3&gt;
&lt;p&gt;그런데 의문이 생깁니다. 로드밸런서를 구축했을떄 항상 동일한 애플리케이션 인스턴스를 호출할까요?&lt;/p&gt;
&lt;p&gt;여러 요청들은 무작위로 다른 파드에 골구로 분산됩니다. 즉, &lt;strong&gt;하나 이상의 파드가 서비스 뒤에 존재할때, 서비스는 다수의 파드 앞에서 로드밸런서 역할을 적절히 수행&lt;/strong&gt;하죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;파드와-노드의-관계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EB%93%9C%EC%99%80-%EB%85%B8%EB%93%9C%EC%9D%98-%EA%B4%80%EA%B3%84&quot; aria-label=&quot;파드와 노드의 관계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파드와 노드의 관계&lt;/h2&gt;
&lt;p&gt;또 쿠버네티스에서는 파드가 적절히 실행하는데 필요한 CPU 와 메모리를 제공하는 노드에 스캐줄링됐다면, 어떤 노드에 파드가 실행 중인지는 중요하지 않습니다.&lt;/p&gt;
&lt;p&gt;파드가 스케줄링된 노드와 상관없이 컨테이너 내부에 실행중인 모든 애플리케이션은 동일한 유형의 운영체제 환경을 가집니다. 각 파드는 자체 IP 를 가지고, 다른 특정 파드가 같은 노드에 위치해있는지 다른 노드에 위치했는지 신경쓸 필요도 없습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;minikube-대시보드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#minikube-%EB%8C%80%EC%8B%9C%EB%B3%B4%EB%93%9C&quot; aria-label=&quot;minikube 대시보드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Minikube 대시보드&lt;/h2&gt;
&lt;p&gt;마지막으로 Minikube 를 활용시 쿠버네티스 클러스터를 활용중이라면 대시보드를 활용해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;$ minikube dashboard&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.manning.com/books/kubernetes-in-action-second-edition&quot;&gt;Kubernetes in Action, Second Edition&lt;/a&gt;
&lt;a href=&quot;https://wiki.terzeron.com/ko/OS_%EC%9D%BC%EB%B0%98_%EC%8B%9C%EC%8A%A4%ED%85%9C/Docker/minikube%EC%99%80_kubectl%EB%A1%9C_%EA%B0%84%EB%8B%A8%ED%95%9C_%EC%98%88%EC%A0%9C_%EC%84%9C%EB%B9%84%EC%8A%A4_%EB%B0%B0%ED%8F%AC%ED%95%B4%EC%84%9C_%EC%8B%A4%ED%96%89%ED%95%B4%EB%B3%B4%EA%B8%B0&quot;&gt;minikube와 kubectl로 간단한 예제 서비스 배포해서 실행해보기&lt;/a&gt;
&lt;a href=&quot;https://velog.io/@roon-replica/k8s-2-b.-k8s-%EC%8B%A4%EC%8A%B5&quot;&gt;k8s - 2-b. k8s 실습&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[멀티 모듈이란 무엇이고, 왜 사용하는걸까?]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/architecture/multi-module/</link><guid isPermaLink="false">https://haon.site/haon/architecture/multi-module/</guid><pubDate>Mon, 13 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;p&gt;최근 진행하는 프로젝트가 &lt;code class=&quot;language-text&quot;&gt;Hexagonal Architecture(헥사고날 아키텍처)&lt;/code&gt; 에 기반하여 진행하게 되면서, 멀티 모듈에 대한 지식을 뺴놓을 수 없게 되었습니다. 멀티모듈이 무엇인지 먼저 학습을 진행하고자 이렇게 포스팅을 진행합니다. 헥사고날과 관련한 내용은 추후 학습을 하며 포스팅을 올릴 예정이고, 이번 포스팅에서는 멀티 모듈의 컨셉에 대해서만 다루어보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;멀티-모듈multi-module&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%80%ED%8B%B0-%EB%AA%A8%EB%93%88multi-module&quot; aria-label=&quot;멀티 모듈multi module permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;멀티 모듈(Multi Module)&lt;/h2&gt;
&lt;p&gt;멀티모듈이란 &lt;strong&gt;하나의 단일 프로젝트를 여러개의 모듈로 분리해서 구성&lt;/strong&gt;하는 기법입니다. 각 모듈은 개별적으로 컴파일되어 라이브러리 또는 실행가능한 파일로 생성됩니다.&lt;/p&gt;
&lt;h3 id=&quot;모듈module-이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A8%EB%93%88module-%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;모듈module 이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;모듈(Module) 이란?&lt;/h3&gt;
&lt;p&gt;오라클 자바 도큐먼트에서는, 모듈을 패키지의 한 단계 위의 집합체이며, 관련된 패키지와 리소스들의 재사용할 수 있는 그룹이라고 정의하고 있습니다.
이렇게만 말해선 이해가 잘 안가실겁니다. 지금부터 멀티모듈이 왜 등장했으며, 등장배경과 함께 왜 사용해야하는지를 알아봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;단일모듈-멀티-프로젝트--사설-레포지토리-공유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%9D%BC%EB%AA%A8%EB%93%88-%EB%A9%80%ED%8B%B0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8--%EC%82%AC%EC%84%A4-%EB%A0%88%ED%8F%AC%EC%A7%80%ED%86%A0%EB%A6%AC-%EA%B3%B5%EC%9C%A0&quot; aria-label=&quot;단일모듈 멀티 프로젝트  사설 레포지토리 공유 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단일모듈 멀티 프로젝트 &amp;#x26; 사설 레포지토리 공유&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/28c4ad24-d12b-47d6-afce-27d74f966ca8/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;멀티 모듈의 개념이 적극적으로 활성되기전에, 기존에는 여러 프로젝트에서 공통되는 코드와 Dto, 도메인등을 &lt;code class=&quot;language-text&quot;&gt;Nexus&lt;/code&gt; 와 같은 하나의 사설 Maven 레포지토리에 올려둠으로써 &lt;strong&gt;시스템의 일관성이 보장되는 것 처럼 보이게 했습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;중복되는 코드와 기능에 대한 관리 주체가 기존에는 인간이 직접 일일이 개입해야 했다면, 이로써 주체자가 시스템으로 바뀌게됩니다. 이 방식을 통해 위와 같이 주문 관련 &lt;strong&gt;다중 프로젝트 OrderA, OrderB, OrderC 는 하나의 사설 저장소로 공통된 내용을 공유&lt;/strong&gt;함으로써 흔히 말하는 &lt;code class=&quot;language-text&quot;&gt;복.붙&lt;/code&gt; 의 과정을 제거할 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;하지만 &lt;code class=&quot;language-text&quot;&gt;Nexus&lt;/code&gt; 등을 활용하는 방식은 변경사항이 일어날때마다 코드를 업로드시키고, 그 Nexus 에 최신회된 내용을 다운받는 &lt;strong&gt;개발 사이클이 너무 번거롭다는 단점&lt;/strong&gt;이 있었습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;단일-프로젝트에서-멀티모듈-도입&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%9D%BC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-%EB%A9%80%ED%8B%B0%EB%AA%A8%EB%93%88-%EB%8F%84%EC%9E%85&quot; aria-label=&quot;단일 프로젝트에서 멀티모듈 도입 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단일 프로젝트에서 멀티모듈 도입&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/30c38753-5db3-4225-8918-d31211c31d9b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;앞선 방식은 여러개의 프로젝트가 하나의 저장소를 공유하는 방식이였다면, 이러한 다중 프로젝트를 하나의 자그마한 &quot;모듈&quot; 단위로 쪼개자는 아이디어가 등장하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;멀티모듈이란 여러 프로젝트 각각을 하나의 모듈(서브 프로젝트) 로 모듈화 시키는 방식&lt;/strong&gt;입니다. 결국 기존의 여러 프로젝트들은 모듈화되었고, 여러 멀티모듈들이 모여서 하나의 단일 프로젝트를 구성하게 되는 구조입니다. 즉, 하나의 프로젝트 내에 여러 모듈을 설치해서, 시스템의 일관성을 보장하고 빠른 개발 사이클도 가져가는 방식이죠.&lt;/p&gt;
&lt;p&gt;위 그림처럼 기존의 OrderA, OrderB, OrderC, Order 프로젝트는 각각 모듈화 되었으며, 이들이 하나의 프로젝트를 구성하게 되는 것입니다. 그런데 실제 운영 서비스라면 한 서비스에는 주문 관련 기능외에도, 결제 기능, 쿠폰 기능등 여러 기능이 추가되어서 여러 집군들이 하나의 서비스로써 구성될 수 있겠죠? 이때 바로 &lt;code class=&quot;language-text&quot;&gt;MSA&lt;/code&gt; 가 등장하게 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;monolithic-architecture--msa&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#monolithic-architecture--msa&quot; aria-label=&quot;monolithic architecture  msa permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Monolithic Architecture &amp;#x26; MSA&lt;/h2&gt;
&lt;h3 id=&quot;monolithic&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#monolithic&quot; aria-label=&quot;monolithic permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Monolithic&lt;/h3&gt;
&lt;p&gt;기존의 여러 서비스들은 &lt;code class=&quot;language-text&quot;&gt;Monolthic(모놀리틱)&lt;/code&gt; 아키텍처 방식으로써, 멀티 프로젝트에서 단일 모듈을 사용하는 방식을 채택했습니다. 하나의 프로젝트안에 API 및 웹서버, 캐시와 DB 등을 모두 집어넣는 방식이였죠. 하지만 그 규모가 커지고, &lt;code class=&quot;language-text&quot;&gt;MSA&lt;/code&gt; 로 전환되면서 각 서비스들을 마이크로서비스 단위로 쪼개는 방식으로 진화하게 되었습니다.&lt;/p&gt;
&lt;h3 id=&quot;msa-micro-service-architecture&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#msa-micro-service-architecture&quot; aria-label=&quot;msa micro service architecture permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MSA (Micro Service Architecture)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1154bd32-620c-499c-a4ef-40a8256e7718/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;MSA 를 채택한 대규모 서비스에서 시스템을 분리했다가 통합하는 일들이 많아지면서, 멀티모듈을 활용하면서 분리와 통합을 더 유연하게 만들 수 있기 때문에 MSA 에서 멀티모듈이 많이 활용되고 있습니다.&lt;/p&gt;
&lt;p&gt;MSA 의 각 구성단위인 마이크로서비스는 하나의 독립적인 기능을 수행하는것이 중요합니다. &lt;strong&gt;멀티모듈을 도입시, 각 마이크로서비스에 멀티 모듈로 구성하는 방식입니다.&lt;/strong&gt; 예를 들어, 주문 관리 서비스는 주문, 결제, 배송 등 다양한 기능을 제공할 수 있으므로 각각의 기능에 해당하는 모듈로 분리할 수 있습니다.&lt;/p&gt;
&lt;p&gt;앞서 설명했듯이, 멀티모듈은 하나의 프로젝트 안에서 여러 개의 모듈로 구성되어 있는 구조입니다. 각각의 모듈은 독립적으로 개발, 빌드, 배포가 가능하며, 다른 모듈과의 의존성을 정의할 수 있습니다. 이를 통해, 마이크로서비스 간의 의존성을 적절하게 관리할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;msa-에서-멀티모듈을-어디에-도입한다는-거야-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#msa-%EC%97%90%EC%84%9C-%EB%A9%80%ED%8B%B0%EB%AA%A8%EB%93%88%EC%9D%84-%EC%96%B4%EB%94%94%EC%97%90-%EB%8F%84%EC%9E%85%ED%95%9C%EB%8B%A4%EB%8A%94-%EA%B1%B0%EC%95%BC-&quot; aria-label=&quot;msa 에서 멀티모듈을 어디에 도입한다는 거야  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MSA 에서 멀티모듈을 어디에 도입한다는 거야 !?&lt;/h3&gt;
&lt;p&gt;멀티모듈과 MSA 를 처음 학습하며 개인적으로 햇갈렸던 점은, MSA 에 기반한 아키텍처를 구성시 멀티모듈에 어디에, 어떤 범위에 적용되는가입니다. 지금까지 이해한바로는, 보통 MSA 에서의 멀티모듈 도입시 &lt;code class=&quot;language-text&quot;&gt;각각의 마이크로서비스가 멀티 모듈로 구성됨을 의미합니다.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;앞서 설명했듯이 하나의 프로젝트 및 서비스를 멀티모듈로 구성하면, 각 서비스(정확히는 여기선 마이크로서비스) 끼리는 서로 의존적이지 않고 독립적으로 개발 및 배포가 가능해지는 것이죠. 또한 각 서비스의 모듈끼리도 서로 다른 의존성을 지니며, 독립적으로 빌드 및 테스트, 배포가 가능해지는 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;객체지향으로-귀결되는-멀티모듈&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9C%BC%EB%A1%9C-%EA%B7%80%EA%B2%B0%EB%90%98%EB%8A%94-%EB%A9%80%ED%8B%B0%EB%AA%A8%EB%93%88&quot; aria-label=&quot;객체지향으로 귀결되는 멀티모듈 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;객체지향으로 귀결되는 멀티모듈&lt;/h2&gt;
&lt;p&gt;멀티모듈 프로젝트를 구성하는 방식은 개발자 나름이겠지만, 보통 계층 구조로 모듈들이 구성되는 것이 일반적입니다. 이는 각 모듈의 &lt;code class=&quot;language-text&quot;&gt;역할과 책임&lt;/code&gt; 분리하고, 모듈간의 의존성을 최소화하여 유연하고 확장성있는 애플리케이션을 개발하기 위함입니다.&lt;/p&gt;
&lt;p&gt;결국 모듈의 정의는 &lt;code class=&quot;language-text&quot;&gt;객체지향&lt;/code&gt;으로 귀결되는듯 했습니다. 멀티모듈에서의 각 모듈은 &lt;code class=&quot;language-text&quot;&gt;역할과 책임&lt;/code&gt; 이 명확히 부여되어야지 적절한 계층에 배치되고, 각 계층간의 의존적인 흐름이 생기게 되는 것입니다. &lt;a href=&quot;https://velog.io/@msung99/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%84-%EC%95%84%EB%8A%94%EC%B2%99%ED%95%98%EC%A7%80-%EB%A7%90%EC%9E%90-%EC%9A%B0%EB%A6%AC%EA%B0%80-%EC%98%A4%ED%95%B4%ED%95%98%EA%B3%A0-%EC%9E%88%EC%97%88%EB%8D%98-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%97%90-%EB%8C%80%ED%95%B4&quot;&gt;객체지향을 아는척하지 말자 : 오해하고 있었던 객체지향의 정체&lt;/a&gt; 에서도 설명했듯이, 모듈도 하나의 객체로써 바라보고 서로 협력하는 관계로 바라보면 좋을듯합니다.&lt;/p&gt;
&lt;h3 id=&quot;객체지향을-살리지못한-실패한-멀티모듈-프로젝트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%84-%EC%82%B4%EB%A6%AC%EC%A7%80%EB%AA%BB%ED%95%9C-%EC%8B%A4%ED%8C%A8%ED%95%9C-%EB%A9%80%ED%8B%B0%EB%AA%A8%EB%93%88-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8&quot; aria-label=&quot;객체지향을 살리지못한 실패한 멀티모듈 프로젝트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;객체지향을 살리지못한, 실패한 멀티모듈 프로젝트&lt;/h3&gt;
&lt;p&gt;만약 객체지향을 살라지 못한 멀티모듈 프로젝트라면 어떻게 될까요? 우선 &lt;code class=&quot;language-text&quot;&gt;1. 스파케티 코드&lt;/code&gt; 가 발생할 수 있습니다. 하위 계층의 모듈이 상위 계층의 모듈을 공통 모듈로써 바라보고 있을텐데, 각 모듈에 대한 역할과 책임이 적절히 분배되지 않았으므로 애매모호한 모듈의 정의로 인해 공통모듈에다 필요 이상의 기능을 추가해버리는 것입니다. 결국 해당 공통모듈에 의존적인 상황이 발생해버리는 것이죠.&lt;/p&gt;
&lt;p&gt;이렇게 되면 공통모듈의 수많은 클래스가 생성될 것이고, 각 클래스끼리는 서로 의존하고 영켜있는 스파게티 코드가 발생해버리는 것입니다. 가령 클래스A를 수정하려해도, A를 의존하고 있는 몇십개의 클래스를 변경해야하는 번거로움이 생길 수 있게 되는것입니다.&lt;/p&gt;
&lt;p&gt;다음으로 &lt;code class=&quot;language-text&quot;&gt;2.대규모의 의존성 덩어리가 발생&lt;/code&gt; 할수도 있습니다. 공통모듈에 모든 의존성을 주입하는경우, 하위 모듈들중에 사용하지 않는것에 대한 의존성이 생길수도 있는것입니다. 각 모듈에 대해 의존성의 무개가 커지고, 추후 잡을수없는 에러가 터질 수도 있는것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;더-학습해볼-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%ED%95%99%EC%8A%B5%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;더 학습해볼 키워드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 학습해볼 키워드&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;DDD(도메인 주도 설계)&lt;/li&gt;
&lt;li&gt;클린 아키텍처&lt;/li&gt;
&lt;li&gt;Hexagonal Architecture&lt;/li&gt;
&lt;li&gt;스프링부트 애플리케이션에서의 gradle 및 멀티모듈 적용법&lt;/li&gt;
&lt;li&gt;MSA, Monolithic&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=nH382BcycHc&amp;#x26;t=1800s&quot;&gt;[우아한테크세미나] 190829 우아한멀티모듈 by 우아한형제들 권용근님&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://techblog.woowahan.com/2637/&quot;&gt;멀티모듈 설계 이야기 with Spring, Gradle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@soyeon207/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EB%A9%80%ED%8B%B0-%EB%AA%A8%EB%93%88-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0#%EF%B8%8F-%EB%A9%80%ED%8B%B0-%EB%AA%A8%EB%93%88-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EA%B0%80-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B4%EC%9C%A0&quot;&gt;[Spring] 멀티 모듈 프로젝트 만들기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@tritny6516/Spring-Multi-Module&quot;&gt;Spring Multi Module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-09-06-multi-module/&quot;&gt;멀티 모듈 적용하기 with Gradle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[자바의 동시성 제어 키워드 : volatile 과 synchornized]]></title><description><![CDATA[volatile 과 가시성 volatile 키워드를 사용하면 멀티 쓰레드 환경에서 모든 쓰레드들이 CPU 메인 메모리의 동일한 변수를 공유할 수 있게됩니다. CPU Cache 가시성과 volatile 를 이해하기 위해선 CPU Cache…]]></description><link>https://haon.site/haon/java/concurrency-keyword/</link><guid isPermaLink="false">https://haon.site/haon/java/concurrency-keyword/</guid><pubDate>Mon, 13 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;volatile-과-가시성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#volatile-%EA%B3%BC-%EA%B0%80%EC%8B%9C%EC%84%B1&quot; aria-label=&quot;volatile 과 가시성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;volatile 과 가시성&lt;/h2&gt;
&lt;p&gt;volatile 키워드를 사용하면 멀티 쓰레드 환경에서 &lt;strong&gt;모든 쓰레드들이 CPU 메인 메모리의 동일한 변수를 공유&lt;/strong&gt;할 수 있게됩니다.&lt;/p&gt;
&lt;h3 id=&quot;cpu-cache&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cpu-cache&quot; aria-label=&quot;cpu cache permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CPU Cache&lt;/h3&gt;
&lt;p&gt;가시성과 volatile 를 이해하기 위해선 CPU Cache 에 대해 먼저 이해해야합니다. 이를 위해, 멀티코어 CPU 환경에섯 저희는 공유자원 count 를 동시에 2개의 쓰레드가 변경하는 경우에 대해 생각해볼 겁니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/9011092c-c59f-4be2-82ee-41c9ca939017/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;우선 쓰레드를 실행하는건 CPU가 실행합니다. 쓰레드를 CPU가 실행할 때 메인 메모리에서 변수 값을 읽어오는데, 메인 메모리와 한 CPU 사이의 거리가 멀어서 매번 리소스(공유자원)을 획득하려고 접근하는건은 번거롭습니다. 따라서 &lt;strong&gt;CPU 내부에 CPU Cache 를 생성하고 공유자원을 카피해서 캐싱해놓는 방식&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/75eb5816-cdb1-435a-bc8d-62434d37f1b6/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;더 자세히 풀어써보면, 쓰레드1 을 실행할 CPU 는 쓰레드를 실행할때 필요한 값을 메인메모리에서 카피해와서 CPU 캐시에 담아두고, 쓰레드 1에게 CPU 캐시에 카피해둔 자원에 대한 연산을 진행하도록 지시합니다. 그러고 CPU1 의 Cache에 카피해둔 자원에 대해 모든 연산을 반영한 다음, 캐시에 최신화된 값을 메인 메모리에도 반영시켜줍니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0ded1c8f-b7ee-4337-8203-57ab7b34c413/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그런데 여기서 문제가 발생할 수 있습니다. 만약 CPU Cache1 이 최신화한 공유자원 값(count = 3)을 메인메모리에도 반영하기 이전에 CPU Cache2가 구버전 값(count = 0) 을 읽어올 수도 있습니다. 즉, CPU2에 대한 쓰레드인 Thread2 는 최신화되기 이전에 구버전 count 값을 기반으로 연산을 진행할 수도 있는 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;volatile&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#volatile&quot; aria-label=&quot;volatile permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;volatile&lt;/h3&gt;
&lt;p&gt;volatile 키워드를 사용해서 CPU 메모리 영역에 캐싱된 값이 아니라 항상 최신의 값을 가지도록 메인 메모리 영역에서 값을 참조하도록 할 수 있습니다. 즉, 각 CPU 의 쓰레드가 가진 CPU Cache 에다 캐싱을 하는것이 아니라, &lt;strong&gt;모든 쓰레드가 공유하는 메인 메모리에서 읽고 쓰는 연산을 진행하도록 하는 방법&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyClass&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 이렇게 volatile 키워드를 공유변수에&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 사용하면 멀티 쓰레드 환경에서 메인메모리의 변수를 공유할 수 있습니다.&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Mytest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 	  &lt;span class=&quot;token comment&quot;&gt;// ... (비즈니스 로직)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;volatile-은-언제쓸까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#volatile-%EC%9D%80-%EC%96%B8%EC%A0%9C%EC%93%B8%EA%B9%8C&quot; aria-label=&quot;volatile 은 언제쓸까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;volatile 은 언제쓸까?&lt;/h3&gt;
&lt;p&gt;앞서 말했듯이, volatile 키워드로 변수를 선언해서 여러개의 쓰레드에서 공유할 수 있도록할 때 사용하면 됩니다. 그러나 주의할점은, Volatile 은 Write 를 하나의 쓰레드에서만 할때 유용하고 &lt;strong&gt;여러 쓰레드에서 Write 한다면 부적절합니다.&lt;/strong&gt; 지난번에 살펴봤던 경쟁상태인 Read-Modify-Write 패턴이 발생하 수 있기 때문이죠.
여러개의 변수가 동시에 읽고 그 시점을 기준으로 데이터를 변경할 때 혼란이 생기기 때문입니다.&lt;/p&gt;
&lt;p&gt;아래에서 설명드릴 synchronized 와 비교해볼 경우 동기화 이슈로 인해 volatile 대신에 synchronized 키워드를 사용하면 되지 않나? 라는 생각이 들수 있습니다. 하지만 synchronized 를 사용할경우 대규모 서비스에서 심각한 성능저하가 생긴다는 장점이 생기므로, 상황을 적절히 고려하여 어떤걸 사용할지 결정해야합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;synchronized&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#synchronized&quot; aria-label=&quot;synchronized permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;synchronized&lt;/h2&gt;
&lt;p&gt;synchronized 를 사용하면 값을 변경하기 위해 읽고-저장하는 연산(비-원자적 연산)은 &lt;strong&gt;동시간에 하나의 쓰레드만 처리할 수 있도록 일종의 락을 거는 방법&lt;/strong&gt;입니다.
경쟁상태를 방지하는 가장 안전한 방벙이지만, 앞서 설명했듯이 서비스에 트래픽이 발생할 경우 심각한 성능 저하가 발생할 수 있다는점에 유의합시다.&lt;/p&gt;
&lt;p&gt;synchronized 키워드의 경우 &lt;strong&gt;synchronized 블록에 진입하기 전에 CPU 캐시 메모리와 메인 메모리 값을 동기화하여 가시성을 해결&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	number&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;배타적-실행동기화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%ED%83%80%EC%A0%81-%EC%8B%A4%ED%96%89%EB%8F%99%EA%B8%B0%ED%99%94&quot; aria-label=&quot;배타적 실행동기화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배타적 실행(동기화)&lt;/h3&gt;
&lt;p&gt;이렇듯 synchronized 키워드를 붙여서 동시에 하나의 쓰레드만 진입할 수 있도록 하는 것을 배타적 실행(동기화)라고 합니다.&lt;/p&gt;
&lt;h3 id=&quot;통신-동기화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%86%B5%EC%8B%A0-%EB%8F%99%EA%B8%B0%ED%99%94&quot; aria-label=&quot;통신 동기화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;통신 동기화&lt;/h3&gt;
&lt;p&gt;또 synchronized 는 단일 쓰레드만 진입하도록하는 배타적 실행뿐만 아니라, &lt;strong&gt;메인 메모리에서 가장 최근의 값을 가져오는&lt;/strong&gt; 동기화 가능도 함께 수행합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;volatile-vs-synchronized&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#volatile-vs-synchronized&quot; aria-label=&quot;volatile vs synchronized permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;volatile VS synchronized&lt;/h2&gt;
&lt;p&gt;volatile 만으로 동기화 되는 상황이라면 synchronized 보다는 volatile 만으로 동기화 처리르 하는것이 낫습니다. 배타적 실행을 위해 락을 획득하고 반환하는 비용이 발생하지 않기 때문이죠. 반면 배타적 실행이 필요하다면 synchronized 를 사용하면 되겠죠?&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;atomic-variable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#atomic-variable&quot; aria-label=&quot;atomic variable permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Atomic variable&lt;/h2&gt;
&lt;p&gt;추가적으로 Atomic 변수(AtomicInteger, AtomicLong, AtomicBoolean 등)을 사용하면 &lt;strong&gt;CAS(compare-and-swap)알고리즘을 이용해서 synchronized보다 효율적으로 동시성 및 원자성을 보장&lt;/strong&gt;합니다. 멀티쓰레드에서 write도 가능합니다.&lt;/p&gt;
&lt;p&gt;보통 synchronized 를 사용하여 해당 블럭 전체를 블로킹(blocking) 해버리는데, 이 경우 다른 쓰레드는 아무런 작업을 하지 못하고 기다리는 상황이 발생할 수 있어서 낭비가 심합니다. 따라서 &lt;code class=&quot;language-text&quot;&gt;논블로킹(Non Blocking)&lt;/code&gt; 하면서 동기화 문제를 해결하기 위한 방법이 Atomic 입니다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;CAS 알고리즘&lt;/code&gt;은 쓰레드가 가지고 있던 원래 값이 현재의 값과 같은 지 비교하고, 같으면 그냥 사용하고 다르면 현재의 값을 받아옵니다. 그래서 asynchronized보다 훨씬 작은 범위에 Lock을 걸 수 있게되고, volatile의 문제를 해결할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyClass&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AtomicLong&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AtomicLong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// AtomicLong, AtomicInteger, AtomicBoolean, ...&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			number&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        	&lt;span class=&quot;token comment&quot;&gt;// 또는 number.incrementAndGet();&lt;/span&gt;
    		&lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 위 코드를 동시에 2개의 쓰레드가 실행시킬때, 변수가 atomic 변수가 아닌 일반 정수형 변수 int 였다면 동기화가 지켜지지 않았을겁니다.&lt;/p&gt;
&lt;h3 id=&quot;cas-알고리즘&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cas-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98&quot; aria-label=&quot;cas 알고리즘 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CAS 알고리즘&lt;/h3&gt;
&lt;p&gt;CAS 를 알고리즘은 앞서 살핀 volatile 키워드의 성능을 조금 더 개선시킨 것입니다. volatile 의 문제점은 메인메모리의 저장된 값과 CPU 캐시에 저장된 값이 다른 경우가 발생할 수 있다는 점이죠.
이 방식은 &lt;code class=&quot;language-text&quot;&gt;현재 쓰레드에 저장된 값과 메인 메모리에 저장된 값을 비교&lt;/code&gt;하여 일치하는경우 새로운 값으로 교체되고, 일치하지않는다면 실패하고 무한 루프를 돌면서 일치할 때 까지 재시도합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;atomic-variable-vs-synchornized&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#atomic-variable-vs-synchornized&quot; aria-label=&quot;atomic variable vs synchornized permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;atomic variable VS synchornized&lt;/h2&gt;
&lt;p&gt;atomic 변수가 활용하는 CAS 알고리즘은 &lt;strong&gt;원자성뿐 아니라 가시성 문제도 해결&lt;/strong&gt;해주는 것을 볼 수 있습니다. 또한 non-blocking 이 가능하므로 blocking 방식인 synchornized 보다 성능상 이점이 있죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://steady-coding.tistory.com/568&quot;&gt;[Java] atomic과 CAS 알고리즘&lt;/a&gt;
&lt;a href=&quot;https://jronin.tistory.com/110&quot;&gt;자바 동기화 처리 - volatile 와 synchronized&lt;/a&gt;
&lt;a href=&quot;https://jaehoney.tistory.com/112&quot;&gt;Java - Atomic Variable (+ 동시성 제어 비교 with volatile, synchronized)&lt;/a&gt;
&lt;a href=&quot;https://www.youtube.com/watch?v=ktWcieiNzKs&quot;&gt;[10분 테코톡] 알렉스, 열음의 멀티스레드와 동기화 In Java&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[경쟁 상태의 2가지 패턴. Check-Then-Act, Read-Modify-Write]]></title><description><![CDATA[경쟁상태란? 경쟁상태는 멀티쓰레드 환경에서 여러 쓰레드들이 공유자원(Shared Resource…]]></description><link>https://haon.site/haon/java/race-condition/</link><guid isPermaLink="false">https://haon.site/haon/java/race-condition/</guid><pubDate>Mon, 13 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;경쟁상태란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%BD%EC%9F%81%EC%83%81%ED%83%9C%EB%9E%80&quot; aria-label=&quot;경쟁상태란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;경쟁상태란?&lt;/h2&gt;
&lt;p&gt;경쟁상태는 멀티쓰레드 환경에서 여러 쓰레드들이 공유자원(Shared Resource) 을 병행적으로 읽거나 쓰는 동작을 할때 타이밍이나 접근순서에 따라 실행결과가 달라지는 상황을 의미합니다.&lt;/p&gt;
&lt;h3 id=&quot;경쟁상태의-대표유형&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%BD%EC%9F%81%EC%83%81%ED%83%9C%EC%9D%98-%EB%8C%80%ED%91%9C%EC%9C%A0%ED%98%95&quot; aria-label=&quot;경쟁상태의 대표유형 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;경쟁상태의 대표유형&lt;/h3&gt;
&lt;p&gt;그런데 경쟁상태가 언제, 어떠한 상황에서 발생하는 것인지 잘 정리되지 않아서 이번 기회에 포스팅을 다루면서 학습을 진행해보고자 합니다.&lt;/p&gt;
&lt;p&gt;경쟁상태의 대표적인 상황으로는 Read-Modify-Write 패턴과 Check-then-act 패턴이 있습니다. 이들에 대해 알아봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;read-modify-write-패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#read-modify-write-%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;read modify write 패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Read-Modify-Write 패턴&lt;/h2&gt;
&lt;p&gt;Read-Modify-Write 패턴이란 &lt;strong&gt;이전 상태를 기준으로 객체의 현재 상태를 변경하면서 발생하는 문제&lt;/strong&gt;를 뜻합니다.&lt;/p&gt;
&lt;p&gt;count라는 공유자원이 있습니다. 이 공유자원에 대한 연산인 count++; 연산의 경우 코드상에서는 딱 1줄만 작성되어 있지만, 사실은 3번의 연산이 일어나는 것입니다. 1) count 변수에 있는 값을 읽어오고, 2) 변수에 있는 값을 수정하고, 3) 그 변수에 있는 값을 덮어쓰는 3번의 연산이 일어나게 되는 것이죠.&lt;/p&gt;
&lt;p&gt;쓰레드 1이 count 값 100 에서 101로 증가시키고 저장하기 직전에, 쓰레드2가 증가되기 직전의 값인 100을 읽어와서 증가시킨다면 count 의 결과값이 102가 아닌 101이 되므로 경쟁상태가 발생한 것을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;즉, &lt;strong&gt;count++; 연산은 원자성(atomic) 이 보장되지 않은 연산입니다.&lt;/strong&gt; 위 3개의 순차적인 연산 사이에서 다른 쓰레드가 중간에 개입해버리는 경우 기대하지 못한 결과가 발생하는 것이 바로 Read-Modify-Write 연산입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;check-then-act-패턴&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#check-then-act-%ED%8C%A8%ED%84%B4&quot; aria-label=&quot;check then act 패턴 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Check-Then-Act 패턴&lt;/h2&gt;
&lt;p&gt;Check-Then-Act 패턴은 &lt;strong&gt;이전에 검증(Check) 한 결과가 행동(Act) 시점에는 더 이상 유효하지 않을때 발생하는 문제&lt;/strong&gt;를 뜻합니다.&lt;/p&gt;
&lt;p&gt;수강신청에 관한 로직이 있다고 해봅시다. 수강신청 인원이 20명 미만일 경우 폐강될수도 있기 때문에, 이와 관련한 알림 메시지 구문을 출력하는 로직입니다.
현 수강신청 인원은 count 변수로 측정하며, if문으로 20명 이하인지 체크하고 인원수에 따라 알림 구문를 보내는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;if문(Check)을 통과하기 전에는 조건에 부합하는 값이지만, if문을 통과한 이후에는 조건에 부합하지 않는 숫자기 되기 때문에 이렇게 경쟁상태가 발생&lt;/strong&gt;하게 되는 것입니다.&lt;/p&gt;
&lt;p&gt;count 변수값이 15인 경우를 생각해봅시다. 이 15라는 값은 20보다 작기 때문에 if문을 통과(Check)하겠죠? 그러고 1 mills 시간동안 해당 쓰레드는 잠시 sleep 모드에 들어가게 됩니다.&lt;/p&gt;
&lt;p&gt;그러고 이제 알림 메시지를 출력하려는데(then Act), 잠시 sleep 모드에 들어간 사이에 그 짧은 시간 찰나에 if문을 통과한 다른 여러 쓰레드들에 의해 count 값이 대폭 증가해있는 상태가 되는것입니다. 그래서 해당 쓰레드는 대폭 증가한 count 값을 출력하게되는 문제가 발생하는 것이죠.&lt;/p&gt;
&lt;p&gt;쉽게말해, Check절 (if문) 조건에 부합하여 통과하게 되었지만, 공유자원(count 변수) 를 천천히 출력해보고자 했으나 짧은 찰나에 조건을 통과한 다른 여러 쓰레드들에 의해 조건에 부합하지 않는 숫자가 출려되는 경쟁상태가 발생하게 되는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;결국 연산사이의 시간텀이 발생하기 때문에 한 쓰레드가 연산을 하고있을때 도중에 다른 쓰레드의 연산이 개입&lt;/strong&gt;할 수 있어서 경쟁조건이 발생하게 되는 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=ktWcieiNzKs&quot;&gt;https://www.youtube.com/watch?v=ktWcieiNzKs&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[톰캣(Tomcat) 의 개념, 요청 처리 내부과정]]></title><description><![CDATA[Tomcat (톰캣) 스프링부트 애플리케이션은 다중 요청을 처리하기 위해 내장 서블릿 컨테이너인 Tomcat(톰캣) 을 이용합니다. Tomcat 은 다중 요청을 처리하기 위해, 부팅시 쓰레드의 집합소라 할 수 있는 쓰레드 풀(Thread Pool…]]></description><link>https://haon.site/haon/spring/tomcat-concept/</link><guid isPermaLink="false">https://haon.site/haon/spring/tomcat-concept/</guid><pubDate>Sun, 12 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;tomcat-톰캣&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tomcat-%ED%86%B0%EC%BA%A3&quot; aria-label=&quot;tomcat 톰캣 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tomcat (톰캣)&lt;/h2&gt;
&lt;p&gt;스프링부트 애플리케이션은 다중 요청을 처리하기 위해 내장 서블릿 컨테이너인 Tomcat(톰캣) 을 이용합니다. Tomcat 은 다중 요청을 처리하기 위해, 부팅시 쓰레드의 집합소라 할 수 있는 쓰레드 풀(Thread Pool) 을 생성하죠. &lt;strong&gt;즉, Tomcat 을 통해 쓰레드풀을 지원받을 수 있는것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;요청에-대한-쓰레드-풀-활용방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%94%EC%B2%AD%EC%97%90-%EB%8C%80%ED%95%9C-%EC%93%B0%EB%A0%88%EB%93%9C-%ED%92%80-%ED%99%9C%EC%9A%A9%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;요청에 대한 쓰레드 풀 활용방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;요청에 대한 쓰레드 풀 활용방식&lt;/h2&gt;
&lt;p&gt;클라이언트에 요청이 들어왔을 때 어떤 메커니즘으로 쓰레드 풀을 활용하여 처리할까요?&lt;/p&gt;
&lt;p&gt;톰캣(Tomcat) 은 앞서 언급했듯이 쓰레드 풀을 생성합니다. 클라이언트의 요청(HttpServletReqeust) 가 들어오면 쓰레드 풀에서 각 요청에 대해 처리해줄 하나씩 쓰레드를 할당하고, 스프링부트에서 작성한 Dispatcher Servlet 을 거쳐서 요청을 처리합니다. 그리고 작업을 모두 마쳤다면 쓰레드는 다시 쓰레드풀로 반환되죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;그래서-쓰레드-풀이-뭔데&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%9E%98%EC%84%9C-%EC%93%B0%EB%A0%88%EB%93%9C-%ED%92%80%EC%9D%B4-%EB%AD%94%EB%8D%B0&quot; aria-label=&quot;그래서 쓰레드 풀이 뭔데 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그래서 쓰레드 풀이 뭔데!?&lt;/h2&gt;
&lt;p&gt;우선 쓰레드(Thread) 란 CPU 의 리소스를 이용해서 코드를 실행하는 하나의 단위 객체를 의미합니다. 또 쓰레드 풀은 이렇게 프로그램 실행에 필요한 쓰래드(Thread) 들을 미리 생성해놓은 집합 저장소를 의미합니다.&lt;/p&gt;
&lt;p&gt;쓰레드 풀은 지난번에 학습했던 &lt;a href=&quot;https://velog.io/@msung99/DB-%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80Connection-Pool-%EA%B3%BC-DataSource-%EC%9D%B4%EB%9E%80&quot;&gt;데이터베이스 커넥션 풀(Connection Pool),DataSource&lt;/a&gt; 의 커넥션 풀의 개념과 유사하다고 느꼈습니다. DB 에 원하는 리소스를 요청하기 위해 매번 커넥션을 생성하고 얻어오는 과정은 리소스를 많이 잡아먹게되므로, 일정량의 커넥션을 커넥션 풀에 미리 생성해두는 것과 동일한 이유입니다.&lt;/p&gt;
&lt;h3 id=&quot;쓰레드-풀-개념-도입-이전의-tomcat-의-요청-처리방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%93%B0%EB%A0%88%EB%93%9C-%ED%92%80-%EA%B0%9C%EB%85%90-%EB%8F%84%EC%9E%85-%EC%9D%B4%EC%A0%84%EC%9D%98-tomcat-%EC%9D%98-%EC%9A%94%EC%B2%AD-%EC%B2%98%EB%A6%AC%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;쓰레드 풀 개념 도입 이전의 tomcat 의 요청 처리방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쓰레드 풀 개념 도입 이전의 Tomcat 의 요청 처리방식&lt;/h3&gt;
&lt;p&gt;Tomcat 3.2 이전 버전에서는 쓰레드 풀 개념이 존재하지 않았습니다. 매 요청이 들어올 때 마다 운영체제 자체에서 지원해주는 쓰레드를 매번 생성하고 요청을 처리했으며, 내용을 처리했다면 쓰레드가 제거되는 방식이었습니다.&lt;/p&gt;
&lt;p&gt;그러나 운영체제의 쓰레드 숫자는 제한되어 있기에, 운영체제가 지원하는 쓰레드 수를 초과해서 사용시 애플리케이션에 장애(ex. 서버다운) 를 유발할 수 있습니다. 또 하나의 운영체제 쓰레드를 만드는 작업은 자원 낭비(과한 CPU와 메모리 자원 사용)가 심했으며, 매번 많은 요청에 대해 쓰레드를 생성하는 것은 OS 와 JVM 에 큰 부하를 안겨줄 수 있습니다.&lt;/p&gt;
&lt;p&gt;동시간대에 대규모 트래픽이 발생할 경우 이를 감당하지 못하는 문제가 발생하게됩니다. 이런 한계를 극복하도록 해당 문제를 해결하기 위해, 톰캣에 스레드풀을 도입합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;요청에-대한-처리-메커니즘&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%94%EC%B2%AD%EC%97%90-%EB%8C%80%ED%95%9C-%EC%B2%98%EB%A6%AC-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98&quot; aria-label=&quot;요청에 대한 처리 메커니즘 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;요청에 대한 처리 메커니즘&lt;/h2&gt;
&lt;p&gt;그렇다면 쓰레드 풀을 도입시, 요청에 대한 처리방식이 어떠할지 알아봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/21d48816-f5bc-459c-a3ca-99dfe4ff2dea/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;첫 작업 내용이 요청으로 들어오면, application.yml 파일에서 별도의 설정이 없다면 기본적으로 CPU Core 의 갯수만큼 쓰레드를 생성합니다. 그리고 매 클라이언트로 부터 받은 요청에 대한 커넥션 객체(Connection) 를 작업 큐(Queue) 에 넣어두죠. 그리고 쓰레드 풀에서 작업 가능한 상태인 쓰레드를 꺼내서 작업 큐에 대한 커넥션들에 대해 작업을 처리하는 방식입니다.&lt;/p&gt;
&lt;p&gt;만약 작업 가능한 쓰레드가 없다면 커넥션들은 계속 작업 큐에서 대기상태로 있어야합니다. 대기상태에 빠진 커넥션이 늘어가서 작업 큐가 꽉찬다면, 쓰레드를 새롭게 생성합니다. 이렇게 진행하다 최악의 경우 현재 요청이 더 들어오는데 작업 가능한 쓰레드도 없고 작업 큐가 완전 꽉찬 상태라면, 해당 요청은 refused 됩니다. (connection refused 오류 반환 )
(=&gt; 추후 설명하겠지만, 이렇게 refused 되는 방식은 BIO Connector 에서나 그러는것입니다. 성능이 개선된 NIO Connector 에서는 요청이 refused 되지 않고 처리 가능합니다.)&lt;/p&gt;
&lt;p&gt;마지막으로 쓰레드가 작업을 마쳤다면 다시 쓰레드 풀로 반환됩니다. 이때 쓰레드는 다시 바로 작업 가능한 상태로 반환되며, 작업 큐가 텅텅 비어있고 CPU Core 사이즈(또는 yml파일에 지정해준 갯수) 이상으로 쓰레드가 쓰레드 풀에 존재할 경우, 필요 이상으로 쓰레드가 많이 존재할 이유가 없기 때문에 해당 쓰레드를 제거시킵니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;쓰레드풀-생성-threadpoolexecutor&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%93%B0%EB%A0%88%EB%93%9C%ED%92%80-%EC%83%9D%EC%84%B1-threadpoolexecutor&quot; aria-label=&quot;쓰레드풀 생성 threadpoolexecutor permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쓰레드풀 생성 (ThreadPoolExecutor)&lt;/h2&gt;
&lt;p&gt;ThreadPoolExecutor 는 쓰레드풀을 자바에서 구현한 구현체입니다. application.yml에서 쓰레드 풀 구현체인 ThreadPoolExecutor에 대한 자세한 설정이 가능한데, 어떤 옵션을 부여할 수 있는지 알아보겠습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;server&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  tomcat&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    threads&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      max&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;  # 생성할 수 있는 thread의 최대 총 개수
      min&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;spare&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;  # 항상 활성화 되어있는 thread의 개수
    max&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;connections&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8192&lt;/span&gt; # max&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;connections &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; 수립가능한 connection의 총 개수
    accept&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;count&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt; # 작업 큐의 사이즈
    connection&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;timeout&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20000&lt;/span&gt;  # 커넥션 유지시간 &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;생명주기&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  port&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;우선 max 옵션으로 쓰레드 최대 사이드를 설정하고, min-spare 로 항상 활성화 되어있는 쓰레드의 갯수를 지정할 수 있습니다. 이들은 Tomcat 9.0 기준으로 디폴트 값이 200개, 10개 입니다. 당연한 말이지만, min-spare 에 지정한 갯수만큼 쓰레드 풀에 쓰레드가 생성되고 작업큐에 처리할 내용이 없어도 소멸되지 않고 활성화된 상태로 계속 존재합니다.&lt;/p&gt;
&lt;p&gt;accept-count는 작업큐의 사이즈로, 디폴트로 Integer.MAX 값을 부여합니다. 이는 무한 대기열 전략 으로, 아무리 요청이 많이 들어와도 core size를 늘리지 않는다는 정책입니다.&lt;/p&gt;
&lt;h3 id=&quot;무한-대기열-전략&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B4%ED%95%9C-%EB%8C%80%EA%B8%B0%EC%97%B4-%EC%A0%84%EB%9E%B5&quot; aria-label=&quot;무한 대기열 전략 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;무한 대기열 전략&lt;/h3&gt;
&lt;p&gt;그런데 무한 대기열 전략에선 작업큐가 꽉 찰 일이 없으므로, 스레드풀의 max사이즈가 의미가 없어지지 않을까요? 작업큐가 꽉 차고나서야 추가적인 쓰레드들이 계속 생성되며, 작업 큐의 사이즈는 굉장히 커서 쓰레드가 추가적으로 생성될 일이 없을테니까요. 그러나 설정을 주어 max값을 변경하는 건 아무 의미 없는것이 아닙니다. 이는 추후 설명드리겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nio-connector--tomcat-90-부터-지원&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nio-connector--tomcat-90-%EB%B6%80%ED%84%B0-%EC%A7%80%EC%9B%90&quot; aria-label=&quot;nio connector  tomcat 90 부터 지원 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;NIO Connector : Tomcat 9.0 부터 지원&lt;/h2&gt;
&lt;p&gt;저희는 작업 큐의 사이즈에 따라서 요청이 refused 될수도 있는 상황에 놓일 수 있습니다. 만약 accept-count 옵션을 1, max 옵션을 2 로 부여했을 때 동시간대에 5개의 요청이 온 경우를 가정해봅시다.
즉, 작업 큐의 사이즈가 1이며 최대 생성 가능한 쓰레드 개수가 2인 경우를 생각해봅시다. 이 경우 5개의 요청을 다 수락하지 못하고 거절될 수 있으나, NIO Connector 를 활용함에 따라 모든 요청을 수락할 수 있게됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이때 몰론 수많은 트래픽이 발생시 Race Condition 등에 의해 동시성 이슈가 발생할 수 있으나, 이런 상황들은 멀티쓰레드의 주제에서 벗어나므로 제외하고 생각하겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;bio-connector&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bio-connector&quot; aria-label=&quot;bio connector permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BIO Connector&lt;/h3&gt;
&lt;p&gt;톰캣 8.0부터 NIO(NonBlocking I/O) Connector 라는 커넥터가 기본으로 채택되고, 9.0부터는 NIO Connector 가 채택됨으로써 위 설명과는 다른 방식으로 진행되게 됩니다.&lt;/p&gt;
&lt;p&gt;기존 방식이라면 특정 커넥션이 닫힐 때까지 하나의 Thread는 특정 커넥션에 계속 할당되어 있을 것입니다. 이러한 방식을 채택해서 사용할 경우 thread들이 충분히 사용되지 않고 idle(아무것도하지않는) 상태로 낭비되는 시간이 많이 발생합니다. 이러한 문제점을 해결하고 리소스(thread)를 효율적으로 사용하기 위해 NIO Connector가 등장했습니다.&lt;/p&gt;
&lt;h3 id=&quot;nio-connector&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nio-connector&quot; aria-label=&quot;nio connector permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;NIO Connector&lt;/h3&gt;
&lt;p&gt;NIO Connector에선 Poller 라는 별도의 스레드가 커넥션을 처리합니다. Poller는 커넥션들을 잠시 캐시에 들고 있다가 해당 커넥션에서 데이터에 대한 처리가 가능한 순간에만 번쩍 thread를 할당하는 방식을 사용해서 thread이 idle 상태로 낭비되는 시간을 줄여줍니다.&lt;/p&gt;
&lt;h3 id=&quot;poller-쓰레드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#poller-%EC%93%B0%EB%A0%88%EB%93%9C&quot; aria-label=&quot;poller 쓰레드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Poller 쓰레드&lt;/h3&gt;
&lt;p&gt;Poller에선 max connection 까지 연결을 수락하고, 작업큐 사이즈와 관계 없이 캐싱 기능을 활용하므로 추가로 커넥션을 refuse하지 않고 받아놓을 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;쓰레드-풀의-적정크기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%93%B0%EB%A0%88%EB%93%9C-%ED%92%80%EC%9D%98-%EC%A0%81%EC%A0%95%ED%81%AC%EA%B8%B0&quot; aria-label=&quot;쓰레드 풀의 적정크기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쓰레드 풀의 적정크기&lt;/h2&gt;
&lt;p&gt;쓰레드 풀에 무조건 많은 스레드를 생성해두면 좋을까요? 쓰레드를 많이 생성해둔다고 그 스레드를 다 사용할 수 있는 것이 아닙니다.&lt;/p&gt;
&lt;p&gt;따라서 쓸데없이 쓰레드를 많이 생성한다면 생성하는 데에 드는 자원과 비용이 낭비됩니다. 그렇다고 쓰레드를 부족하게 만들어둔다면 클라이언트에 대한 응답시간이 너무 느려지고, CPU 사용률이 낮아지게 될 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;1-cpu-코어의-개수&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-cpu-%EC%BD%94%EC%96%B4%EC%9D%98-%EA%B0%9C%EC%88%98&quot; aria-label=&quot;1 cpu 코어의 개수 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. CPU 코어의 개수&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;하나의 CPU 코어는 한 번에 하나의 스레드를 실행할 수 있습니다.&lt;/strong&gt; 만약 쿼드 코어라면 4개의 스레드를 한 번에 실행할 수 있죠. 이러한 &lt;strong&gt;코어의 개수에 따라 적정 스레드 크기를 정하면 됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-cpu-bound-tasks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-cpu-bound-tasks&quot; aria-label=&quot;2 cpu bound tasks permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. CPU Bound Tasks&lt;/h3&gt;
&lt;p&gt;하이퍼 쓰레딩이 지원되지 않는경우, 하나의 코어는 하나의 쓰레드만 실행가능합니다. 따라서 CPU를 점유하는 작업이 하나의 스레드에서 실행되고 있다면 다른 스레드는 그 시간 동안 아무것도 할 수 없습니다.&lt;/p&gt;
&lt;p&gt;따라서 메모리를 낭비할 뿐, 성능 향상에는 아무런 도움이 되지 않습니다. 따라서 &lt;strong&gt;CPU bound 작업의 경우에는 CPU 코어 개수와 동일하게 스레드 풀의 크기를 정하는 게 좋습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;계산식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%84%EC%82%B0%EC%8B%9D&quot; aria-label=&quot;계산식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;계산식&lt;/h3&gt;
&lt;p&gt;대게의 서적에서 권장하는 적정 쓰레드 개수 공식은 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;쓰레드 개수 = CPU 코어 수 * (1 + 대기시간/작업시간)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@sihyung92/how-does-springboot-handle-multiple-requests&quot;&gt;스프링부트는 어떻게 다중 유저 요청을 처리할까? (Tomcat9.0 Thread Pool)&lt;/a&gt;
&lt;a href=&quot;https://velog.io/@nyong_i/Thread-%ED%92%80%E3%84%B9%E3%84%B9%E3%84%B9%E3%84%B9&quot;&gt;SpringBoot에서 Thread 풀 건드려보기&lt;/a&gt;
&lt;a href=&quot;https://code-lab1.tistory.com/269?category=1272047&quot;&gt;이상적인 스레드 풀의 적정 크기에 대하여, 스레드 풀 크기 공식, 리틀의 법칙&lt;/a&gt;
&lt;a href=&quot;https://velog.io/@yohanblessyou/%EC%8A%A4%EB%A0%88%EB%93%9C-%ED%92%80Thread-pool%EC%9D%98-%EC%A0%81%EC%A0%95-%ED%81%AC%EA%B8%B0-feat.-%EB%A6%AC%ED%8B%80%EC%9D%98-%EB%B2%95%EC%B9%99&quot;&gt;스레드(Thread)의 적정 개수&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[쿠버네티스(Kubernetes) 클러스터와 애플리케이션 실행 아키텍처]]></title><description><![CDATA[왜 K8S? [Kubernetes…]]></description><link>https://haon.site/haon/kubernetes/cluster/</link><guid isPermaLink="false">https://haon.site/haon/kubernetes/cluster/</guid><pubDate>Sat, 11 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;왜-k8s&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-k8s&quot; aria-label=&quot;왜 k8s permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 K8S?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Kubernetes-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%98-%EB%B6%84%ED%95%A0%EA%B3%BC-%ED%99%95%EC%9E%A5%EC%97%90-%EB%94%B0%EB%A5%B8-%EC%9D%B8%ED%94%84%EB%9D%BC-%EA%B4%80%EB%A6%AC&quot;&gt;[Kubernetes] 마이크로서비스 애플리케이션의 분할과 확장에 따른 인프라 관리&lt;/a&gt; 에서도 언급했듯이, 기존의 모놀리스 애플리케이션에서 &lt;strong&gt;마이크로서비스&lt;/strong&gt;로 변함에 따라 데브옵스의 중요성은 더욱 두각되고 있습니다. 시스템의 배포 가능한 애플리케이션 구성 요소의 수가 많아짐에 따라 모든 구성 요소의 관리가 더 어려워지고있죠. 구글은 10년 동안 보그와 오메가를 비밀로 유지하다가 오픈소스 시스템인 쿠버네티스를 출시하게 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;쿠버네티스의-메인기능&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4%EC%9D%98-%EB%A9%94%EC%9D%B8%EA%B8%B0%EB%8A%A5&quot; aria-label=&quot;쿠버네티스의 메인기능 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쿠버네티스의 메인기능&lt;/h2&gt;
&lt;p&gt;쿠버네티스는 리눅스 컨테이너의 기능에 의존해 애플리케이션의 내부 세부 상항을 알 필요없이, 각 호스트에 애플리케이션을 수동으로 배포하지 않고도 여러 애플리케이션을 실행할 수 있게합니다. &lt;strong&gt;애플리케이션은 컨테이너에서 실행되므로 동일한 서버에서 실행되는 다른 애플리케이션에 영향을 미치지 않습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;마치-클러스터-운영체제와-같다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EC%99%80-%EA%B0%99%EB%8B%A4&quot; aria-label=&quot;마치 클러스터 운영체제와 같다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치 클러스터 운영체제와 같다&lt;/h3&gt;
&lt;p&gt;쿠버네티스는 클러스터의 운영체제로 이해하면 쉽습니다. &lt;strong&gt;서비스 디스커버리(서비스 보존), 스케일링, 로드밸런싱, 자가 치유, 리더 선출&lt;/strong&gt; 등의 다양한 기능을 제공받을 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;쿠버네티스-클러스터의-아키텍처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EC%9D%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; aria-label=&quot;쿠버네티스 클러스터의 아키텍처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쿠버네티스 클러스터의 아키텍처&lt;/h2&gt;
&lt;p&gt;지금부터 쿠버네티스 클러스터가 어떻게 구성돼있는지 자세히 살펴봅시다. 쿠버네티스 클러스터는 2종류의 여러 노드로 구성되어 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;마스터--워커-노드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%8A%A4%ED%84%B0--%EC%9B%8C%EC%BB%A4-%EB%85%B8%EB%93%9C&quot; aria-label=&quot;마스터  워커 노드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마스터 &amp;#x26; 워커 노드&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;마스터 노드 : 전체 쿠버네티스 시스템을 제어하고 관리하는 &lt;strong&gt;쿠버네티스 컨트롤 플레인을 실행&lt;/strong&gt;합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;워커 노드 : 실제 배포되는 &lt;strong&gt;컨테이너 애플리케이션을 실행&lt;/strong&gt;합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위와같은 두 종류의 노드의 구성요소를 자세히 살펴보면 아래와 같습니다. 이들에 대해서도 알아봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/62f1ff68-026c-4180-b469-a5702d219b81/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;컨트롤-플레인control-plane&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%8A%B8%EB%A1%A4-%ED%94%8C%EB%A0%88%EC%9D%B8control-plane&quot; aria-label=&quot;컨트롤 플레인control plane permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨트롤 플레인(Control Plane)&lt;/h3&gt;
&lt;p&gt;컨트롤 플레인은 &lt;strong&gt;클러스터(Cluster)를 제어하고 작동&lt;/strong&gt;시킵니다. 하나의 마스터 노드에서 실행하거나 여러 노드로 분할되고 복재돼 고가용성을 보장할 수 있는 여러 구성 요소로 구성됩니다. 그에 대한 구성요소는 다음과 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;쿠버네티스 API 서버 : 사용자, 컨트롤 플레인 구성요소와 통신합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;스캐줄러 : 애플리케이션의 배포를 담당합니다.&lt;/li&gt;
&lt;li&gt;컨트롤러 매너저 : 구성요소 복제본, 워커노드 추적, 노드 장애처리와 같은 클러스터단의 기능을 수행합니다.&lt;/li&gt;
&lt;li&gt;Etcd : 클러스터 구성을 저장하는 분산 데이터 저장소입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;워커-노드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%8C%EC%BB%A4-%EB%85%B8%EB%93%9C&quot; aria-label=&quot;워커 노드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;워커 노드&lt;/h3&gt;
&lt;p&gt;워커 노드는 컨테이너화된 애플리케이션을 실행하는 시스템입니다. 애플리케이션을 실행하고 모너터링하며 애플리케이션에 서비스를 제공하는 작업은 다음 구성요소들에 의해 수행됩니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;컨테이너 런타임 : 컨테이너를 실행하는 도커, rkt 등이 해당됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Kubelet : API 서버와 통신하고 노드의 컨테이너를 관리합니다.&lt;/li&gt;
&lt;li&gt;kube-proxy : 로드밸런싱 역할을 수행하는 쿠버네티스 서비스 프록시입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;애플리케이션-실행-메커니즘&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%8B%A4%ED%96%89-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98&quot; aria-label=&quot;애플리케이션 실행 메커니즘 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;애플리케이션 실행 메커니즘&lt;/h2&gt;
&lt;h3 id=&quot;디스크립션&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%94%94%EC%8A%A4%ED%81%AC%EB%A6%BD%EC%85%98&quot; aria-label=&quot;디스크립션 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;디스크립션&lt;/h3&gt;
&lt;p&gt;애플리케이션을 실행하려면, 애플리케이션을 이미지로 패키징하고 이미지 레지스트리
(ex. DockerHub) 에 푸시하고 &lt;strong&gt;쿠버네티스 API 서버에다 애플리케이션 디스크립션을 게시해야 합니다.&lt;/strong&gt; 게시하면 API 서버는 작성된 디스크립션 내용에 기반하여 컨테이너를 실행하고 인프라 환경을 구축합니다.&lt;/p&gt;
&lt;p&gt;이 디스크립션에는 컨테이너 이미지, 애플리케이션 구성요소가 포함된 이미지, 해당 구성요소들이 서로 통신하는 방법, 동일 서버에 함께 배치되야하는 구성요소와 같은 정보가 포함됩니다. 추가적으로 실행할 각 구성 요소의 복제본 수도 지정할 수도 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;아키텍처와-애플리케이션-실행-메커니즘&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%99%80-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%8B%A4%ED%96%89-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98&quot; aria-label=&quot;아키텍처와 애플리케이션 실행 메커니즘 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;아키텍처와 애플리케이션 실행 메커니즘&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e3d5dff3-4e99-42d2-948c-af87af61f321/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;API 서버가 애플리케이션 디스크립션을 처리할 때 &lt;strong&gt;스케줄러&lt;/strong&gt;는 각 컨테이너에 필요한 리소스를 계산하고, 각 노드에 할당되지 않은 리소스를 기반으로 &lt;strong&gt;사용 가능한 워커 노드에 지정된 컨테이너를 할당합니다.&lt;/strong&gt;
그 다음 해당 노드의 &lt;strong&gt;Kubelet&lt;/strong&gt; 은 컨테이너 런타임(ex. 도커) 에 필요한 컨테이너 이미지를 가져와 &lt;strong&gt;컨테이너를 실행하도록 지시&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;p&gt;위 예시를 보면 애플리케이션 디스크립터는 3개 세트(파드)로 그룹화된 4개의 컨테이너를 가지고 있습니다. 디스크립터에서 2개의 파드는 각각 하나의 컨테이너만 가지고, 마지막 파드는 2개의 컨테이너가 있습니다. 즉, 2개의 컨테이너를 함께 배치해야하며 서로 격리해서는 안됩니다.
또 파드 옆에는 실행해야하는 각 파드의 복제본 수도 지정되어 있습니다. 디스크립터를 쿠버네티스에 제출 후 각 파드의 지정된 복제본 수를 사용가능한 워커노드로 할당하는 방식입니다.&lt;/p&gt;
&lt;h3 id=&quot;실행된-컨테이너-유지하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%ED%96%89%EB%90%9C-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%9C%A0%EC%A7%80%ED%95%98%EA%B8%B0&quot; aria-label=&quot;실행된 컨테이너 유지하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실행된 컨테이너 유지하기&lt;/h3&gt;
&lt;p&gt;애플리케이션이 실행되면 쿠버네티스는 &lt;strong&gt;애플리케이션의 배포 상태가 사용자가 제공한 디스크립션과 일치하는지 지속적으로 확인합니다.&lt;/strong&gt; 예를들어 항상 4개의 인스턴스를 실행하도록 디스크립션에 지정했다면 항상 정확히 4개의 인스턴스가 실행되도록 합니다.&lt;/p&gt;
&lt;h3 id=&quot;복제본-수-스캐일링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B3%B5%EC%A0%9C%EB%B3%B8-%EC%88%98-%EC%8A%A4%EC%BA%90%EC%9D%BC%EB%A7%81&quot; aria-label=&quot;복제본 수 스캐일링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;복제본 수 스캐일링&lt;/h3&gt;
&lt;p&gt;애플리케이션이 실행되는 동안 복제본 수를 늘릴지 줄일지 결정할 수 있습니다. 또는 최적의 복제본 수를 결정하는 작업을 쿠버네티스에 맡길 수도 있죠.&lt;/p&gt;
&lt;h3 id=&quot;로드밸런싱&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1&quot; aria-label=&quot;로드밸런싱 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로드밸런싱&lt;/h3&gt;
&lt;p&gt;쿠버네티스는 클러스터 안에서 컨테이너를 이동시킬 수도 있다고 했었습니다. 애플리케이션 컨테이너가 이동이 어떻게 찾을 수 있을까요? 이는 &lt;strong&gt;kube-proxy&lt;/strong&gt; 가 제공해주는 로드밸런싱 기능으로 해결됩니다. 하나의 고정 IP 주소로 모든 컨테이너라 로드밸런싱 되도록하며, 해당 IP 주소로 클러스터에서 실행중인 모든 애플리케이션을 노출시키는 방식입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;쿠버네티스의-강점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4%EC%9D%98-%EA%B0%95%EC%A0%90&quot; aria-label=&quot;쿠버네티스의 강점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쿠버네티스의 강점&lt;/h2&gt;
&lt;h3 id=&quot;애플리케이션-배포의-단순화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EB%B0%B0%ED%8F%AC%EC%9D%98-%EB%8B%A8%EC%88%9C%ED%99%94&quot; aria-label=&quot;애플리케이션 배포의 단순화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;애플리케이션 배포의 단순화&lt;/h3&gt;
&lt;p&gt;쿠버네티스는 모든 워커 노드를 하나의 배포 플랫폼으로 제공합니다. 모든 노드는 이제 애플리케이션이 해당 노드를 사용하기를 기다리는 리소스 입장이 되었으며, 개발자는 애플리케이션이 어느 서버에서 실행 중인지 신경쓰지 않아도 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;상태확인-및-자가치유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%81%ED%83%9C%ED%99%95%EC%9D%B8-%EB%B0%8F-%EC%9E%90%EA%B0%80%EC%B9%98%EC%9C%A0&quot; aria-label=&quot;상태확인 및 자가치유 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;상태확인 및 자가치유&lt;/h3&gt;
&lt;p&gt;쿠버네티스는 애플리케이견 구성요소와 애플리케이션이 구동중인 노드를 모니터링하다가 노드에 장애가 발생시 자동으로 애플리케이션을 다른 노드로 스캐줄링합니다.
이로써 애플리케이션을 재배치하는 대신 즉시 노드 자체를 수정해 사용 가능한 하드웨어 리소스 풀에 반환하는데 집중할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;오토-스캐일링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%A4%ED%86%A0-%EC%8A%A4%EC%BA%90%EC%9D%BC%EB%A7%81&quot; aria-label=&quot;오토 스캐일링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;오토 스캐일링&lt;/h3&gt;
&lt;p&gt;급격한 부하 증가시 각 애플리케이션에서 사용하는 리소르를 모니터링하고, 각 애플리케이션의 실행중인 인스턴스 수를 계속 조정하도록 지시할 수 있습니다.
즉, &lt;strong&gt;배포된 애플리케이션의 부하에 따라 전체 클러스터 크기를 자동으로 확장 또는 축소할 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;k8s가-개발자에게도-제공하는-이점이-있을까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#k8s%EA%B0%80-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%97%90%EA%B2%8C%EB%8F%84-%EC%A0%9C%EA%B3%B5%ED%95%98%EB%8A%94-%EC%9D%B4%EC%A0%90%EC%9D%B4-%EC%9E%88%EC%9D%84%EA%B9%8C&quot; aria-label=&quot;k8s가 개발자에게도 제공하는 이점이 있을까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;K8S가 개발자에게도 제공하는 이점이 있을까?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;애플리케이션이 개발과 프로덕션 환경이 모두 동일한 환경에서 실행된다&lt;/strong&gt;는 사실로 돌아가보면 버그가 발견됐을 때 큰 효과가 있습니다. 버그를 해결하고 수정해야하는 개발자의 작업이 줄어들죠.&lt;/p&gt;
&lt;p&gt;또한 새로운 버전의 애플리케이션을 출시할 때 쿠버네티스가 &lt;strong&gt;새로운 버전이 잘못됐는지 자동으로 감지하고 즉시 롤아웃을 중지&lt;/strong&gt;하기 때문에, 개발자들의 신뢰성을 증사킬 수 있죠. 이로 매번 버전 업데이트를 자주 할 수 있게되고, 지속적인 전달(CD) 작업 속도가 빨라져서 개발팀과 운영팀 모두에게 도움이 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.manning.com/books/kubernetes-in-action-second-edition&quot;&gt;Kubernetes in Action, Second Edition&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[도커는 어떻게 컨테이너 단위의 격리된 프로세스 환경을 구성할 수 있을까?]]></title><description><![CDATA[컨테이너 단위의 격리된 프로세스 환경 가상머신(Virtual Machine…]]></description><link>https://haon.site/haon/server/docker-container-principle/</link><guid isPermaLink="false">https://haon.site/haon/server/docker-container-principle/</guid><pubDate>Sat, 11 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;컨테이너-단위의-격리된-프로세스-환경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%8B%A8%EC%9C%84%EC%9D%98-%EA%B2%A9%EB%A6%AC%EB%90%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%ED%99%98%EA%B2%BD&quot; aria-label=&quot;컨테이너 단위의 격리된 프로세스 환경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨테이너 단위의 격리된 프로세스 환경&lt;/h2&gt;
&lt;p&gt;가상머신(Virtual Machine) 을 사용해 각 마이크로서비스의 환경을 격리하는 대신 개발자들은 리눅스 컨테이너 기술로 눈을 돌렸습니다. 컨테이너에서 실행되는 프로세스는 다른 프로세스와 마찬가지로 동일한 호스트 OS 에서 실행됩니다. 그러나 &lt;strong&gt;컨테이너내의 프로세스는 여전히 다른 프로세스와 격리되어 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;컨테이너-vs-가상머신&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-vs-%EA%B0%80%EC%83%81%EB%A8%B8%EC%8B%A0&quot; aria-label=&quot;컨테이너 vs 가상머신 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨테이너 vs 가상머신&lt;/h3&gt;
&lt;p&gt;컨테이너는 가상머신에 비해 경량화된 것으로써 동일한 환경에서 더 많은 소프트웨어 구성 요소를 실행할 수 있습니다. 가상머신은 구성 요소 프로세스뿐만 아니라 시스템 자체의 프로세스도 실행해야 하기 때문에 리소스가 많이 요구됩니다. 반면 &lt;strong&gt;컨테이너는 호스트 OS에서 실행되는 격리된 프로세스에 지나지 않으며, 각 컨테이너에 들어있는 애플리케이션을 실행할 때 소비되는 리소스외에는 추가 프로세스에 대한 오버헤드는 없습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d892b7ed-f1ca-46f9-8072-ca8388bdd41b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위와 같이 호스트에서 가상머신 3개를 실행하면, 3개는 각각 완전히 분리된 OS가 실행도고 동일한 베어메탈 하드웨어를 공유하게 됩니다. 또 이런 가상머신 단위의 격리환경의 구성요소는 호스트 OS 와 하이퍼바이저가 존재하죠.&lt;/p&gt;
&lt;h3 id=&quot;하이퍼바이저--guest-os&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%98%EC%9D%B4%ED%8D%BC%EB%B0%94%EC%9D%B4%EC%A0%80--guest-os&quot; aria-label=&quot;하이퍼바이저  guest os permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;하이퍼바이저 &amp;#x26; Guest OS&lt;/h3&gt;
&lt;p&gt;하이퍼바이저는 각 가상머신에 대해 게스트 OS를 생성하고, 각 게스트 OS에게 자원을 적절히 분배합니다. 또한 각 게스트 OS 는 각각 다른 게스트 OS 와 완전히 독립된 공간과 시스템 자원을 할당받고 사용합니다.&lt;/p&gt;
&lt;p&gt;특정 가상머신 내에서 실행되는 애플리케이션이 가상머신 게스트 OS 커널에 대한 시스템 콜을 호출하면, 커널은 하이퍼바이저로 호스트의 물리적 CPU 에서 명령을 수행합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;컨테이너를-활용시-동일한-호스트-os-에서-실행된다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%A5%BC-%ED%99%9C%EC%9A%A9%EC%8B%9C-%EB%8F%99%EC%9D%BC%ED%95%9C-%ED%98%B8%EC%8A%A4%ED%8A%B8-os-%EC%97%90%EC%84%9C-%EC%8B%A4%ED%96%89%EB%90%9C%EB%8B%A4&quot; aria-label=&quot;컨테이너를 활용시 동일한 호스트 os 에서 실행된다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨테이너를 활용시 동일한 호스트 OS 에서 실행된다&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/aeead788-4e12-494a-837f-33b87a1cd89b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;반면 &lt;strong&gt;컨테이너는 호스트 OS 에서 실행되는 동안 동일한 커널에서 시스템 콜을 수행합니다.&lt;/strong&gt; 직전에 언급한 가상머신 환경은 하이퍼바이저를 활용해서 각기 다른 게스트 OS 커널에 대한 시스템 콜을 수행하는 방식이였죠.&lt;/p&gt;
&lt;p&gt;동일한 시스템에서 더 많은 수의 격리된 프로세스를 사용하려면 컨테이너의 오버헤드가 낮기 때문에 컨테이너를 사용하는 것이 좋습니다. 각 가상머신은 각기 다른 자체 시스템 서비스를 실행하지만, &lt;strong&gt;컨테이너는 모두 동일한 OS 에서 실행되므로 컨테이너는 시스템 서비스를 실행하지 않는다는 점을 기억합시다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;즉 컨테이너를 실행하면 가상머신처럼 부팅할 필요없이, 컨테이너에서 실행되는 프로세스는 즉시 시작됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;컨테이너-격리는-어떻게-가능할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EA%B2%A9%EB%A6%AC%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B0%80%EB%8A%A5%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;컨테이너 격리는 어떻게 가능할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨테이너 격리는 어떻게 가능할까?&lt;/h2&gt;
&lt;p&gt;앞서 언급했듯이, &lt;strong&gt;각 컨테이너는 동일한 OS 커널을 통해 호출되며, 각 컨테이너에는 제각각의 격리된 프로세스를 지니고 있습니다.&lt;/strong&gt; 그렇다면 컨테이너가 동일한 OS 에서 실행중인 경우 어떻게 프로세스를 격리시킬 수 있는 환경을 구성할 수 있을까요?
바로 리눅스 네임스페이스와 cgroup(리눅스 컨트롤 그룹) 을 통해 격리가 가능합니다.&lt;/p&gt;
&lt;h3 id=&quot;리눅스-네임스페이스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%EB%88%85%EC%8A%A4-%EB%84%A4%EC%9E%84%EC%8A%A4%ED%8E%98%EC%9D%B4%EC%8A%A4&quot; aria-label=&quot;리눅스 네임스페이스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리눅스 네임스페이스&lt;/h3&gt;
&lt;p&gt;각 리눅스 시스템은 기본적으로 하나의 네임스페이스라는 것을 보유하고 있습니다.
파일시스템, 프로세스 ID, 사용자 ID, 네트워크 등과 같은 모든 시스템 리소스는 하나의 네임스페이스에 속합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;각 프로세스를 실행시 여러 네임스페이스중 하나에서 프로세스를 실행하며, 프로세스는 동일한 네임스페이스 내에 있는 리소스만 조회 가능합니다.&lt;/strong&gt; 이러한 특징을 활용하여, 각 네임스페이스는 특정 리소스 그룹을 격리하는데 사용합니다. 두 프로세스를 마치 2개의 다른 시스템에서 실행중인 것 처럼 보이게 할 수 있는것입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;각 컨테이너는 고유한 각 네임스페이스를 사용하므로, 각 컨테이너는 고유한 네임스페이스를 활용해 격리 가능합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;cgroup&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cgroup&quot; aria-label=&quot;cgroup permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;cgroup&lt;/h3&gt;
&lt;p&gt;또 컨테이너가 사용할 수 있는 시스템 리소스의 양을 제한하는 방법도 활용해서 격리합니다. 리소스 사용을 제한하는 리눅스 커널 기능인 cgroup 을 활용합니다. 각 프로세스는 설정된 양 이상의 CPU, 메모리 등을 사용할 수 없어서, &lt;strong&gt;프로세스는 다른 프로세스용으로 예약된 리소스를 사용할 수 없으며&lt;/strong&gt;, 이는 각 프로세스가 별도의 시스템에서 실행될 때와 비슷합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;도커-컨테이너-플랫폼&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%ED%94%8C%EB%9E%AB%ED%8F%BC&quot; aria-label=&quot;도커 컨테이너 플랫폼 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도커 컨테이너 플랫폼&lt;/h2&gt;
&lt;p&gt;도커로 패키징된 애플리케이션을 실행시 함께 제공된 파일시스템 (메타데이터) 내용을 정확히 볼 수 있습니다. 각 컨테이너의 애플리케이션은 실행중인 서버의 내용은 볼 수 없으므로, 서버에 개발 컴퓨터와 다른 설치 라이브러리가 있는지는 중요하지 않습니다.&lt;/p&gt;
&lt;p&gt;도커는 애플리케이션을 전체 환경과 함께 패키지화 할 수 있습니다. &lt;strong&gt;애플리케이션에서 필요한 몇가지 라이브러리나 OS의 파일시스템에 설치되는 모든 파일을 포컨테이너안에 포함시킬 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;도커-이미지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%BB%A4-%EC%9D%B4%EB%AF%B8%EC%A7%80&quot; aria-label=&quot;도커 이미지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도커 이미지&lt;/h3&gt;
&lt;p&gt;애플리케이션과 해당 환경을 패키지화 한 것입니다. 이 이미지에는 애플리케이션에서 사용할 수 있는 &lt;strong&gt;파일시스템&lt;/strong&gt;과, 이미지가 실행될 때 실행되야하는 실행파일 경로와 같은 &lt;strong&gt;메타데이터&lt;/strong&gt;가 포함되어 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;도커-컨테이너&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88&quot; aria-label=&quot;도커 컨테이너 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도커 컨테이너&lt;/h3&gt;
&lt;p&gt;도커 이미지로부터 생성된 일반적인 리눅스 컨테이너입니다. 컨테이너안에는 프로세스가 존재하며, 컨테이너는 프로세스의 생명을 관리하는 툴이라 보면 됩니다. &lt;strong&gt;각 컨테이너의 프로세스는 동일한 호스트에서 실행되는 프로세스이지만, 프로세스들은 각각 다른 프로세스들과 모두 격리되어 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;또 프로세스는 리소스 사용이 제한돼 있으므로 리소스의 지정된 양(CPU, RAM 등) 만 엑세스하고 사용할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;이미지-레이어&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A0%88%EC%9D%B4%EC%96%B4&quot; aria-label=&quot;이미지 레이어 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이미지 레이어&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c59bb353-d0c8-4357-8de9-7ba563da5d0e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그런데 사실 &lt;strong&gt;동일한 부모 도커 이미지를 통해 실행된 컨테이너의 애플리케이션들은 같은 파일을 공유할 수 있습니다.&lt;/strong&gt; 각 컨테이터는 격리된 자체 파일시스템이 존재한다고 했는데, 어떻게해서 애플리케이션들이 같은 파일을 공유할 수 있을까요?&lt;/p&gt;
&lt;h3 id=&quot;컨테이너들이-함께-공유하는-부모-이미지-레이어는-읽기-전용이다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%93%A4%EC%9D%B4-%ED%95%A8%EA%BB%98-%EA%B3%B5%EC%9C%A0%ED%95%98%EB%8A%94-%EB%B6%80%EB%AA%A8-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A0%88%EC%9D%B4%EC%96%B4%EB%8A%94-%EC%9D%BD%EA%B8%B0-%EC%A0%84%EC%9A%A9%EC%9D%B4%EB%8B%A4&quot; aria-label=&quot;컨테이너들이 함께 공유하는 부모 이미지 레이어는 읽기 전용이다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨테이너들이 함께 공유하는 부모 이미지 레이어는 읽기 전용이다&lt;/h3&gt;
&lt;p&gt;도커 이미지는 래이어로 구성돼있으며, 2개의 다른 이미지를 기본 이미지로 동일한 부모 이미지를 사용할 수 있으므로 동일한 레이어가 포함될 수 있습니다.
각 레이어는 동일 호스트에 1번만 저장됩니다. 따라서 &lt;strong&gt;동일한 기본 레이어를 기반으로 한 2개의 이미지에서 생성된 컨테이너는 동일한 파일을 읽을 수&lt;/strong&gt; 있지만, 그 중 하나가 해당 파일을 덮어쓰면 다른 해당 변경 사항을 읽을 수 없습니다. 따라서 파일을 공유하더라도 여전히 서로 격리돼있는데 이것은 컨테이너 이미지 레이어가 읽기 전용이기 때문입니다.&lt;/p&gt;
&lt;h3 id=&quot;각-컨테이너마다-별도의-쓰기-가능한-레이어가-생긴다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%81-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%A7%88%EB%8B%A4-%EB%B3%84%EB%8F%84%EC%9D%98-%EC%93%B0%EA%B8%B0-%EA%B0%80%EB%8A%A5%ED%95%9C-%EB%A0%88%EC%9D%B4%EC%96%B4%EA%B0%80-%EC%83%9D%EA%B8%B4%EB%8B%A4&quot; aria-label=&quot;각 컨테이너마다 별도의 쓰기 가능한 레이어가 생긴다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;각 컨테이너마다 별도의 쓰기 가능한 레이어가 생긴다&lt;/h3&gt;
&lt;p&gt;또 &lt;strong&gt;각 컨테이너가 실행될 때 이미지 레이어 위에 각각 쓰기 기능한 레이어가 생깁니다.&lt;/strong&gt; 컨테이너 프로세스가 기본 부모 레이어 중 하나에 있는 파일에 쓰면 전체 파일의 복사본의 최상위 레이어에 만글어지고 프로세스는 복사본에 씁니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;결론&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EB%A1%A0&quot; aria-label=&quot;결론 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결론&lt;/h2&gt;
&lt;p&gt;도커는 컨테이너를 주류로 만든 최초의 컨테이터 플랫폼입니다. 각 컨테이너는 격리된 프로세스가 실행되고 있으며, 동일한 부모 이미지 레이어에서 생성된 컨테이너들은 동일한 파일시스템을 공유하며, 각 컨테이너마다 별도의 쓰기 가능한 레이어가 생깁니다.&lt;/p&gt;
&lt;p&gt;이때 도커 자체가 프로세스 격리를 제공하지 않는다는 것을 유의합시다.&lt;strong&gt;컨테이너의 격리는 리눅스 네임스페이스와 cgroup 과 같은 커널 기능으로 리눅스 커널 수준에서 수행되는 것입니다.&lt;/strong&gt; 도커는 이런 기능을 사용하기 쉽게 하는것이고요.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.manning.com/books/kubernetes-in-action-second-edition&quot;&gt;Kubernetes in Action, Second Edition&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[MSA 애플리케이션의 분할과 확장에 따른 인프라 관리]]></title><description><![CDATA[현 포스팅은 마이크로서비스의 특징을 짚어보고, 왜 K8S 를 사용하는 것이 효율적인지 이유를 핵심으로 다루었습니다. K8S…]]></description><link>https://haon.site/haon/kubernetes/msa/</link><guid isPermaLink="false">https://haon.site/haon/kubernetes/msa/</guid><pubDate>Fri, 10 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;현 포스팅은 마이크로서비스의 특징을 짚어보고, 왜 K8S 를 사용하는 것이 효율적인지 이유를 핵심으로 다루었습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;k8s-의-등장배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#k8s-%EC%9D%98-%EB%93%B1%EC%9E%A5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;k8s 의 등장배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;K8S 의 등장배경&lt;/h2&gt;
&lt;p&gt;몇년전까지만 해도 개발되던 거대한 모놀리스 레거시 애플리케이션은 릴리스 주기가 느리고 업데이트가 자주 되지 않는 편입니다. 이런 모놀리스 애플리케이션은 점차 마이크로서비스라는 독립적인 실행되는 더 작은 구성 요소로 세분화되었죠.&lt;/p&gt;
&lt;p&gt;마이크로서비스는 서로 분리되어 있기 때문에 개별적으로 개발, 배포, 업데이트, 확장이 가능합니다. 하지만 &lt;strong&gt;배포 가능한 구성 요소 수가 많아지면서 시스템을 원활히 구성, 관리하는 일이 어려워졌습니다.&lt;/strong&gt; 이런 구성 요소의 서버 배포를자동으로 스캐줄링하고 구성, 관리, 장애 처리를 자동화해주는 작업이 필요하게 되었으며, 이것이 바로 쿠버네티스가 등장한 이유입니다.&lt;/p&gt;
&lt;p&gt;쿠버네티스틑 하드웨어에 장애가 발생시 해당 애플리케이션을 자동으로 모니터링하고 스캐줄링을 조정해서 운영 팀을 도와주는 강력한 기능을 지닙니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;모놀리스-애플리케이션의-한계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%A8%EB%86%80%EB%A6%AC%EC%8A%A4-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%98-%ED%95%9C%EA%B3%84&quot; aria-label=&quot;모놀리스 애플리케이션의 한계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;모놀리스 애플리케이션의 한계&lt;/h2&gt;
&lt;h3 id=&quot;버전-관리가-불편하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B2%84%EC%A0%84-%EA%B4%80%EB%A6%AC%EA%B0%80-%EB%B6%88%ED%8E%B8%ED%95%98%EB%8B%A4&quot; aria-label=&quot;버전 관리가 불편하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;버전 관리가 불편하다&lt;/h3&gt;
&lt;p&gt;모놀리스 애플리케이션은 전체 애플리케이션이 하나의 운영체제 프로세스로 실행되기 때문에 하나의 객체로 개발, 배포, 관리되어야 합니다. &lt;strong&gt;애플리케이션의 한 부분을 변경하면 전체 애플리케이션을 재배포해야 합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;수직확장scale-in-수평확장scale-out-하는-것도-한계가-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%98%EC%A7%81%ED%99%95%EC%9E%A5scale-in-%EC%88%98%ED%8F%89%ED%99%95%EC%9E%A5scale-out-%ED%95%98%EB%8A%94-%EA%B2%83%EB%8F%84-%ED%95%9C%EA%B3%84%EA%B0%80-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;수직확장scale in 수평확장scale out 하는 것도 한계가 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;수직확장(Scale In), 수평확장(Scale Out) 하는 것도 한계가 있다&lt;/h3&gt;
&lt;p&gt;또 애플리케이션을 실행하기 위한 인프라를 구축시 서버 부하를 위해 수직확장, 수평확장을 진행해야할 상황이 발생할겁니다. 그러나 &lt;a href=&quot;https://velog.io/@msung99/%ED%94%84%EB%A1%9D%EC%8B%9C-%EC%84%9C%EB%B2%84Forward-Proxy-Reverse-Proxy-%EC%99%80-Load-Balancer-%EB%9E%80&quot;&gt;Proxy Server와 로드밸런싱, 수평확장(Scale Out)과 수직확장(Scale Up)에 대해&lt;/a&gt; 에서도 언급했듯이, 수직확장은 애플리케이션을 변경할 필요가 없지만 비용이 많이 들죠.&lt;/p&gt;
&lt;p&gt;반면 수평확장은 저렴하지만 애플리케이션의 코드의 큰 변경이 생기게됩니다. 그러나 서버가 1000대 이상으로 수없이 늘어난 경우라면 이 작업도 매우 곤란해집니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마이크로서비스의-등장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4%EC%9D%98-%EB%93%B1%EC%9E%A5&quot; aria-label=&quot;마이크로서비스의 등장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마이크로서비스의 등장&lt;/h2&gt;
&lt;h3 id=&quot;독립적인-프로세스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%85%EB%A6%BD%EC%A0%81%EC%9D%B8-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4&quot; aria-label=&quot;독립적인 프로세스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;독립적인 프로세스&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e4f56aee-ace8-4e17-8bf7-28645e8fe657/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;앞서 살핀 기존의 복잡한 모놀리스 애플리케이션을 마이크로서비스라는 독립적으로 배포할 수 있는 작은 구성 요소(서비스)로 분할함으로서 단점을 보완할 수 있게 되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;각 마이크로서비스는 독립적인 프로세스로 실행되며, 단순하고 잘 정의된 API로 다른 마이크로서비스와 통신합니다.&lt;/strong&gt; 일반적으로 RestAPI 를 제공하는 HTTP 프로토콜로 통신하죠.&lt;/p&gt;
&lt;p&gt;각 마이크로서비스는 대체로 정적인 외부 API를 제공하는 독립형 프로세스이기 때문에, 개별적으로 개발 및 배포가 가능합니다. API 코드가 변경되지 않거나 이전 버전과 호환되는 방식으로 변경됐다면 이들 중 하나를 변경해도 다른 서비스를 변경하거나 재배포하지 않아도 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;마이크로서비스의-확장성과-배포&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4%EC%9D%98-%ED%99%95%EC%9E%A5%EC%84%B1%EA%B3%BC-%EB%B0%B0%ED%8F%AC&quot; aria-label=&quot;마이크로서비스의 확장성과 배포 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마이크로서비스의 확장성과 배포&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6ef8164b-ec93-415d-a929-31df2f1e8432/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4&gt;마이크로서비스의 강점&lt;/h4&gt;
&lt;p&gt;전체 시스템을 확장해야하는 모놀리스 시스템과 달리 마이크로서비스 확장은 각 시스템별로 수행되므로 &lt;strong&gt;리소스만 필요한 서비스만 별도로 확장 가능합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;또한 필요에 따라 &lt;strong&gt;특정 구성 요소(서비스)는 다른 서버에 배포된 여러 프로세스로 복제, 실행되며 다른 구성요소는 애플리케이션 프로세스 하나로 실행됩니다.&lt;/strong&gt; 모놀리스 애플리케이션의 구성요소가 확장이 불가능한 경우, 애플리케이션을 마이크로서비스 형태로 분할해서 수평확장 또는 수직확장을 가능하게 합니다.&lt;/p&gt;
&lt;h3 id=&quot;마이크로서비스의-한계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4%EC%9D%98-%ED%95%9C%EA%B3%84&quot; aria-label=&quot;마이크로서비스의 한계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마이크로서비스의 한계&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;세부적인 서비스 기능을 각자 팀마다 개발시, 동일한 일관된 환경을 맞추면서 개발하기가 힘들다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;하지만 구성 요소가 많아지면서 배포 조합의 수뿐만 아니라 구성 요소간의 상호 종속성 수가 더 많아지므로 &lt;strong&gt;배포 관련 결정 및 설계가 어려워집니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;마이크로서비스는 여러개가 서로 함께 작업을 수행하므로 서로를 찾아 통신해야합니다. 또 마이크로서비스는 여러 프로세스와 시스템에 분산되어 있기 때문에 버그가 발생할 경우 추적하기 어렵다는 한계가 있습니다.&lt;/p&gt;
&lt;p&gt;또한 마이크로서비스 아키텍처의 구성요소는 한 애플리케이션의 각 구성요소간의 &lt;strong&gt;종속관계를 잘 고려해야한다&lt;/strong&gt;는 까다로움이 존재합니다.
&lt;strong&gt;각 구성요소는 독립적으로 배포될 뿐만 아니라 독립적인 방식으로 개발된다&lt;/strong&gt;고 했었죠? 각 구성요소를 개발하는 팀이 세부적으로 여럿 존재할텐데, 각 팀이 다른 라이브러리를 사용한다면 호환성 문제가 발생할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;데브옵스로-애플리케이션에-일관된-환경을-제공해볼까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EB%B8%8C%EC%98%B5%EC%8A%A4%EB%A1%9C-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90-%EC%9D%BC%EA%B4%80%EB%90%9C-%ED%99%98%EA%B2%BD%EC%9D%84-%EC%A0%9C%EA%B3%B5%ED%95%B4%EB%B3%BC%EA%B9%8C&quot; aria-label=&quot;데브옵스로 애플리케이션에 일관된 환경을 제공해볼까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데브옵스로 애플리케이션에 일관된 환경을 제공해볼까?&lt;/h2&gt;
&lt;p&gt;이렇듯 개발시 애플리케이션을 실행하는 환경이 각 팀과 부서마다 다를 수 있다는 문제점이 존재합니다. 인프라 및 프로덕션 머신의 환경(ex. OS, 라이브러리, 네트워크 환경 등) 도 시간이 지남에 따라 버전이 변할 수 있어서, &lt;strong&gt;모든 환경요소를 동일히 맞추기란 굉장히 힘든일&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;따라서 상충되는 다른 버전의 라이브러리와 환경을 필요로 각자 개발되었을지라도, &lt;strong&gt;프로덕션 시스템은 운영하는 모든 애플리케이션에 호환되는 적절한 환경을 제공해줘야합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;또한 시간이 지나고도 해당 서버의 기존 애플리케이션에 영향을 주지않고 동일한 서버에 새로운 버전의 애플리케이션을 추가할 수 있는 기능을 제공해야 좋을것입니다.&lt;/p&gt;
&lt;h3 id=&quot;데브옵스의-필요성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EB%B8%8C%EC%98%B5%EC%8A%A4%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1&quot; aria-label=&quot;데브옵스의 필요성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데브옵스의 필요성&lt;/h3&gt;
&lt;p&gt;과거 개발팀의 업무는 애플리케이션을 만들고 배포하고 관리하며 운영(인프라)팀에 넘겨주는 방식이었죠. 그러나 이제는 개발팀이 애플리케이션을 배포하는 것에 더 많은 관여하는 방식을 바로 데브옵스라고 합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;개발자는 프로덕션 인프라 환경에서 애플리케이션을 실행하고 더 관여하는데 기여하면 운영팀이 직면하는 문제가 무엇인지 더 잘 알수있게됩니다.&lt;/strong&gt; 개발자는 그에 따라 인프라 구조를 더욱 잘 파악하고 피드백이나 개선점을 빠르게 반영할 수 있습니다.&lt;/p&gt;
&lt;p&gt;개발자가 만일 새로운 기능 개발에 집중하고 싶고, 운영팀은 인프라 관리에만 집중하고 싶다면 쿠버네티스(Kubernetes)를 사용하면 좋습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;쿠버네티스를 통해 하드웨어를 추상화하고 이를 애플리케이션 배포, 실행을 위한 플랫폼으로 제공&lt;/strong&gt;함으로써 개발자는 시스템 관리자의 도움 없이도 애플리케이션을 구성, 배포할 수 있습니다. 또 시스템 관리잔는 실제 실행되는 애플리케이션을 잘 알 필요없이 인프라를 유지하고 운영하는데 집중할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.manning.com/books/kubernetes-in-action-second-edition&quot;&gt;Kubernetes in Action, Second Edition&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[JPA의 비관적 락, MySQL 8.0 공유락과 베타락을 통한 동시성 제어]]></title><description><![CDATA[JPA 의 낙관적 락(Optimistic Lock) 과 비관적 락(Pessimistic Lock…]]></description><link>https://haon.site/haon/jpa/pemistic-lock/</link><guid isPermaLink="false">https://haon.site/haon/jpa/pemistic-lock/</guid><pubDate>Tue, 07 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/JPA-%EB%82%99%EA%B4%80%EC%A0%81-%EB%9D%BDOptimistic-Lock-%EA%B3%BC-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BDPessimistic-Lock-%EC%9D%84-%ED%86%B5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0&quot;&gt;JPA 의 낙관적 락(Optimistic Lock) 과 비관적 락(Pessimistic Lock) 으로 엔티티에 대한 동시성 이슈 제어하기&lt;/a&gt; 에서도 설명했듯이, 비관적 락은 여러 트랜잭션간의 충돌이 발생한다는 가정하에 우선 락을 걸고 보는 방법이였습니다.&lt;/p&gt;
&lt;p&gt;그리고 비관적 락을 구현하는 방법에는 공유 락(Shared Lock), 배타 락(Exclusive Lock) 이 존재합니다. 이들이 무엇인지 알아보고, 지난 내용에 이어 JPA 에서 제공하는 비관적 락에 대한 구현방법을 알아봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;공유-락-shared-lock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%B5%EC%9C%A0-%EB%9D%BD-shared-lock&quot; aria-label=&quot;공유 락 shared lock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;공유 락 (Shared Lock)&lt;/h2&gt;
&lt;p&gt;공유 락(Shared Lock) 은 읽기 락(Read Lock) 이라고도 불립니다. &lt;strong&gt;공유 락이 걸린 데이터에 대해서는 읽기 연산(SELECT) 만 가능하며, 쓰기 연산은 불가능합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;공유 락이 걸린 데이터에 대해서 다른 트랜잭션도 똑같이 공유락을 획득해서 읽기가 가능합니다. 하지만 배타 락(Exclusive Lock) 은 획득할 수 없습니다. 공유 락이 걸려도 다른 트랜잭션들이 읽기 작업은 가능하다는 뜻입니다.&lt;/p&gt;
&lt;p&gt;공유 락을 사용하면, 조회한 데이터가 트랜잭션 내내 변경되지 않음을 보장한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;다른 트랜잭션들에 대해 읽기 허용, 쓰기 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;배타락이 없는경우 사용가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;배타-락-exclusive-lock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%ED%83%80-%EB%9D%BD-exclusive-lock&quot; aria-label=&quot;배타 락 exclusive lock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배타 락 (Exclusive Lock)&lt;/h2&gt;
&lt;p&gt;배타 락은 쓰기 락(Write Lock) 이라고도 불립니다. 데이터에 대해 베타 락을 획득한 트랜잭션은 읽기 연산과 쓰기 연산 모두 실행할 수 있죠. 하지만 다른 트랜잭션은 배타 락이 걸린 데이터에 대해 읽기 작업도, 쓰기 작업도 수행할 수 없습니다. 즉 트랜잭션 A가 다른 트랜잭션이 락이 걸려 읽기, 쓰기 작업을 진행하지 못하게 되는 &lt;strong&gt;블로킹(Blocking)&lt;/strong&gt; 상태가 됩니다.&lt;/p&gt;
&lt;p&gt;즉, 배타 락이 걸려있다면 다른 트랜잭션은 공유 락, 배타 락 둘다 획득할 수 없습니다.
&lt;strong&gt;배타 락을 획득한 트랜잭션은 해당 데이터에 대해 독점권을 가지게 되는 것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;배타락을-구현시-데드락deadlock-이-발생한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%ED%83%80%EB%9D%BD%EC%9D%84-%EA%B5%AC%ED%98%84%EC%8B%9C-%EB%8D%B0%EB%93%9C%EB%9D%BDdeadlock-%EC%9D%B4-%EB%B0%9C%EC%83%9D%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;배타락을 구현시 데드락deadlock 이 발생한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배타락을 구현시 데드락(DeadLock) 이 발생한다?&lt;/h2&gt;
&lt;p&gt;지난번에 언급한 내용이지만, 더 부가설명을 붙여보겠습니다. 비관적 락을 구현할 경우 언제든 교착상태(DeadLock) 이 발생할 가능성을 염두해둬야 합니다.&lt;/p&gt;
&lt;h3 id=&quot;트랜잭션이-락을-획득하지-못하는-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%B4-%EB%9D%BD%EC%9D%84-%ED%9A%8D%EB%93%9D%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%98%EB%8A%94-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;트랜잭션이 락을 획득하지 못하는 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션이 락을 획득하지 못하는 경우&lt;/h3&gt;
&lt;p&gt;공유락과 달리 배타락은 공유락과 배타락 모두 획득할 수 없고 대기상태에 빠져수도 있게 됩니다. 트랜잭션이 락을 획득하지 못하게 된 경우에는 락을 획득하기 위해 대기하게 됩니다&lt;/p&gt;
&lt;p&gt;데드락이란 서로가 점유하고 있는 자원에 대해 무한정 대기하고 있는 상황을 의미합니다. 특정 데이터를 점유한다는 락(Lock)의 특성상 데드락이 발생할 수 있죠. 지난번에도 예시를들며 설명했지만, 새로운 예시로 다시 살펴봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/027c3e5f-5bd6-4af7-a4bd-3cd6174b5302/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;트랜잭션 A와 B는 동시에 실행된 상태이며, 각각 Order, User 데이터에 대해 조회 및 업데이트를 하며 배타락을 걸게된 상태입니다. 추후 트랜잭션 A 와 B 가 서로가 조회했었던 타인의 자원(데이터)에 접근하는데, 락이 걸린 상태로 인해 무한정 대기하게 되는 데드락 상태에 빠지게 되는 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;deadlock-해결방안&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#deadlock-%ED%95%B4%EA%B2%B0%EB%B0%A9%EC%95%88&quot; aria-label=&quot;deadlock 해결방안 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DeadLock 해결방안&lt;/h3&gt;
&lt;p&gt;데드락을 해결하기 위한 방법으로 다음과 같은 해결방안들을 생각해볼 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;트랜잭션 진행방향을 같은방향으로 처리합시다.&lt;/li&gt;
&lt;li&gt;트랜잭션 처리속도를 최소화해서, 데드락에 빠지는 상황을 방지합시다.&lt;/li&gt;
&lt;li&gt;LOCK TIMEOUT 을 이용하여 락 해제 시간을 조절해서 데드락으로부터 빠져나오게 합시다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timeout &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;jpa의-비관적-락-lockmodetype&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jpa%EC%9D%98-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BD-lockmodetype&quot; aria-label=&quot;jpa의 비관적 락 lockmodetype permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JPA의 비관적 락 LockModeType&lt;/h2&gt;
&lt;p&gt;JPA 에서는 @Lock 어노테이션을 통해 비관적 락과 추가 세부옵션을 지원해줍니다. 앞서 살펴본 공유 락, 배타 락등 비관적 박을 어떤 방식으로 구현할지 옵션을 지정할 수 있습니다.
비관적 락은 주로 PESSIMISTIC_WRITE (배타 락)을 사용합니다.&lt;/p&gt;
&lt;h3 id=&quot;pessimistic_write&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pessimistic_write&quot; aria-label=&quot;pessimistic_write permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PESSIMISTIC_WRITE&lt;/h3&gt;
&lt;p&gt;해당 자원에 데이터베이스 **베타 락(쓰기에 락)**을 걸때 사용합니다.
다른 트랜잭션에서 읽기와 쓰기 모두 불가능합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LockModeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PESSIMISTIC_WRITE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; price&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 또는 아래처럼 JpaRepository 에도 적용 가능하다&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JpaRepository&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token annotation punctuation&quot;&gt;@Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LockModeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PESSIMISTIC_READ&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;token class-name&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findOrderEntityById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; orderId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 또는 Row JPA 를 사용하고 있다면 EntityManager 를 통해 직접 락 옵션을 지정할 수 있습니다.&lt;/span&gt;
entityManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;order&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LockModeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PESSIMISTIC_WRITE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;이점 : Dirty Read 가 발생하지 않고 공유 락을 획득하며, 데이터가 UPDATE, UPDATE 되는 것을 방지할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;pessimistic_read&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pessimistic_read&quot; aria-label=&quot;pessimistic_read permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PESSIMISTIC_READ&lt;/h3&gt;
&lt;p&gt;해당 자원에 **공유 락(Shared Lock)**을 걸때 사용합니다.
다른 트랜잭션에서는 읽기는 가능하지만 쓰기는 불가능합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LockModeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PESSIMISTIC_READ&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; price&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;이점 : 배타 락을 획득하고 데이터를 다른 트랜잭션에서 READ, UPDATE, DELETE 하는것을 방지할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;pessimistic_force_increment&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pessimistic_force_increment&quot; aria-label=&quot;pessimistic_force_increment permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PESSIMISTIC_FORCE_INCREMENT&lt;/h3&gt;
&lt;p&gt;해당 자원에 &lt;strong&gt;배타 락&lt;/strong&gt;을 걸며, 비관적 락 중 유일하게 버전 정보를 사용한다. &lt;strong&gt;비관적 락이지만 버전 정보를 강제적으로 증가&lt;/strong&gt;시킵니다.&lt;/p&gt;
&lt;p&gt;이 잠금은 PESSIMISTIC_WRITE와 유사하게 작동 하지만 @Version이 지정된 Entity와 협력하기 위해 도입되어 PESSIMISTIC_FORCE_INCREMENT 잠금을 획득할 시 버전이 업데이트 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LockModeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PESSIMISTIC_FORCE_INCREMENT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; price&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;mysql-80-에서의-공유-락-베타-락&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mysql-80-%EC%97%90%EC%84%9C%EC%9D%98-%EA%B3%B5%EC%9C%A0-%EB%9D%BD-%EB%B2%A0%ED%83%80-%EB%9D%BD&quot; aria-label=&quot;mysql 80 에서의 공유 락 베타 락 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MySQL 8.0 에서의 공유 락, 베타 락&lt;/h2&gt;
&lt;p&gt;공유 락, 베타 락에 대한 옵션은 Auto Commit 이 비활성화 되거나, BEGIN 또는 START TRANSACTION 명령을 통해 트랜잭션이 시작된 상태에서만 락이 유지됩니다.&lt;/p&gt;
&lt;h3 id=&quot;공유-락&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%B5%EC%9C%A0-%EB%9D%BD&quot; aria-label=&quot;공유 락 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;공유 락&lt;/h3&gt;
&lt;p&gt;SELECT FOR SHARE 를 사용하여 특정 데이터로부터 공유 락을 획득할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WHERE&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;SHARE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;배타-락&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%ED%83%80-%EB%9D%BD&quot; aria-label=&quot;배타 락 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배타 락&lt;/h3&gt;
&lt;p&gt;SELECT FOR UPDATE 를 사용해서 특정 데이터로부터 배타 락을 획득할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WHERE&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;UPDATE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;트랜잭션-격리수준과-락의-차이&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80%EA%B3%BC-%EB%9D%BD%EC%9D%98-%EC%B0%A8%EC%9D%B4&quot; aria-label=&quot;트랜잭션 격리수준과 락의 차이 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 격리수준과 락의 차이&lt;/h2&gt;
&lt;p&gt;공부를 하면서 햇갈린 점이 하나 있습니다. 바로 트랜잭션의 격리수준과 앞서 살펴본 락의 차이입니다. 정리해보자면 다음과 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;격리수준은 해당 트랜잭션이 다른 트랜잭션에서 변경한 데이터를 볼 수 있는 기준을 정의한 것입니다.&lt;/li&gt;
&lt;li&gt;락(Lock) 은 다른 트랜잭션에서 해당 데이터에 접근하는 것을 막는 기능을 수행하는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;때로는-비관적-락이-성능이-좋을때가-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%95%8C%EB%A1%9C%EB%8A%94-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BD%EC%9D%B4-%EC%84%B1%EB%8A%A5%EC%9D%B4-%EC%A2%8B%EC%9D%84%EB%95%8C%EA%B0%80-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;때로는 비관적 락이 성능이 좋을때가 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;때로는 비관적 락이 성능이 좋을때가 있다?&lt;/h2&gt;
&lt;p&gt;추가적으로 낙관적 락은 일반적으로 비관적 락보다 성능이 좋은게 맞으나, 간혹 데이터 성향에 따라 비관적 락이 더 좋은 경우도 있습니다. 예를들어 재고가 1개인 상품이 있고, 100만 유저가 동시에 주문을 요청하는 상황입니다.&lt;/p&gt;
&lt;p&gt;비관적 락의 경우 1명의 유저 외에는 대기를 하다가 미리 트랜잭션 충돌 여부를 파악하게 됩니다. 즉, 재고가 없음을 미리 알리고 복잡한 처리를 하지 않아도 됩니다.&lt;/p&gt;
&lt;p&gt;반면 낙관적 락의 경우, 동시 요청을 보낸 유저들에 대해 순차적으로 처리하다가 커밋하는 시점이 되어서야 재고가 없음을 파악하게 됩니다. 또 처리한만큼 롤백도 해야하기 때문에, 자원 소모도 크게 발생하게 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/mysql-8.0-shared-lock-and-exclusive-lock/&quot;&gt;MySQL 8.0의 공유 락(Shared Lock)과 배타 락(Exclusive Lock)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://galid1.tistory.com/790&quot;&gt;Spring MVC 동기화와 JPA 잠금기법&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://isntyet.github.io/jpa/JPA-%EB%B9%84%EA%B4%80%EC%A0%81-%EC%9E%A0%EA%B8%88(Pessimistic-Lock)/&quot;&gt;JPA 비관적 잠금(Pessimistic Lock)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devbksheen.tistory.com/228&quot;&gt;JPA에서 낙관적 락(Optimistic Lock)과 비관적 락(Pessimistic Lock) 사용&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafcamus.tistory.com/48&quot;&gt;[JPA] 비관적 락과 낙관적 락, 트랜잭션의 격리 수준&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sabarada.tistory.com/187&quot;&gt;[Spring + JPA] jpa에서 Repository를 이용한 비관적락을 구현해봅시다. With MariaDB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@on5949/DB-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EA%B3%BC-%EB%9D%BD-2.-%EA%B3%B5%EC%9C%A0%EB%9D%BDShared-Lock-%EB%B0%B0%ED%83%80%EB%9D%BDExclusive-Lock&quot;&gt;[DB] 트랜잭션과 락 - 2. 공유락(Shared Lock) &amp;#x26; 배타락(Exclusive Lock)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://escapefromcoding.tistory.com/727&quot;&gt;낙관적 잠금과 비관적 잠금으로 동시성 해결하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@lsb156/JPA-Optimistic-Lock-Pessimistic-Lock#%EB%B9%84%EA%B4%80%EC%A0%81-%EC%9E%A0%EA%B8%88&quot;&gt;JPA의 낙관적 잠금(Optimistic Lock), 비관적 잠금(Pessimistic Lock)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@lsb156/JPA-Optimistic-Lock-Pessimistic-Lock&quot;&gt;JPA의 낙관적 잠금(Optimistic Lock), 비관적 잠금(Pessimistic Lock)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jaehoney.tistory.com/159&quot;&gt;JPA - 비관적 락, 낙관적 락 (+ 트랜잭션 격리 수준) 정리!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[JPA의 낙관적 락을 통해 엔티티 동시성 이슈 제어하기]]></title><description><![CDATA[락(Lock) 은 왜 필요한가? 짧은시간안에 요청이 많은 서버에서 여러 트랜잭션이 동시에 간읕 데이터에 업데이트를 발생시킬 경우에, 일부 요청이 유실디는 경우가 발생하여 장애로 이어질 수 있습니다. DBMS…]]></description><link>https://haon.site/haon/jpa/optimistic-lock/</link><guid isPermaLink="false">https://haon.site/haon/jpa/optimistic-lock/</guid><pubDate>Mon, 06 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;락lock-은-왜-필요한가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9D%BDlock-%EC%9D%80-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%9C%EA%B0%80&quot; aria-label=&quot;락lock 은 왜 필요한가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;락(Lock) 은 왜 필요한가?&lt;/h2&gt;
&lt;p&gt;짧은시간안에 요청이 많은 서버에서 여러 트랜잭션이 동시에 간읕 데이터에 업데이트를 발생시킬 경우에, 일부 요청이 유실디는 경우가 발생하여 장애로 이어질 수 있습니다.&lt;/p&gt;
&lt;p&gt;DBMS 에서 특정 데이터에 대한 동시접근이 발생시 일관성과 무결성이 보장되어야 합니다. 이러한 락(Lock) 을 구현하는 방식은 크게 낙관적 락(Optimistic Lock) 과 비관적 락(Pessimistic Lock)으로 나뉩니다.
(추후 설명하겠지만, 낙관적 락의 경우는 락을 구현한다는 표현은 사용하기 애매한 경계선에 있습니다.)&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;멀티쓰레드-환경에서의-동시성-이슈&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%80%ED%8B%B0%EC%93%B0%EB%A0%88%EB%93%9C-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C%EC%9D%98-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88&quot; aria-label=&quot;멀티쓰레드 환경에서의 동시성 이슈 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;멀티쓰레드 환경에서의 동시성 이슈&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Redis-%EB%B6%84%EC%82%B0-%EB%9D%BD%EC%9D%84-%EA%B5%AC%ED%98%84%ED%95%B4-race-condition-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0&quot;&gt;[Redis] 분산락(Distribution Lock) 을 구현해 다중화 서버에서 발생하는 동시성 문제 제어하기&lt;/a&gt; 에서도 설명했듯이, 멸티쓰레드 환경에서는 모든 쓰레드들이 동일한 공유자원에 접근시 경쟁 상황(Race Condition) 이 발생할 수 있고, 이는 곧 동시성 이슈로 이어질 수 있다고 했었습니다.&lt;/p&gt;
&lt;p&gt;위 내용에서 다룬것은 인프라에 다중 서버가 구축되어 있는 상황을 가정한 것이고, 저희는 단일서버임을 가정하고 조금 더 쉬운 관점으로 다시 동시성 이슈를 이해해보겠습니다.&lt;/p&gt;
&lt;p&gt;패션몰 온라인 서비스에서 고객이 주문한 상품의 배송지를 변경하는 쓰레드와, 관리자 페이지에서 운영자가 해당 고객이 주문한 상품의 배송상태를 변경하는 쓰레드가 있다고 가정해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6f239186-47b2-4047-a082-48cfc9f0207a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위 시나리오에서 조건이 하나 있다고하죠. 배송중인 상품은 배송지를 변경할 수 있는 정책이 존재한다고 해봅시다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;운영자는 배송상태를 변경하기 위해, 고객은 배송지를 변경하기 위해 주문정보를 조회합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;운영자는 배송상태를 배송중으로 변경합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;고객이 배송지를 변경합니다. (=&gt; But 조건으로 인해 배송지 변경이 안된다.)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;운영자, 고객애 대한 트랜잭션이 커밋과 함께 DB 에 반영됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이렇게 되면 운영자와 고객의 트랜잭션 모두 롤백(Rollback) 되지 않고 성공하는 것이죠? 운영자는 배송지를 변경못하도록 막기위해 배송상태를 변경했음에도 불구하고, 고객의 트랜잭션 성공으로 인해 배송지가 변경되버리는 상황이 발생하죠.&lt;/p&gt;
&lt;p&gt;이런 상황을 방지하도록 DBMS 가 지원하는 트랜잭션과 함께, 비즈니스 로직에도 추가적인 트랜잭션 처리 기법이 필요합니다. 그들이 바로 낙관적 락과 비관적 락 기법입니다.&lt;/p&gt;
&lt;p&gt;JPA 는 데이터베이스에 대한 동시 접근으로부터 엔티티에 대한 무결성을 유지할 수 있게 해주는 동시성 제어 메커니즘을 지원해주는 것입니다. 이 메커니즘에는 낙관적 락과 비관적 락이 존재하죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;낙관적-락optimistic-lock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%82%99%EA%B4%80%EC%A0%81-%EB%9D%BDoptimistic-lock&quot; aria-label=&quot;낙관적 락optimistic lock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;낙관적 락(Optimistic Lock)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;동시에 동일한 데이터에 대한 여러 업데이트에 서로 간섭하지 않도록 방지하는 version 이라는 속성을 확인하여 엔티티의 변경사항을 감지하는 메커니즘&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;JPA 엔티티 내부에 @Version 어노테이션이 붙은 필드를 구현해서 낙관적 락을 구현한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;낙관적 락(Optimistic Lock) 은 특정 자원에 대한 경쟁을 낙관적으로 바라보는 락 방식으로, &lt;strong&gt;여러 트랜잭션이 데이터를 동시에 수정하지 않는다는 가정하에 트랜잭션 충돌을 방지하는 기법&lt;/strong&gt;입니다. 쉽게말해, 자원에 락을 걸어서 선점하지말고 커밋할 때 동시성 문제가 발생하면 그때 처리하자는 방법론입니다.&lt;/p&gt;
&lt;p&gt;JPA 의 엔티티 특정 필드에 version 속성을 포함시켜서(@Version 어노테이션 붙이기), 별도의 락(Lock) 없이 트랜잭션 충돌을 방지할 수 있는 방법입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GeneratedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenerationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; orderId&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; orderName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Version&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; version&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;방금 설명했듯이 데이터를 읽는 시점에서는는 락(Lock)을 별도로 설정 및 사용하지 않습니다. 하지만 트랜잭션에 의해 잘못된 업데이트(update) 을 알아서 방지하지 않습니다. 데이터를 읽는 시점에서는 락을 사용하지 않지만, 데이터를 수정하는 시점에서 앞에 읽은 데이터가 다른 사용자에 의해 변경되었는지 검사해야합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;장점 : 읽기 시점에서 락을 사용하지 않기 때문에 성능이 좋다. 동시 업데이트가 없는 경우 이 방법을 사용시 비관적 락보다 빠르게 조회 및 업데이트가 가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;단점 : 여러 트랜잭션이 작업중에 하나의 트랜잭션이 공유자원을 변경할 경우, 다른 트랜잭션의 작업 내용이 거부된다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;optimisticlockexception&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#optimisticlockexception&quot; aria-label=&quot;optimisticlockexception permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OptimisticLockException&lt;/h3&gt;
&lt;p&gt;엔티티에서 낙관적 락 충돌(트랜잭션 충돌)을 감지한경우 OptimisticLockException 예외를 발생시키게 되고, 트랜잭션을 롤백 처리합니다.
즉, 엔티티를 수정할 때마다 JPA가 자체적으로 versioning 을 지원하기 때문에 &lt;strong&gt;조회시점과 수정시점의 버전이 다르면 OptimisticLockException 예외가 발생&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;p&gt;이에 대한 권장되는 예외처리 방법은 엔티티를 다시 로드하거나, 새로고침해서 업데이트를 재시도하는 방법입니다.&lt;/p&gt;
&lt;h3 id=&quot;낙관적-락-충돌감지-상황-예시&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%82%99%EA%B4%80%EC%A0%81-%EB%9D%BD-%EC%B6%A9%EB%8F%8C%EA%B0%90%EC%A7%80-%EC%83%81%ED%99%A9-%EC%98%88%EC%8B%9C&quot; aria-label=&quot;낙관적 락 충돌감지 상황 예시 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;낙관적 락 충돌감지 상황 예시&lt;/h3&gt;
&lt;p&gt;예시를 보시면 더 이해가 잘 되실겁니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/b15dd9d4-8a87-4a8d-972a-3bfb76a7dfc5/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;운영자는 배송상태를 변경하기 위해 version이 1인 주문정보를 조회한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;고객은 배송지를 변경하기 위해 version이 1인 주문정보를 조회한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;운영자는 배송상태를 배송중으로 변경한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;고객은 배송지를 변경한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;운영자의 행동이 DB에 반영됨과 함께 version이 2로 업데이트된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;고객의 행동이 DB에 반영됨과 함께 version1 정보를 version2 로 업데이트 할때 이미 DB 엔 해당 주문정보 엔티티에 대한 version 값이 2이므로, 누군가 수정했다고 판단하여 트랜잭션 커밋이 실패한다. (낙관적 락 충돌 감지)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;낙관적-락의-lockmodetype&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%82%99%EA%B4%80%EC%A0%81-%EB%9D%BD%EC%9D%98-lockmodetype&quot; aria-label=&quot;낙관적 락의 lockmodetype permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;낙관적 락의 LockModeType&lt;/h2&gt;
&lt;p&gt;낙관적 락을 구현시 기본적인 @Version 어노테이션만 붙여도 낙관적 락이 적용되지만, 더 세밀한 낙관적 락을 구현하고 싶다면 추가 옵션을 부여할 수 있습니다.&lt;/p&gt;
&lt;p&gt;엔티티의 특정 필드에 version 어노테이션을 명시해줬다면, 아래처럼 추가적인 락 옵션을 통해 다양한 락을 적용시킬 수 있습니다.
&lt;strong&gt;JpaRepsitory 를 사용한다면 @Lock 어노테이션의 LockModeType 를 지정할 수 있는것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LockModeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OPTIMISTIC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findByIdForUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; orderId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;none&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#none&quot; aria-label=&quot;none permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;None&lt;/h3&gt;
&lt;p&gt;별도의 옵션을 사용하지 않아도 엔티티에 @Version 어노테이션이 적용된 필드만 있다면 낙관적 락이 적용됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;용도 : 조회한 엔티티를 수정할때 다른 트랜잭션에 의해 변경(삭제)되지 않아야한다. 조회 시점부터 수정 시점까지를 보장합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;동작 : 엔티티를 수정시 버전을 체크하면서 버전을 증가합니다. (UPDATE 쿼리 사용). 이때 데이터베이스의 버전값이 현재 버전이 아니면 예외가 발생합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이점 : 두번의 갱실분실문제 (Second Lost updates probelem) 을 예방합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;optimistic-read&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#optimistic-read&quot; aria-label=&quot;optimistic read permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OPTIMISTIC (Read)&lt;/h3&gt;
&lt;p&gt;엔티티 수정시에만 발생하는 낙관적 락이 읽기 시에도 발생하도록 설정하는 락 모드입니다.
트랜잭션 시작시 버전 검사가 수행되고, 트랜잭션 종료시에도 버전 검사가 수행됩니다. &lt;strong&gt;읽기시에도 버전(version) 값을 체크하고 트랜잭션이 종료될 때까지 다른 트랜잭션에서 변경하지 않음을 보장합니다&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이를 통해 &lt;strong&gt;Dirty Read 와 Non Repeatable Read 를 방지&lt;/strong&gt;합니다. 혹시 이들이 무엇인지 모르신다면 &lt;a href=&quot;https://velog.io/@msung99/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%98-%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80isolation-level-ACID-%EC%84%B1%EC%A7%88%EC%97%90-%EB%8C%80%ED%95%B4&quot;&gt;트랜잭션의 격리수준(Transcation isolation level) 4단계, ACID 성질&lt;/a&gt; 을 참고하셔도 좋습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LockModeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OPTIMISTIC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 방법1&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findByIdForUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; orderId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

entityManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; orderid&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LockModeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OPTIMISTIC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 방법2 : EntityManager 활용&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;용도 : 조회 시점부터 트랜잭션이 끝날때까지 조회한 엔티티가 변경되지 않음을 보장한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;동작 : 트랜잭션을 커밋할 때 버전 정보를 조회해서 (SELECT 쿼리 사용) 현재 엔티티의 버전과 같은지 검증한다. 만일 버전이 다르담면 예외가 발생한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이점 : Dirty Read 와 Non-Repeatable Read 를 방지한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;optimistic_force_increment-write&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#optimistic_force_increment-write&quot; aria-label=&quot;optimistic_force_increment write permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OPTIMISTIC_FORCE_INCREMENT (Write)&lt;/h3&gt;
&lt;p&gt;낙관적 락을 사용하면서 버전 정보를 강제로 증가시키는 옵션입니다.
관계를 가진 다른 엔티티가 수정되면 버전이 변경됩니다. (ex. 댓글이 수정되면 게시글도 버전이 변경된다)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LockModeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OPTIMISTIC_FORCE_INCREMENT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 방법1&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findByIdForUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; orderId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

entityManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; studentId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LockModeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OPTIMISTIC_FORCE_INCREMENT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;용도 : 논리적인 단위의 엔티티 묶음을 관리할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;동작 : 엔티리를 수정하지 않아도 트랜잭션을 커밋할 때 버전 정보를 강제로 증가시킵니다. 예를들어 엔티티 A 에서 연관관계가 있는 엔티티 B 가 수정되었을 때 엔티티 A의 버전도 증가해야하는데, 이때 버전 정보를 강제로 증가시킵니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;비관적-락pessimistic-lock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BDpessimistic-lock&quot; aria-label=&quot;비관적 락pessimistic lock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비관적 락(Pessimistic Lock)&lt;/h2&gt;
&lt;p&gt;비관적 락은 어떤 자원 경쟁을 비관적으로 바라보기 때문에, 여러 트랜잭션이 데이터를 동시에 수정할 것이라고 가정하는 기법입니다.
하나의 트랜잭션이 데이터를 읽는 시점에서 락(Lock) 을 걸고, 조회 또는 업데이트 처리가 완료될 때까지 유지합니다.&lt;/p&gt;
&lt;p&gt;쉽게 말해, &lt;strong&gt;트랜잭션의 충돌이 발생한다고 가정하고 우선 락(Lock)을 걸고 보는방법입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;유의사항--데드락deadlock-상황-발생&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9C%A0%EC%9D%98%EC%82%AC%ED%95%AD--%EB%8D%B0%EB%93%9C%EB%9D%BDdeadlock-%EC%83%81%ED%99%A9-%EB%B0%9C%EC%83%9D&quot; aria-label=&quot;유의사항  데드락deadlock 상황 발생 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;유의사항 : 데드락(DeadLock) 상황 발생&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;쓰레드1 : 정보 A를 구하고 잠금&lt;/li&gt;
&lt;li&gt;쓰레드2 : 정보 B를 구하고 잠금&lt;/li&gt;
&lt;li&gt;쓰레드1 : 정보 B를 구하고자할때 블로킹(Blocking)&lt;/li&gt;
&lt;li&gt;쓰레드2 : 정보 A를 구하고자할때 블로킹&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이런 상황이라면 두 쓰레드 모두 영원히 작업을 끝낼 수 없는 교착상태, 즉 데드락(DeadLock) 에 빠지게됩니다. 데드락이 발생하지 않도록하려면 락을 시도할때 최대 잠금 시간을 지정해주면 됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;장점 : 트랜잭션의 동시 접근을 확실하게 방지해서 순차적인 처리가 가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;단점 : 한 트랜잭션 내용이 완료되기 전까지 다른 트랜잭션이 공유자원에 접근하지 못해서, 동시성이 떨어져 &lt;strong&gt;대기가 길어지고 성능이 떨어질 수 있습니다.&lt;/strong&gt;
또한 각 트랜잭션이 서로 자원을 점유한 채, 서로의 자원을 요청하며 무한정 대기하는 &lt;strong&gt;데드락(DeadLock)&lt;/strong&gt; 상황이 발생할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;비관적-락-충돌감지-예시&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BD-%EC%B6%A9%EB%8F%8C%EA%B0%90%EC%A7%80-%EC%98%88%EC%8B%9C&quot; aria-label=&quot;비관적 락 충돌감지 예시 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비관적 락 충돌감지 예시&lt;/h3&gt;
&lt;p&gt;비관적 잠근은 어떤 쓰레드가 아래처럼 주문에 대한 트랜잭션을 먼저 시도했다면, 주문 정보에 대한 트랜잭션이 끝나기 전까지는 다른 쓰레드들이 주문정보를 조회 및 트랜잭션 시도를 못하도록 잠그는 방식입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6a246c86-92b0-465a-abc4-e42530ade652/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;비관적-잠금의-lockmodetype&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EA%B4%80%EC%A0%81-%EC%9E%A0%EA%B8%88%EC%9D%98-lockmodetype&quot; aria-label=&quot;비관적 잠금의 lockmodetype permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비관적 잠금의 LockModeType&lt;/h3&gt;
&lt;p&gt;이 내용을 다루려면 공유 락(Shared Lock), 베타 락(Exclusive Lock) 에 대해서 이해해야하며, 이들로 다른 트랜잭션에서 READ, UPDATE, DELETE 하는 연산을 방지할 수 있습니다. 내용이 너무 길어지는 것 같아, 바로 다음 포스팅에서 다루어보겠습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;+추가 : 다음 포스팅 내용은 &lt;a href=&quot;https://velog.io/@msung99/JPA-%EC%9D%98-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BD%EC%97%90-%EB%8C%80%ED%95%9C-LockModeType-%EA%B3%B5%EC%9C%A0-%EB%9D%BD%EA%B3%BC-%EB%B0%B0%ED%83%80-%EB%9D%BD&quot;&gt;JPA, MySQL 8.0 에서의 비관적 락, 공유 락(Shared Lock)과 배타 락(Exclusive Lock)&lt;/a&gt; 을 참고해주세요!&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@lsb156/JPA-Optimistic-Lock-Pessimistic-Lock&quot;&gt;JPA의 낙관적 잠금(Optimistic Lock), 비관적 잠금(Pessimistic Lock)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://escapefromcoding.tistory.com/727&quot;&gt;낙관적 잠금과 비관적 잠금으로 동시성 해결하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hwannny.tistory.com/81&quot;&gt;비관적 잠금(선점 잠금, Pessimistic Lock)과 낙관적 잠금(비선점 잠금, Optimistic Lock)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.blog/jpa-concurrency-control-optimistic-lock-and-pessimistic-lock/&quot;&gt;JPA의 낙관적 락과 비관적 락을 통해 엔티티에 대한 동시성 제어하기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[트랜잭션의 격리수준, ACID]]></title><description><![CDATA[트랜잭션(Transaction) 의 성질 : ACID 트랜잭션하면 빼놓을 수 없는 키워드가 바로 ACID 라는 4가지 성질입니다. 이들이 무엇인지 간단히 되짚어보겠습니다. Atomicity…]]></description><link>https://haon.site/haon/database/isolation/</link><guid isPermaLink="false">https://haon.site/haon/database/isolation/</guid><pubDate>Sun, 05 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;트랜잭션transaction-의-성질--acid&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98transaction-%EC%9D%98-%EC%84%B1%EC%A7%88--acid&quot; aria-label=&quot;트랜잭션transaction 의 성질  acid permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션(Transaction) 의 성질 : ACID&lt;/h2&gt;
&lt;p&gt;트랜잭션하면 빼놓을 수 없는 키워드가 바로 ACID 라는 4가지 성질입니다. 이들이 무엇인지 간단히 되짚어보겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;atomicity-원자성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#atomicity-%EC%9B%90%EC%9E%90%EC%84%B1&quot; aria-label=&quot;atomicity 원자성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Atomicity (원자성)&lt;/h3&gt;
&lt;p&gt;우선 트랜잭션은 원자성이라는 성격을 띠고있습니다. &lt;strong&gt;트랜잭션 안의 내용은 모두 DB에 반영되거나 또는 전혀 반영되서는 안된다&lt;/strong&gt;는 뜻이죠. 트랜잭션 안의 중간 어딘가까지만 실행되고 갑자기 종료되는 일이 있어서는 안되는 것입니다.&lt;/p&gt;
&lt;p&gt;원자성이 보장되면 구매자의 계좌에서 돈이 빠져나갔는데, 판매자의 계좌에 돈이 들어오지 않는 일은 없을겁니다.&lt;/p&gt;
&lt;h3 id=&quot;consistency-일관성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#consistency-%EC%9D%BC%EA%B4%80%EC%84%B1&quot; aria-label=&quot;consistency 일관성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Consistency (일관성)&lt;/h3&gt;
&lt;p&gt;또 트랜잭션은 일관된 데이터베이스 상태를 유지해야 한다는 뜻입니다.** 즉, 트랜잭션은 DB 에 여러 제약조건에 맞는 상태를 보장해준다는 것이죠.**
만약 마이너스 통장(통장 잔금이 0원 이하)을 허락하지 않는 DB의 조건이 있다면, 이 조건이 위배될 때 트랜잭션은 바로 종료됩니다.&lt;/p&gt;
&lt;h3 id=&quot;isolation-독립성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#isolation-%EB%8F%85%EB%A6%BD%EC%84%B1&quot; aria-label=&quot;isolation 독립성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Isolation (독립성)&lt;/h3&gt;
&lt;p&gt;다음으로 독립성이라는 성질입니다. 트랜잭션은 각각의 독립성을 보장해야 하는 것이죠. &lt;strong&gt;즉, 둘 이상의 트랜잭션이 동시에 실행되고 있을때 그 어떤 트랜잭션도 다른 트랜잭션의 연산에 끼어들 수 없다는 뜻입니다.&lt;/strong&gt;
구매자의 계좌에서 돈이 빠져나가고 판매자의 계좌에 돈이 아직 들어가지 않는 DB 상황, 즉 송금+출금에 관한 트랜잭션이 완전히 끝나기전의 DB 상황을 다른 트랙잭션이 조회해서는 안됩니다.&lt;/p&gt;
&lt;h3 id=&quot;durability-지속성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#durability-%EC%A7%80%EC%86%8D%EC%84%B1&quot; aria-label=&quot;durability 지속성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Durability (지속성)&lt;/h3&gt;
&lt;p&gt;마지막으로 지속성입니다. &lt;strong&gt;트랜잭션이 성공했을 경우, 그 해당 결과가 DB에 영구적으로 반영되어야 한다는 것입니다.&lt;/strong&gt; 한번 송금에 성공했다면 은행 시스템에 문제가 발생하더라도 송금이 성공한 상태로 복구할 수 있어야합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;왜-격리수준을-알아야하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80%EC%9D%84-%EC%95%8C%EC%95%84%EC%95%BC%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;왜 격리수준을 알아야하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 격리수준을 알아야하는가?&lt;/h2&gt;
&lt;p&gt;위와 같은 ACID 성질은 트랜잭션이 이론적으로 보장해야하는 성질이며, 실제로는 성능을 위해 손실보장이 완화되기도 합니다.&lt;/p&gt;
&lt;p&gt;예를들어 독립성을 완벽히 보장하려고 하면 동일한 데이터에 100개의 연결이 접근했을 떄, 이 100개의 연결을 순차적으로 해결해야하죠.&lt;/p&gt;
&lt;p&gt;동시성(동시간대에 처리하는 양에 대한 성능)이 매우 떨어지는 문제가 발생하게 되는데, 동시성을 얻기위한 1가지 방법으로 트랜잭션의 격리수준(레벨) 설정이 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;동시성애 대한 성능을 향상시키기 위해, 격리수준을 이해하자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;트랜잭션-격리수준isolation-level&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80isolation-level&quot; aria-label=&quot;트랜잭션 격리수준isolation level permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트랜잭션 격리수준(isolation level)&lt;/h2&gt;
&lt;p&gt;트랜잭션 격리수준(isolation level)이란 동시에 DB에 접근할 떄, 그 접근을 어떻게 제외할지에 대한 설정입니다.&lt;/p&gt;
&lt;p&gt;좀 더 자세히 말해보면, &lt;strong&gt;동시에 여러 트랜잭션이 처리될 때, 트랜잭션끼리 얼마나 서로 고립되어 있는지를 나타내는 것입니다. 즉, 특정 트랙잭션이 다른 트랜잭션이 변경한 데이터를 볼 수 있도록 허용할지 말지를 결정하는 것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;격리수준은 크게 아래와 같은 4가지 단계로 나뉩니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;READ UNCOMMITED&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;READ COMMITED&lt;/li&gt;
&lt;li&gt;REPEATABLE READ&lt;/li&gt;
&lt;li&gt;SERIALIZABLE&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;아래로 내려갈수록 각 트랜잭션간의 고립 정도가 높아지며, 성능이 떨어지는 특징이 있습니다. 데이터 정합성과 성능이 반비례하므로, 각 상황에 알맞는 격리수준을 잘 선택하는 것이 중요합니다.&lt;/p&gt;
&lt;p&gt;참고로 일반적인 DB 서비스는 READ COMMITED 또는 REPEATABLE READ 중 하나를 선택합니다.
(oracle = READ COMMITED, mysql = REPEATABLE READ)&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;read-uncommited&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#read-uncommited&quot; aria-label=&quot;read uncommited permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;READ-UNCOMMITED&lt;/h2&gt;
&lt;p&gt;첫번째 격리레벨은 격리수준이 가장 낮은 READ_UNCOMMITED 레벨입니다. &lt;strong&gt;한 트랜잭션이 아직 커밋되지 않은 상태임에도 불구하고, 다른 트랜잭션에서 읽을 수 있는 격리수준&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;즉 트랙잭션 A의 변경내용이 커밋이나 롤백을 얼만큼 진행한 것과 상관없이 다른 트랜잭션B, C, ... 에게 노출되는 것이죠.&lt;/p&gt;
&lt;h3 id=&quot;더티-리드dirty-read-발생&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8D%94%ED%8B%B0-%EB%A6%AC%EB%93%9Cdirty-read-%EB%B0%9C%EC%83%9D&quot; aria-label=&quot;더티 리드dirty read 발생 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더티 리드(Dirty Read) 발생&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7843f5dc-e7c5-45ac-a96a-e2063e82bdcd/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이 격리수준은 아래와 같은 문제가 발생할 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;트랙잭션 A에서 10번 사원의 나이를 30살에서 31살으로 바꾼다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;아직 커밋하지는 않은 상태이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;트랜잭션 B에서 10번 사원의 나이를 조회한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;30살이 조회된다. =&gt; 이를 더티 리드(Dirty Read)라고 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;트랜잭션 A 에서 문제가 발생해 롤백합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;트랜잭션 B 는 10번 사원이 여전히 30살이라고 생각하고 로직을 수행합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이런식으로 데이터 정합성에 문제가 많기 때문에, RDBMS 표준에서는 격리수준으로 인정하지도 않습니다.&lt;/p&gt;
&lt;p&gt;또한 READ-UNCOMMITED 격리 레벨에서는 Dirty Read 현상뿐만 아니라 Repeatable Read 와 Phantom Read 문제도 발생할 수 있습니다. 이는 추후 다른 격리레벨에서 설명하겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;read-commited&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#read-commited&quot; aria-label=&quot;read commited permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;READ-COMMITED&lt;/h2&gt;
&lt;p&gt;두번째는 READ_COMMITED 격리수준입니다. &lt;strong&gt;어떤 트랜잭션의 변경내용이 커밋이 완료된 데이터만 다른 트랜잭션에서 조회 가능합니다.&lt;/strong&gt; 트랜잭션이 이루어지는동안 다른 사용자는 해당 데이터에 접근이 불가능하죠.&lt;/p&gt;
&lt;p&gt;트랜잭션 A에서 아직 커밋이 완료되지 않은 상태라면 트랜잭션 B는 시작전의 값을 읽어오고, 반대로 A에서 커밋이 완료된 상태라면 B는 변경된 데이터를 읽어옵니다.&lt;/p&gt;
&lt;p&gt;이 격리수준은 Oracle DBMS 에서 기본으로 사용하고 있으며, 대중적으로 가장 많이 선택되는 격리수준입니다.&lt;/p&gt;
&lt;h3 id=&quot;non-repeatable-read-발생&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#non-repeatable-read-%EB%B0%9C%EC%83%9D&quot; aria-label=&quot;non repeatable read 발생 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;NON-REPEATABLE READ 발생&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c533bad2-1d09-4bb5-810e-800e497707ee/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그러나 이 격리수준도 데이터에 대한 정합성 문제가 발생할 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;트랜잭션 B에서 10번 사원의 나이를 조회한다.&lt;/li&gt;
&lt;li&gt;30살이 조회되었다! 여기까지는 문제없다.&lt;/li&gt;
&lt;li&gt;트랜잭션 A 에서 10번 사원의 나이를 30살에서 31살로 바꾸기를 시도한다.&lt;/li&gt;
&lt;li&gt;트랜잭션 A에서 나이 변경내용을 커밋(COMMIT) 하기전에, B 에서 10번 사원의 나이를 다시 조회한다.&lt;/li&gt;
&lt;li&gt;그 결과는 30살이 조회된다!&lt;/li&gt;
&lt;li&gt;트랜잭션 A 가 커밋을 완료하고나서, 트랜잭션 B는 다시 나이를 조회해본다. 그 결과는 31살이 조회된다.&lt;/li&gt;
&lt;li&gt;이로써 같은 트랜잭션 내에서 select 쿼리를 2번 조회했는데, 두 값이 나이값이 조회되는 데이터 불일치 문제가 발생한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이는 하나의 트랜잭션내에서 똑같은 SELECT를 수행했을 경우 항상 같은 결과를 반환해야 한다는 REPEATABLE READ 정합성에 어긋나는 것이죠.&lt;/p&gt;
&lt;p&gt;일반적인 웹 애플리케이션이라면 크게 문제될 것 없지만, 결제 기능과 같은 금전
적인 처리와 연결된 기능이라면 문제가 발생할 수 있습니다. 예를들어 여러 트랜잭션에서 입/출금 처리가 계속 진행되는 트랜잭션들이 있고, 오늘의 입금 총 합을 보여주는 트랜잭션이 있다고하면, 총합을 계산하는 SELECT 쿼리는 실행될 떄 마다 다른 결과값을 가져올겁니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;repetable-read&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#repetable-read&quot; aria-label=&quot;repetable read permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;REPETABLE READ&lt;/h2&gt;
&lt;p&gt;3번쨰 격리수준은 REPEATABLE READ 입니다.
&lt;strong&gt;트랜잭션이 시작되기 전에 커밋된 내용에 대해서만 조회할 수 있는 격리수준입니다.&lt;/strong&gt; 트랜잭션이 완료될 떄 까지 SELECT 쿼리가 사용되는 모든 데이터에 Shared Lock(공유 락)이 걸리는 계층이죠.&lt;/p&gt;
&lt;p&gt;트랜잭션 범위 내에서 조회한 데이터 내용이 항상 동일함을 보장하며, 다른 사용자는 트랜잭션 영역에 해당되는 데이터에 대한 수정이 불가능합니다.&lt;/p&gt;
&lt;p&gt;MySQL DBMS에서 기본으로 사용하고 있고, 이 격리수준에서는 NON-REPETABLE READ 부정합이 발생하지 않습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c200669e-1cb8-484a-9d46-160b117f58c8/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;READ COMMITED 마찬가지로 커밋이 완료된 데이터만 읽을 수 있는데, 차이점은 뭘까요? 어떤 한 트랜잭션 A가 조회한 데이터는 해당 트랜잭션이 종료될 때까지 다른 트랜잭션이 변경하거나 삭제하는 것을 막으므로, 한번 조회한 데이터는 반복적으로 조회해도 같은 값을 반환합니다.&lt;/p&gt;
&lt;h3 id=&quot;phantom-read-발생&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#phantom-read-%EB%B0%9C%EC%83%9D&quot; aria-label=&quot;phantom read 발생 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Phantom Read 발생&lt;/h3&gt;
&lt;p&gt;하지만 이 격리수준도 센텀 리드(Phantom Read) 문제가 발생할 수 있습니다.&lt;/p&gt;
&lt;p&gt;센텀 리드는 Non-repeatable Read 의 한 종류로, 조건이 걸렸든 안 걸렸든 select 문을 쓸때 나타날 수 있는 현상입니다. 해당 쿼리로 읽히는 데이터에 들어가는 행이 새로 생기거나 없어져있는 현상이죠.&lt;/p&gt;
&lt;p&gt;즉, 한 트랜잭션 내에서 같은 쿼리를 2번실행시, 첫번쨰 쿼리에서 없었던 레코드 (유령,Phantom) 가 2번쨰 쿼리에서 발생하는 현상입니다. INSERT, DELETE 쿼리등에 대해 쓰기 잠금을 거는 경우, 다른 트랜잭션에서 수행한 변경작업 내용에 의해 레코드가 보였다가 안보였다가하는 현상입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/641d371e-5c6b-451f-9b0e-c29718791d02/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;serializable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#serializable&quot; aria-label=&quot;serializable permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SERIALIZABLE&lt;/h2&gt;
&lt;p&gt;마지막으로 격리 레벨이 가장 높은 SERIALIZABLE 입니다. 한 트랜잭션에서 사용하는 데이터를 다른 트랜잭션에서 절대 접근할 수 없습니다.트랜잭션의 ACID 성질이 엄격하게 지켜지지만, 성능은 가장 떨어집니다.&lt;/p&gt;
&lt;p&gt;단순 SELECT 쿼리만으로도 트랜잭션이 커밋될때까지 모든 데이터에 잠금(lock) 이 설정되어 다른 트랜잭션에서 해당 데이터를 변경할 수 없게 됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;트랜잭션이 완료될때까지 SELECT 쿼리가 사용되는 모든 데이터에 Shared Lock(공유 락) 이 걸리는 계층&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;가장 엄격힌 격리수준으로, 완벽한 읽기 일관성 모드를 제공한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;다른 사용자는 트랜잭션 영역에 해당되는 데이터에 대한 수정 및 입력 불가능&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dar0m.tistory.com/225&quot;&gt;트랜잭션 격리수준&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://joont92.github.io/db/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B2%A9%EB%A6%AC-%EC%88%98%EC%A4%80-isolation-level/&quot;&gt;[db] 트랜잭션 격리 수준(isolation level)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://steady-coding.tistory.com/562&quot;&gt;[데이터베이스] MySQL 트랜잭션 격리 수준&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Redis로 분산락을 구현하여 동시성 이슈를 해결해보자!]]></title><description><![CDATA[시작에 앞서 여러 프로세스가 공유하는 동일한 자원을 동일자원이라고 하며, 또 그러한 여러 프로세스가 접근하여 생기는 경쟁 상황(race condition) 을 동시성 문제(Concurrency Problem…]]></description><link>https://haon.site/haon/redis/distribute-lock/</link><guid isPermaLink="false">https://haon.site/haon/redis/distribute-lock/</guid><pubDate>Fri, 03 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;시작에-앞서&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%9E%91%EC%97%90-%EC%95%9E%EC%84%9C&quot; aria-label=&quot;시작에 앞서 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시작에 앞서&lt;/h2&gt;
&lt;p&gt;여러 프로세스가 공유하는 동일한 자원을 동일자원이라고 하며, 또 그러한 여러 프로세스가 접근하여 생기는 경쟁 상황(race condition) 을 동시성 문제(Concurrency Problem) 이라고 합니다.
보통 스프링부트와 같은 멀티 쓰레드 기반의 환경에서 자주 발생하는 문제인데, 저도 최근 학습을 진행하다 직면하게 된 이슈입니다. 보통 동시간대에 대규모의 요청 및 트래픽을 처리하는 상황에 발생하는 문제이죠. 이는 무엇이며, 어떻게 해결할 수 있는것인지 자세하게 알아봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;경쟁-상황race-condition&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%BD%EC%9F%81-%EC%83%81%ED%99%A9race-condition&quot; aria-label=&quot;경쟁 상황race condition permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;경쟁 상황(Race Condition)&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Redis-CS-%EA%B4%80%EB%A0%A8-%EC%A7%80%EC%8B%9D%EB%B6%80%ED%84%B0-%EB%9C%AF%EC%96%B4%EB%B3%B4%EB%A9%B0-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-Redis-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0%EC%99%80-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98%EC%9D%84-%EC%82%B4%ED%8E%B4%EB%B3%B4%EC%9E%90&quot;&gt;[Redis] CS 와 함께 뜯어보며 이해하는 Redis : 내부 구조와 동작원리에 대해&lt;/a&gt; 에서도 설명했듯이, 멀티 쓰레드 환경에서는 충분히 경쟁상황이라는 것이 발생할 수 있다고 했습니다. &lt;strong&gt;race condition 이란 동시에 여러개의 프로세스가 하나의 공유자원(데이터)에 대해 접근하여 Read, Write 연산을 진행하면서 발생하는 경쟁 상황을 의미&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;p&gt;그리고 이러한 상황을 동시성 이슈(Concurrency Problem) 이라고 하는 것이죠. 스프링 기반의 웹 애플라케이션은 이러한 동시성 이슈가 발생하지 않도록 별도의 처리가 필요할겁니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;공유 자원 : 여러 프로세스가 공통으로 이용하는 변수, 메모리등을 의미&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;-공동으로-이용되기-때문에-누가-언제-데이터를-읽고-쓰는가에-따라-그-결과값이-달라질-수-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-%EA%B3%B5%EB%8F%99%EC%9C%BC%EB%A1%9C-%EC%9D%B4%EC%9A%A9%EB%90%98%EA%B8%B0-%EB%95%8C%EB%AC%B8%EC%97%90-%EB%88%84%EA%B0%80-%EC%96%B8%EC%A0%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%9D%BD%EA%B3%A0-%EC%93%B0%EB%8A%94%EA%B0%80%EC%97%90-%EB%94%B0%EB%9D%BC-%EA%B7%B8-%EA%B2%B0%EA%B3%BC%EA%B0%92%EC%9D%B4-%EB%8B%AC%EB%9D%BC%EC%A7%88-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot; 공동으로 이용되기 때문에 누가 언제 데이터를 읽고 쓰는가에 따라 그 결과값이 달라질 수 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;=&gt; 공동으로 이용되기 때문에 누가 언제 데이터를 읽고 쓰는가에 따라 그 결과값이 달라질 수 있다.&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;경쟁 상황 : 2개 이상의 프로새스가 공유 자원을 동시에 읽거나 쓰는 상황&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;임계-구역critical-section&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%84%EA%B3%84-%EA%B5%AC%EC%97%ADcritical-section&quot; aria-label=&quot;임계 구역critical section permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;임계 구역(Critical Section)&lt;/h2&gt;
&lt;p&gt;이러한 동시성 이슈, 즉 여러 프로세스의 동기화에 관한 이슈는 임계구역(Critical Section) 영역에서 부터 시작합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1b51610b-ce89-4a5f-b0aa-bbd2b7118217/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;임계구역critical-section&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%84%EA%B3%84%EA%B5%AC%EC%97%ADcritical-section&quot; aria-label=&quot;임계구역critical section permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;임계구역(Critical-Section)&lt;/h3&gt;
&lt;p&gt;임계구역(Critical-Section) 이란 둘 이상의 스레드가 동시에 접근해서는 안 되는 공유 자원에 접근하는 일부 코드내용을 말합니다.&lt;/p&gt;
&lt;p&gt;각 프로세스는 본인만의 임계 구역(Critical Section) 이라는 부분의 코드 영역을 소유하고 있습니다. 한 프로세스에 대한 임계영역에 대해서는 본인(프로세스)만 진입하여 공유자원에 대해 연산을 진행할 수 있는것이 아니라, &lt;strong&gt;다른 프로세스도 임계영역에 진입하여 공유자원에 대해 접근 및 연산이 진행가능합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이떄 중요한것은 한 프로세스가 자신의 임계구역에서 수행하는 동안에는 다른 프로세스들은 그 프로세스에 대한 임계구역이 진입할 수 없다는 사실입니다. &lt;strong&gt;즉, 동시에 두 프로세스는 그들의 임계 구역 안에서 실행할 수 없죠.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;임계구역-문제-critical-section-problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%84%EA%B3%84%EA%B5%AC%EC%97%AD-%EB%AC%B8%EC%A0%9C-critical-section-problem&quot; aria-label=&quot;임계구역 문제 critical section problem permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;임계구역 문제 (Critical-Section Problem)&lt;/h3&gt;
&lt;p&gt;임계 구역 문제는 프로세스들이 데이터를 협력적으로 공유하기 위하여 자신들의 활동을 동기화할 때 사용할 수 있는 프로토콜을 설계하는 것입니다.
각 프로세스는 자신의 임계구역으로 진입하려면 진입 허가를 요청해야 하며, 이러한 요청을 구현하는 코드 부분을 진입 구역(entry section) 이라고 합니다. 또 임계구역 뒤에는 나머지 구역(remainder section)이라고 부릅니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;경쟁 조건은 프로세스가 공유 자원(데이터) 에 대해 동시간대에 접근할 때 race condition 이 발생할 수 있다. 그리고 이러한 경쟁 조건으로 인해 공유 자원의 값이 손실될 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;임계구역은 공유자원에 조작될 수 있으며, 경쟁 조건이 발생할 수 있는 코드 영역입니다.&lt;/li&gt;
&lt;li&gt;임계구역 문제는 데이터를 협력적으로 공유하기 위하여 자신의 호라동을 동기화하는 프로토콜을 설계하는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;synchronized&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#synchronized&quot; aria-label=&quot;synchronized permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;synchronized&lt;/h2&gt;
&lt;p&gt;스프링부트 프레임워크를 기준으로는, 동기화 환경을 제공하도록 &lt;strong&gt;synchronized&lt;/strong&gt; 라는 키워드를 제공합니다. 예를들어 아래와 같이 진행할 경우, 하나의 프로세스 아래에선 여러 쓰레드에 대한 요청을 정상적으로 동기화시켜주죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// @Transactional&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerCourse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;CourseEntity&lt;/span&gt; courseEntity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findCourseEntityByCourseIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isOverFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;마감 되었습니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        courseRespository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;synchronized 를 이용하면, 현재 접근하고 있는 메소드에 하나의 쓰레드만 접근할 수 있도록 보장 및 동작합니다.&lt;/strong&gt; 자바에서 지원하는 synchronized 는 현재 사데이터를 사용하고 있는 해당 쓰레드를 제외하고 나머지 제외 쓰레드들은 데이터 접근을 막아서, 쓰레드 하나씩 순차적으로 데이터에 접근할 수 있도록 해줍니다.&lt;/p&gt;
&lt;p&gt;그러나 문제점은 애플리케이션을 단 하나만 띄운다면 전혀 무관하지만, 서버가 여러대일 경우 여러개의 인스턴스가 존재하는 것과 동일하기 떄문에 실질적인 운영 환경에서는 데이터의 정합성을 보장할 수 없습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;다중 프로세스(서버) 에서는 synchronized 으로도 동시성 이슈를 해결할 수 없다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;분산-락-distributed-lock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B6%84%EC%82%B0-%EB%9D%BD-distributed-lock&quot; aria-label=&quot;분산 락 distributed lock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;분산 락 (Distributed lock)&lt;/h2&gt;
&lt;p&gt;드디어 본격적으로 분산 락에 대해 설명을 드리네요. 분산락이란 &lt;strong&gt;경쟁 상황(Race Condition) 이 발생할때, 하나의 공유자원에 접근할때 데이터에 결함이 발생하지 않도록 원자성(atomic) 을 보장하는 기법&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;분산락을 구현하기 위해서 Redis 는 RedLock 이라는 알고리즘을 제안하며, 3가지 특성을 보장해야 한다고 말하고있죠.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;오직 한 순간에 하나의 작업자만이 락(lock) 을 걸 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;락 이후, 어떠한 문제로 인해 락을 풀지 못하고, 종료된 경우라도 다른 작업자가 락을 획득할 수 있어야합니다.&lt;/li&gt;
&lt;li&gt;Redis 노드가 작동하는한, 모든 작업자는 락을 걸고 해체할 수 있어야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;분산 락을 구현하기 위해서는 락에 대한 정보를 Redis에 저장하고 있어야합니다. 그리고 분산환경에서 여러대의 서버들은 공통된 Redis 를 바라보며, 자신이 임계영역(Critical Section) 에 접근할 수 있는지 확인합니다. 이렇게 분산 환경에서 원자성(atomic) 을 보장할 수 있게되죠.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이번 포스팅에서는 이 분산락이 발생하는 상황을 직접 발생시켜보고, 직접 분산락을 구현하며 어떻게 동시성 이슈를 해결할 수 있는지에 대해 설명하겠습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;동시성-이슈concurrency-issue-상황-가정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88concurrency-issue-%EC%83%81%ED%99%A9-%EA%B0%80%EC%A0%95&quot; aria-label=&quot;동시성 이슈concurrency issue 상황 가정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동시성 이슈(Concurrency Issue) 상황 가정&lt;/h2&gt;
&lt;p&gt;저희는 다음과 같이 동시성 이슈가 발생하는 상황을 직접 만들어볼겁니다.
동시성 이슈가 발생하는 상황은 곧 짧은시간대에 대규모 트래픽(요청)이 오는 경우일 것이고, 대표적으로는 대학교 수강신청을 떠올리 수 있습니다.&lt;/p&gt;
&lt;p&gt;저희는 동시간대에 몰리는 수강신청 상황을 가정해보고, &lt;strong&gt;동일한 자원(수업)에 대해 수강신청하며 발생하는 동시성 문제를 직접 접해볼겁니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;그리고 해당 애플리케이션에서 발생한 문제를 Redis 를 활용한 분산락으로 해결해보는 과정까지 모두 다루어보겠습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;인프라 서버 : 인스턴스가 2대를 띄운 상황을 가정&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;MySQL 에 수업에 대한 더미데이터를 미리 채워놓은 상황. 또 인프라에 Redis 서버를 이미 띄운 상황입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;course-entity&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#course-entity&quot; aria-label=&quot;course entity permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Course Entity&lt;/h3&gt;
&lt;p&gt;우선 수업에 대한 엔티티입니다. 수업명, 수강신청 제한수(countLimit), 현재까지의 수강신청 인원수(currentCount) 로 구성해줬습니다.&lt;/p&gt;
&lt;p&gt;이때 실무과 동일한 정확한 수강신청 서비스를 개발하기 위해선 User 엔티티까지 만드는것이 맞으나, 저희는 수많은 쓰레드가 currentCount 값을 증가시키는 것만 간단히 확인하도록 하겠습니다. (이게 핵심은 아니므로)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Course&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@AllArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@NoArgsConstructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;access &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AccessLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PROTECTED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Setter&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Builder&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseEntity&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@GeneratedValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenerationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; courseName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; countLimit&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; currentCount&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isOverFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; currentCount &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; countLimit&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;coursecontroller&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#coursecontroller&quot; aria-label=&quot;coursecontroller permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CourseController&lt;/h3&gt;
&lt;p&gt;컨트롤러에서는 아래와 같이 수업에 대한 Id 값, 즉 수업에 대한 ID 값을 입력받고 증가하는 것에 대해 처리해두었습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/course&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt; courseService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt; courseService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;courseService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ResponseBody&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@PostMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/registration/{courseIdx}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerCourse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@PathVariable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;courseIdx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt; registerRes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerCourse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;created&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;URI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/course/registration/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;registerRes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;enablejparepositories&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#enablejparepositories&quot; aria-label=&quot;enablejparepositories permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EnableJpaRepositories&lt;/h3&gt;
&lt;p&gt;레포지토리는 아래와 같이 구성했으나, findCourseEntityByCourseIdx 메소드의 경우는 Spring Data JPA 에서 제공해주는 findById() 메소드를 사용해도 전혀 무관합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@EnableJpaRepositories&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseRespository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JpaRepository&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CourseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// @Lock(LockModeType.PESSIMISTIC_WRITE)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;CourseEntity&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findCourseEntityByCourseIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;courseservice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#courseservice&quot; aria-label=&quot;courseservice permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CourseService&lt;/h3&gt;
&lt;p&gt;서비스단입니다. 이때 앞서 CourseEntity 에서 정의한 isOverFlow 메소드를 통해 수강신청 인원이 초과했는지를 검증합니다. 또 @Transactional 어노테이션으로 트랜잭션으로 연산 단위를 묶었다는 점 인지하고 넘어갑시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseRespository&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisLockRepository&lt;/span&gt; redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CourseRespository&lt;/span&gt;  courseRespository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisLockRepository&lt;/span&gt; redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;courseRespository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisLockRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Transactional&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerCourse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;CourseEntity&lt;/span&gt; courseEntity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findCourseEntityByCourseIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isOverFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;마감 되었습니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            courseRespository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;쓰레드-100개-생성-및-동시성-요청&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%93%B0%EB%A0%88%EB%93%9C-100%EA%B0%9C-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9A%94%EC%B2%AD&quot; aria-label=&quot;쓰레드 100개 생성 및 동시성 요청 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쓰레드 100개 생성 및 동시성 요청&lt;/h2&gt;
&lt;p&gt;Jmeter 로 동시간대에 100개의 요청을 생성하고, 동시간대에 요청을 보내도록 했습니다. 혹시 Jmeter 를 사용하는 방법이 궁금하신 분들은 &lt;a href=&quot;https://velog.io/@rkfksh/JMeter-%EB%AC%B4%EC%9E%91%EC%A0%95-%EB%94%B0%EB%9D%BC%ED%95%B4%EB%B3%B4%EA%B8%B0Windows&quot;&gt;JMeter 무작정 따라해보기(Windows)&lt;/a&gt; 와 &lt;a href=&quot;https://effortguy.tistory.com/164&quot;&gt;[Spring] JMeter 사용법 - JMeter란?, 테스트 방법&lt;/a&gt; 를 참고하시면 되겠습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;open&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;opt&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;homebrew&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;bin&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Jmeter&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// Jmeter 실행&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;RDB 에 저장해둔 수업 목록은 다음과 같고, 저희는 ID 값이 4인 자료구조 수업에 대해 쓰레드 100개를 생성하고 동시간대에 요청을 보내도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/bf802182-5e8c-45d2-8c68-25b4362eca13/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Jmeter 에서 보낸 요청 환경구성은 아래와 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/954cc065-2ce1-4592-9106-1090366e8be5/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;동시성-이슈-발생&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88-%EB%B0%9C%EC%83%9D&quot; aria-label=&quot;동시성 이슈 발생 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동시성 이슈 발생&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/180aad55-80ea-4daa-93a2-40ae1b395491/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;분명 100개의 요청을 보냈음에도 불구하고, currentCount 의 값이 100이 아닌 19밖에 되지 않았습니다. Race Condition 으로 인해 동기화 문제가 발생한 것입니다. 즉 동시성 이슈가 발생한 것이죠.
앞서 언급했듯이, 이 문제는 단일 서버가 아니기 때문에 synchornized 로도 해결할 수 없습니다. 두 애플리케이션은 각각 별개의 프로세스로 동작하고 있기 때문입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;락lock에-대해&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9D%BDlock%EC%97%90-%EB%8C%80%ED%95%B4&quot; aria-label=&quot;락lock에 대해 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;락(Lock)에 대해&lt;/h2&gt;
&lt;p&gt;저희는 Redis Client 중에 Lettuce 로 발생한 동시성 이슈를 해결해볼겁니다. 그전에 락과 관련한 이론들에 대해 다시 짚고 넘어갑시다.&lt;/p&gt;
&lt;h3 id=&quot;락lock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%9D%BDlock&quot; aria-label=&quot;락lock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;락(Lock)&lt;/h3&gt;
&lt;p&gt;락이란 데이터베이스에서 사용하는 개념으로, 트랜잭션 처리의 순차성을 보장하기 위한 기법 중 하나입니다. 트랜잭션이란 DB의 나누어지지 않는 최소한의 처리 단위이며, 이러한 DB의 특징을 atomic 하다고 하는데, 쉽게말해 한번에 하나의 행동이 되는 것을 보장한다는 의미입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;락을 획득한다는 것은 자원을 사용해도 된다는 의미이며,&lt;/strong&gt; 다른 프로세스는 현재 락을 획득한 프로세스가 잠금을 건 자원에 대해 사용할 수 없음을 의미합니다. 이런식으로 순차성으로 보장하는 것이죠/&lt;/p&gt;
&lt;h3 id=&quot;스핀-락spin-lock&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%95%80-%EB%9D%BDspin-lock&quot; aria-label=&quot;스핀 락spin lock permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스핀 락(Spin Lock)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;락을 걸지 못하면 무한루프를 돌면서 계속 락을 얻으려고 시도하는 동기화 기법입니다.&lt;/strong&gt; 락을 얻지 못할경우, 쉬지않고 계속 락을 얻으려고 시도합니다. 이 떄문에 락을 얻을때가지 계속 요청을 보내며 대기하므로 서버에 많은 부하를 주죠.&lt;/p&gt;
&lt;h3 id=&quot;redis-client&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-client&quot; aria-label=&quot;redis client permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis Client&lt;/h3&gt;
&lt;p&gt;스핀락은 서버에 많은 부하를 주죠. Redis Client 인 Redisson, Lettuce 은 스핀 락을 적절한 주기로 적당량을 보내면서, 서버에 가는 부하를 줄이는 방식입니다.&lt;/p&gt;
&lt;p&gt;이는 서버 측에서 구독한 클라이언트에게 &quot;락을 사용해도 된다&quot; 라고 알림을 주어서, 락의 획득 여부를 일일이 클라이언트가 요청해서 확인하지 않아도 되게 하는 기법입니다. 이로써 순수 스핀락을 구현해서 락을 얻어내는 방식보다 부하를 훨씬 줄이는 셈이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;lettuce--setnx-를-활용한-분산락-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lettuce--setnx-%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%B6%84%EC%82%B0%EB%9D%BD-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;lettuce  setnx 를 활용한 분산락 구현 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Lettuce &amp;#x26; SETNX 를 활용한 분산락 구현&lt;/h2&gt;
&lt;p&gt;분산락을 해결하기 위해서 저희는 Redis Client로는 Lettuce를 활용하겠습니다. build.gradle 의 dependencies 에 아래를 추가해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;implementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;cache&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;지금부터 살펴본 방식은 스핀락을 적절히 활용해서 쓰레드들이 순차적으로 락을 획득하는 방식입니다.
락을 획득한다는 것은 &quot;락이 존재하지는지 확인한다&quot;, &quot;존재하지 않는다면 락을 획득한다&quot; 이 두 연산이 atomic 하게 이루어 져야 합니다. 레디스는 &quot;값이 존재하지 않으면 세팅한다&quot; 라는 SETNX 명령어를 지원합니다. 이 SETNX 를 이용하여 레디스에 값이 존재하지 않으면 세팅하게 하고, 값이 세팅 되었는지 여부를 리턴 값으로 받아 락을 획득하는데 성공합니다.&lt;/p&gt;
&lt;p&gt;이 방식을 통해 애플리케이션에서 스핀 락(spin lock)을 구현할 수 있습니다. 즉, 레디스 서버에 지속적으로 SETNX 명령을 보내어 임계 영역(Critical Section) 진입 여부를 확인하는 기법이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;redisrepository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redisrepository&quot; aria-label=&quot;redisrepository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RedisRepository&lt;/h3&gt;
&lt;p&gt;앞서 Redis Client 로 Lettuce 에 대한 의존성을 주입해줬다면, redisTemplate 를 활용해서 락을 관리하는 스프링 빈을 구현합시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisLockRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisLockRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// setIfAbsent() 를 활용해서 SETNX를 실행함. 이때 key는 Course Entity 에 대한 ID값으로, Value 를 &quot;lock&quot; 으로 설정한다.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opsForValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setIfAbsent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lock&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ofMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; redisTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;courseservice-성능-개선&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#courseservice-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0&quot; aria-label=&quot;courseservice 성능 개선 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CourseService 성능 개선&lt;/h3&gt;
&lt;p&gt;그리고 기존 CourseService 코드를 개선해서 분산 락을 적용해줍시다. while 문이 없었다면 스핀락 방식으로써 서버에 부하를 주었겠지만, 앞서 구현한 RedisLockRepository 를 활용해서 while 문을 활용해서 락을 획득할때마다 무한 반복을 돌도록 구현했습니다.
(몰론 스핀 락 방식을 어느정도 활용해서 서버에 조금은 부하가 가긴합니다.)&lt;/p&gt;
&lt;p&gt;Redis 서버에 부하를 덜기위해 100ms 를 쉬어주고, Critical Section 에 진입후 수강신청에 대한 로직을 처리후 finally 블럭으로 락을 해제해줍니다. 이때 락을 해제해주지 않으면 다른 쓰레드에서 Critical Section 에 진입하므로 주의해줍시다.&lt;/p&gt;
&lt;p&gt;또한 @Transactional 어노테이션을 제거한 모습을 볼 수 있습니다. Redis Client 는 @Transcational과 동시에 동작하지 않기 때문입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseRespository&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisLockRepository&lt;/span&gt; redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CourseRespository&lt;/span&gt;  courseRespository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedisLockRepository&lt;/span&gt; redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;courseRespository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisLockRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerCourse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;CourseEntity&lt;/span&gt; courseEntity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findCourseEntityByCourseIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isOverFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;마감 되었습니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            courseRespository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            redisLockRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;실행결과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%A4%ED%96%89%EA%B2%B0%EA%B3%BC&quot; aria-label=&quot;실행결과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;실행결과&lt;/h3&gt;
&lt;p&gt;이렇게 분산 락을 적용시 정상적으로 동시성 이슈가 해결된 모습을 볼 수 있습니다!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/46079805-461c-4be9-b434-729313e65663/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;redisson&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redisson&quot; aria-label=&quot;redisson permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redisson&lt;/h2&gt;
&lt;p&gt;다음으로는 Redisson 를 활용해서 구현해보겠습니다. 이 방식은 앞선 Lettuce 를 활용한것과 달리, 스핀 락 방식을 전혀 활용한 방식을 직접 구현하지 않아도됩니다. 그저 의존성을 추가하고 간단히 적용하면 끝이죠.&lt;/p&gt;
&lt;p&gt;Redisson은 pubsub 기능을 사용하여 스핀 락이 레디스에 주는 엄청난 트래픽을 줄였습니다. &lt;strong&gt;락이 해제될 때마다 subscribe하는 클라이언트들에게 “너네는 이제 락 획득을 시도해도 된다” 라는 알림을 주어서 일일이 레디스에 요청을 보내 락의 획득가능여부를 체크하지 않아도 되도록 개선&lt;/strong&gt;했습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;implementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redisson&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;redisson&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.17&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.7&lt;/span&gt;&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;courseservice-리팩토링&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#courseservice-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81&quot; aria-label=&quot;courseservice 리팩토링 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CourseService 리팩토링&lt;/h3&gt;
&lt;p&gt;RedissonClient 의 getLock() 으로 락을 획득하고, tryLock() 을 통해 락 획득을 시도합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CourseRespository&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RedissonClient&lt;/span&gt; redissonClient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;courseRespository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redissonClient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redissonClient&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerCourse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Long&lt;/span&gt; courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RLock&lt;/span&gt; lock &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; redissonClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; available &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tryLock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TimeUnit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SECONDS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; newCourseIdx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toIntExact&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;available&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Lock 획득 실패!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// 비즈니스 로직&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;CourseEntity&lt;/span&gt; courseEntity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; courseRespository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findCourseEntityByCourseIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newCourseIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isOverFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;마감 되었습니다&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            courseRespository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegisterRes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;courseEntity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InterruptedException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            lock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이로써 Redis 를 통한 분산락 구현방법을 알아보고, 이 방법으로 동시성 트래픽을 오차없이 처리하는 방법에 대해 알아봤습니다.
추후에는 데이터베이스 락, 낙관적/비관적 락등 다양한 이슈와 기법에 대해 다루어볼까합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://dkswnkk.tistory.com/681&quot;&gt;자바에서 동시성을 해결하는 다양한 방법과 Redis의 분산락&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://hyos-dev-log.tistory.com/34&quot;&gt;분산 락을 사용하여, 동시성 문제 해결하기&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://hudi.blog/distributed-lock-with-redis/&quot;&gt;Redis로 분산 락을 구현해 동시성 이슈를 해결해보자!&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://it-hhhj2.tistory.com/102&quot;&gt;redis 설치 및 redisson을 이용한 분산락 구현&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://dkswnkk.tistory.com/407&quot;&gt;[OS] 프로세스 동기화(Process Synchronization)&lt;/a&gt;
&lt;a href=&quot;https://way-be-developer.tistory.com/m/274&quot;&gt;락이란? 분산락, 스핀락의 개념&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://hyperconnect.github.io/2019/11/15/redis-distributed-lock-1.html&quot;&gt;레디스와 분산 락(1/2) - 레디스를 활용한 분산 락과 안전하고 빠른 락의 구현&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[&lt;a href=&quot;https://velog.io/@znftm97/%EB%8F%99%EC%8B%9C%EC%84%B1-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0-V3-%EB%B6%84%EC%82%B0-DB-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EB%B6%84%EC%82%B0-%EB%9D%BDDistributed-Lock-%ED%99%9C%EC%9A%A9&quot;&gt;동시성 문제 해결하기 V3 - 분산 DB 환경에서 분산 락(Distributed Lock) 활용&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://soyeon207.github.io/db/2021/08/29/distributed-lock.html&quot;&gt;분산락 (Distributed lock)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://isntyet.github.io/jpa/JPA-%EB%B9%84%EA%B4%80%EC%A0%81-%EC%9E%A0%EA%B8%88(Pessimistic-Lock)/&quot;&gt;JPA 비관적 잠금(Pessimistic Lock)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://galid1.tistory.com/790&quot;&gt;JPA 잠금기법&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Restful 한 API 설계원칙에 대해]]></title><description><![CDATA[시작에 앞서 : 왜 학습을 진행하게 되었는가? 소규모 프로젝트르 진행하면서, 프론트 팀원와 제가 만든 API를 axios 를 연동하던 중에 API 가 Restful 하지 않다라는 피드백이 있었습니다. 제가 지금까지 만든 API 들은 모두 REST…]]></description><link>https://haon.site/haon/server/restful/</link><guid isPermaLink="false">https://haon.site/haon/server/restful/</guid><pubDate>Mon, 27 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;시작에-앞서--왜-학습을-진행하게-되었는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%9E%91%EC%97%90-%EC%95%9E%EC%84%9C--%EC%99%9C-%ED%95%99%EC%8A%B5%EC%9D%84-%EC%A7%84%ED%96%89%ED%95%98%EA%B2%8C-%EB%90%98%EC%97%88%EB%8A%94%EA%B0%80&quot; aria-label=&quot;시작에 앞서  왜 학습을 진행하게 되었는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시작에 앞서 : 왜 학습을 진행하게 되었는가?&lt;/h2&gt;
&lt;p&gt;소규모 프로젝트르 진행하면서, 프론트 팀원와 제가 만든 API를 axios 를 연동하던 중에 API 가 Restful 하지 않다라는 피드백이 있었습니다. 제가 지금까지 만든 API 들은 모두 REST API 한 인줄 알았으나, 오해하고 있었던 것입니다.&lt;/p&gt;
&lt;p&gt;이번 기회에 RESTful 한 API 에 대해서 다시 학습을 진행해보고자 이렇게 포스팅을 다루게 되었습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;rest의-의미&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rest%EC%9D%98-%EC%9D%98%EB%AF%B8&quot; aria-label=&quot;rest의 의미 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;REST의 의미&lt;/h2&gt;
&lt;p&gt;어떻게 하면 RESTful 하게 자원을 명시하고 주고받는 방법을 API 로써 구현할 수 있을까요? 이를 위해, 저희는 다시 REST 의 의미에 대해 되짚고 갈 필요가 있습니다.&lt;/p&gt;
&lt;p&gt;REST(Representational State Transfer) 란 서버와 클라이언트간의 통신방식 중 하나로, &lt;strong&gt;자원의 이름을 구분하고 자원의 상태를 주고받는 통신 방식&lt;/strong&gt;입니다.
즉, 클라이언트와 서버가 데이터를 주고받는 방식에 대해 정리한 원칙들이 있고, 그 원칙을 기반으로하는 아키텍처 스타일을 REST 라고 하는 것이죠.&lt;/p&gt;
&lt;p&gt;REST 는 다음과 같은 2가지 특징을 지닙니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;자원을 이름을 구분하고,&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;자원의 상태를 주고받는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;uri-에-리소스를-표현하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#uri-%EC%97%90-%EB%A6%AC%EC%86%8C%EC%8A%A4%EB%A5%BC-%ED%91%9C%ED%98%84%ED%95%98%EA%B8%B0&quot; aria-label=&quot;uri 에 리소스를 표현하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;URI 에 리소스를 표현하기&lt;/h2&gt;
&lt;p&gt;저희는 아래와 같은 URI 만 보고도 과정들 중에 프론트엔드에 대한 정보, 백엔드에 대한 정보, 프론트엔드 과정에 있는 인원들 중에 연락처 정보라는것을 어느정도 예측할 수 있습니다.&lt;/p&gt;
&lt;p&gt;이처럼 리소스를 URI 에 표현해서, 주고받을 정보에 대해 어느정도 예측을 할 수 있는 것이죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;course&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;front       &lt;span class=&quot;token comment&quot;&gt;// 프론트엔드에 대한 정보&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;course&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;back        &lt;span class=&quot;token comment&quot;&gt;// 백엔드에 대한 정보&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;course&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;front&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;people&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;phone     &lt;span class=&quot;token comment&quot;&gt;// 프론트엔드 과정에 있는 인원들 중에 연락처 정보&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;collection-document&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#collection-document&quot; aria-label=&quot;collection document permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Collection, Document&lt;/h2&gt;
&lt;p&gt;그러면 REST API 에서 URI 는 어떤 구조를 가질까요? 이 URI 의 구조는 크게 Collection, Document 라는 단위로 나뉘어집니다.
예를들어 아래와 테이블(데이터베이스) 객체를 생성하는 API 를 개발하고 싶을때, URI 어떻게 생성할까요?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/dfd74965-8e26-4e89-a31c-f23d0c0e2ccf/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그것은 바로 앞서 살펴본 URI 의 형태로 정의하면됩니다. 그리고 그 URI 는 Collection 과 Document 라는 단위로 구성되는 것이죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;course&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;front       &lt;span class=&quot;token comment&quot;&gt;// 프론트엔드에 대한 정보&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;course&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;back        &lt;span class=&quot;token comment&quot;&gt;// 백엔드에 대한 정보&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;course&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;front&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;people&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;phone     &lt;span class=&quot;token comment&quot;&gt;// 프론트엔드 과정에 있는 인원들 중에 연락처 정보&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이때 테이블 전체에 해당하는 부분을 Collection, 행 하나의 부분 혹은 객체를 Document 라고 합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4763c55d-2c97-44e0-ad43-88d328532441/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Collection 은 일반적으로 객체들의 집합이기 때문에 복수명사를 사용합니다.&lt;/li&gt;
&lt;li&gt;또 Document는 이 집합들 중 객체를 구분할 수 있는 값을 의미하죠. 보통은 id 컬럼을 많이 사용합니다. (여기서는 title 컬럼을 Document 로 사용했습니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이러한 데이터베이스 테이블의 컬럼 및 객체 단위들을 Collection, Document 라고하며 URI 는 이러한 Collection 과 Document 의 조합으로 이루어져 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;course&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;front   &lt;span class=&quot;token comment&quot;&gt;// =&gt; &quot;course&quot; 부분이 Collection&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;course&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;back
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;course&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;front&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;people&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;phone   &lt;span class=&quot;token comment&quot;&gt;// &quot;front&quot;, &quot;back&quot; 부분이 Document&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1-리소스를-이름으로-구분하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EB%A6%AC%EC%86%8C%EC%8A%A4%EB%A5%BC-%EC%9D%B4%EB%A6%84%EC%9C%BC%EB%A1%9C-%EA%B5%AC%EB%B6%84%ED%95%98%EA%B8%B0&quot; aria-label=&quot;1 리소스를 이름으로 구분하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 리소스를 이름으로 구분하기&lt;/h2&gt;
&lt;h3 id=&quot;uri&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#uri&quot; aria-label=&quot;uri permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;URI&lt;/h3&gt;
&lt;p&gt;이제 본격적으로 REST 로 자원을 표현하는 방법에 대해 알아봅시다. REST 에서는 자원을 표현할때 URI 라는 방식으로 표현하죠? URI 는 REST 에서 자원을 구분하고 처리하기 위해 사용되며 URI 를 잘 네이밍할수록 API 가 직관적이고 사용하기 쉽습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;books
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;customers&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 예시처럼 URI 가 명시되어 있다면, 이게 책에 관한 API 이구나, 또는 이게 고객에 관한 API 이구나라고 쉽게 식별할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;singleton-and-collction-resources&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#singleton-and-collction-resources&quot; aria-label=&quot;singleton and collction resources permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Singleton and Collction Resources&lt;/h3&gt;
&lt;p&gt;다음으로 URI 는 Singleton 이나 Collection 으로 표현한다는 것입니다.
Singleton 이란 /customer 와 같이 단수로 표현된 자원을 Singleton 이라고하고, Collection 은 /customers 와 같이 복수로 표현된 자원을 의미합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;customer    &lt;span class=&quot;token comment&quot;&gt;// Singleton&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;customers   &lt;span class=&quot;token comment&quot;&gt;// Collection&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;collection-and-sub-collection-resources&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#collection-and-sub-collection-resources&quot; aria-label=&quot;collection and sub collection resources permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Collection and Sub-collection Resources&lt;/h3&gt;
&lt;p&gt;또 URI 는 Sub-Collection 을 포함할 수 있습니다. 만일 특정 고객의 계좌를 찾는 API 를 설계하고 싶다면 /customers 하위에 accounts 라는 또 다른 collection 을 두어서, 특정 고객의 계좌를 명시할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;customers&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;customerIdx&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;accounts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;uri-네이밍-규칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#uri-%EB%84%A4%EC%9D%B4%EB%B0%8D-%EA%B7%9C%EC%B9%99&quot; aria-label=&quot;uri 네이밍 규칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;URI 네이밍 규칙&lt;/h2&gt;
&lt;p&gt;지금부터 URI 네이밍 규칙에 대해 알아봅시다.&lt;/p&gt;
&lt;h3 id=&quot;1-명사를-사용하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EB%AA%85%EC%82%AC%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%9E%90&quot; aria-label=&quot;1 명사를 사용하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 명사를 사용하자&lt;/h3&gt;
&lt;p&gt;첫번째로 명사를 사용해서 자원을 표현합시다. 예를들어 사람들에 대한 정보를 표현하고 싶다면 /people 이라고 표현해서 표현하면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;people&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;몰론 예외적으로 명사외에 동사를 허용하는 경우가 있는데, 바로 컨트롤러 역할을 하는 경우입니다. 예를들어 /game/play 에 접근하면 게임이 시작되는 URI 가 있다고하면 이것은 게임의 시작여부를 컨트롤하는 URI 이므로, 이를 동사 play 로 표현할 수 있는 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;game&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;play&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-계층관계를-구분짓기-위해-슬래시--를-사용하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EA%B3%84%EC%B8%B5%EA%B4%80%EA%B3%84%EB%A5%BC-%EA%B5%AC%EB%B6%84%EC%A7%93%EA%B8%B0-%EC%9C%84%ED%95%B4-%EC%8A%AC%EB%9E%98%EC%8B%9C--%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%9E%90&quot; aria-label=&quot;2 계층관계를 구분짓기 위해 슬래시  를 사용하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 계층관계를 구분짓기 위해 슬래시 / 를 사용하자&lt;/h3&gt;
&lt;p&gt;다음으로 자원간에 계층관계를 표현하기 위해서 / (슬래시) 를 사용합시다.
예를들어 상품 중에 3번 상품을 보여주고 싶은경우, /products/3 이렇게 표현할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;products&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-uri-마지막에-슬래시를-붙이지말자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-uri-%EB%A7%88%EC%A7%80%EB%A7%89%EC%97%90-%EC%8A%AC%EB%9E%98%EC%8B%9C%EB%A5%BC-%EB%B6%99%EC%9D%B4%EC%A7%80%EB%A7%90%EC%9E%90&quot; aria-label=&quot;3 uri 마지막에 슬래시를 붙이지말자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. URI 마지막에 슬래시를 붙이지말자&lt;/h3&gt;
&lt;p&gt;말그대로 입니다. URI 마지막에 슬래시를 붙이지맙시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;products      &lt;span class=&quot;token comment&quot;&gt;// 올바른 표현&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;products&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// 잘못된 표현&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-하이픈---기호를-사용해-가독성을-높이자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-%ED%95%98%EC%9D%B4%ED%94%88---%EA%B8%B0%ED%98%B8%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-%EA%B0%80%EB%8F%85%EC%84%B1%EC%9D%84-%EB%86%92%EC%9D%B4%EC%9E%90&quot; aria-label=&quot;4 하이픈   기호를 사용해 가독성을 높이자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 하이픈 - 기호를 사용해 가독성을 높이자&lt;/h3&gt;
&lt;p&gt;다음으로 하이폰 (-) 기호를 사용해서 URI 의 가독성을 높일 수 있습니다. 아래처럼 무식하게 일렬로 나열하는 방식은 좋지 않습니다. 또 카멜 케이스를 사용하는 것도 가독성에 있어서 조금 아쉽죠. 맨 마지막에 하이픈 (-) 기호를 붙인 URI 와 비교해보면 훨씬 가독성이 좋아졌습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;profilemanagement   &lt;span class=&quot;token comment&quot;&gt;// 그냥 일렬로 나열한 방식&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;profileManagement   &lt;span class=&quot;token comment&quot;&gt;// 카멜 케이스를 사용한 방식&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;profile&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;management  &lt;span class=&quot;token comment&quot;&gt;// 하이픈 기호를 사용한 방식&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-언더스코어를-사용하지-말자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-%EC%96%B8%EB%8D%94%EC%8A%A4%EC%BD%94%EC%96%B4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EB%A7%90%EC%9E%90&quot; aria-label=&quot;5 언더스코어를 사용하지 말자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. 언더스코어를 사용하지 말자&lt;/h3&gt;
&lt;p&gt;또 URI 는 가급적 언더스코어, 즉 밑줄( _ ) 을 사용하지 맙시다. 왜냐하면 일부 브라우저나 화면에서 글꼴에 따라 언더스크어 문자가 가려지거나 숨겨질 수 있기 때문입니다.&lt;/p&gt;
&lt;p&gt;따라서 /backend_people 이렇게 표현하기보다는 /people/backend 이렇게 표현하는 것이 더 좋습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;backend_people   &lt;span class=&quot;token comment&quot;&gt;// X&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;people&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;backend   &lt;span class=&quot;token comment&quot;&gt;// O&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;6-소문자만을-사용하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-%EC%86%8C%EB%AC%B8%EC%9E%90%EB%A7%8C%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EC%9E%90&quot; aria-label=&quot;6 소문자만을 사용하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. 소문자만을 사용하자&lt;/h3&gt;
&lt;p&gt;URI 는 또 대문자가 아닌, 소문자만을 사용해야합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PLAYERS&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// X&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;players&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;// O&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2-http-메소드를-활용하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-http-%EB%A9%94%EC%86%8C%EB%93%9C%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0&quot; aria-label=&quot;2 http 메소드를 활용하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. HTTP 메소드를 활용하기&lt;/h2&gt;
&lt;p&gt;다음으로는 HTTP 메소드를 어떻게 활용하면 좋을지에 대해 알아봅시다. 만약에 유저 리스트를 받아오고 싶다면 어떻게 할까요? 아까 말했던 URI 네이밍 규칙에서는 CRUD 함수의 이름을 URI 에 사용하지 말라고 했었습니다.
그렇다면 /getUsers 나, 아니면 유저 리스트에 등록하고 싶을때 /postUsers 라는걸 사용해선 안되겠죠?&lt;/p&gt;
&lt;p&gt;이를 해결하도록 HTTP 메소드(GET, POST, PUT, PATCH, DELETE) 등을 사용하면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;users &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;GET&lt;/span&gt; 메소드   &lt;span class=&quot;token comment&quot;&gt;// 유저 리스트를 얻어오고 싶은경우&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;users &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;POST&lt;/span&gt; 메소드  &lt;span class=&quot;token comment&quot;&gt;// 유저 리스트에 신규 유저를 등록하고 싶은경우&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;users &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PUT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PATCH&lt;/span&gt; 메소드  &lt;span class=&quot;token comment&quot;&gt;// 유저 리스트를 수정하고 싶은경우&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;users &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DELETE&lt;/span&gt; 메소드    &lt;span class=&quot;token comment&quot;&gt;// 유저 리스트에서 특정 유저를 제거하고 싶은경우&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;표현의-어려움&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%91%9C%ED%98%84%EC%9D%98-%EC%96%B4%EB%A0%A4%EC%9B%80&quot; aria-label=&quot;표현의 어려움 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;표현의 어려움&lt;/h2&gt;
&lt;p&gt;하지만 이러한 4가지 방식만으로, URI 만으로 모든거을 표현하는데는 어려움이있습니다. 예를들어 로그인과 로그아웃이 있는데, 로그인을 하는거니까 Collection 을 유저로 사용하면 되나? 그리고 아이디와 패스워드가 필요하니까 이런 내용을 body 에 표현해서 POST 요청을 보내면 된다? 라는 생각을 했었는데 이러한 표현은 꼭 회원을 추가하는 회원가입처럼 보이더라구요.&lt;/p&gt;
&lt;p&gt;그래서 이 부분은 /login, /logout 으로 예외적으로 처리했던 기억이납니다.
그러면 저의 API 를 틀린것일까요? REST API 의 구조에 어긋나기 때문에 동작하지 않을까요?&lt;/p&gt;
&lt;p&gt;초반부에 말했던것처럼 REST는 아키텍처 스타일이다. 그렇기 때문에 &lt;strong&gt;REST 의 모든 원칙들을 지키지 않는다고 해서 API 가 틀린것은 아닙니다.&lt;/strong&gt; 이번 포스팅을 준비하면서 REST 에 대해 찾아보면서 처음에 말했던것처럼, 이건 RESTful 하지않아, 이건 Restful 이 아니야 같은 RESTful 에 대한 논의를 많이 볼 수 있었죠. 몰론 REST 에는 명확한 원칙이 있고 이를 모두 준수한다면 좋겠지만 그게 조금 어렵다는 것입니다.&lt;/p&gt;
&lt;p&gt;REST 의 원칙을 지키기 위해서 고민하는 것은 몰론 좋은 고민일것이에요. 하지만 저희처럼 공부하는 단계에서는 REST 아키텍처의 장점을 활용하면서 API 를 설계하는 연습을 하는것도 충분힐 좋은 경험이 될 것이라고 생각합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금까지 RESTful 한 API, 즉 REST API 를 어떻게 설계할지에 대해 알아봤습니다. REST 라는것이 말씀드렸듯이, REST 의 모든 원칙들을 지키지 않는다고 해서 API 가 틀린것은 아닙니다. 따라서 API 설계를 할때 반드시 지킬 필요는 없으나, 아키텍처를 학습하는 입장에서는 충분히 장점을 활용해보면서 API 를 설계해보는 연습을 해봅시다!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Redis란 무엇이고, 어떤 원리로 동작하는걸까?]]></title><description><![CDATA[시작에 앞서 매번 Redis 를 깊이있게 학습해야겠다 다짐만 하다가 드디어 학습을 시작하게 되었네요! Redis 의 내부구조부터 동작원리까지 자세하게 소개해볼까 합니다. Redis 란? Redis 란 key-value…]]></description><link>https://haon.site/haon/redis/concept/</link><guid isPermaLink="false">https://haon.site/haon/redis/concept/</guid><pubDate>Mon, 20 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;hr&gt;
&lt;h2 id=&quot;시작에-앞서&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%9E%91%EC%97%90-%EC%95%9E%EC%84%9C&quot; aria-label=&quot;시작에 앞서 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시작에 앞서&lt;/h2&gt;
&lt;p&gt;매번 Redis 를 깊이있게 학습해야겠다 다짐만 하다가 드디어 학습을 시작하게 되었네요! Redis 의 내부구조부터 동작원리까지 자세하게 소개해볼까 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;redis-란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-%EB%9E%80&quot; aria-label=&quot;redis 란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis 란?&lt;/h2&gt;
&lt;p&gt;Redis 란 key-value 구조의 비정형 데이터를 저장하고 관리하기 위한 비관계형 데이터베이스 관리 시스템(DBMS) 입니다.&lt;/p&gt;
&lt;p&gt;데이터베이스, 캐시, 메시지 브로커로 사용되며 인메모리 데이터 구조를 가진 저장소이죠. 오픈소스로써 NoSQL 로 분류되기도 합니다.&lt;/p&gt;
&lt;p&gt;또 Redis 는 Remote Dictionary Server 의 약자로써 외부에서 사용 가능한 key-value 쌍의 해시 맵 형태의 서버라고 생각할 수 있습니다. 따라서 &lt;strong&gt;별도의 쿼리문 작성없이 key값으로 빠르게 결과를 가져올 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Remote : 외부&lt;/li&gt;
&lt;li&gt;Dicitionary : 딕셔너리 자료구조&lt;/li&gt;
&lt;li&gt;Server : 서버
=&gt;딕셔너리 자료구조 형태를 사용하는 외부에 존재하는 서버&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;cache-memory-hierarchy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cache-memory-hierarchy&quot; aria-label=&quot;cache memory hierarchy permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Cache, Memory Hierarchy&lt;/h2&gt;
&lt;p&gt;이제 본격적으로 Redis 에 대해 자세히 살펴봅시다. &lt;strong&gt;우선 Redis 는 In-memory Database 로써 캐시(Cache) 영역에 존재하는 데이터베이스입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;memory-hierarchy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#memory-hierarchy&quot; aria-label=&quot;memory hierarchy permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Memory Hierarchy&lt;/h3&gt;
&lt;p&gt;캐시(Cache) 라는 영역이 무엇인지 이해하려면 우선 메모리 계층에 대해 알아야합니다. 메모리는 아래처럼 크게 4자기의 메모리 계층구조로 이루어져있죠.&lt;br&gt;
아래 계층구조를 보면 각 계층의 메모리는 위로 갈수록 빠르고 비싸며, 아래로 갈수록 느려지고 저렴한 메모리 저장소라는 특징이 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/621df5db-becf-4aad-a9e0-4db97fb3d341/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Main Memory(DRAM) : 비교적 빠르고 적당한 크기 + 휘발성&lt;/li&gt;
&lt;li&gt;Disk(SSD, HDD) : 비교적 느리고 엄청 큰 크기 + 비휘발성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;cache-in-memory-database&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cache-in-memory-database&quot; aria-label=&quot;cache in memory database permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Cache (In-memory Database)&lt;/h3&gt;
&lt;p&gt;기본적으로 데이터베익스는 컴퓨터가 꺼지더라도 데이터를 저장해야하므로 SSD(디스크) 에 데이터를 저장합니다. 즉 데이터베이스의 데이터에 접근하려면 시간이 조금 느린것이죠.&lt;/p&gt;
&lt;p&gt;이때 &lt;strong&gt;데이터베이스 보다는 더 자주 접근하고 덜 자주 바뀌는 데이터를 Memory(메모리) 상에 저장해서 빠르고 쉽게 접근하자는 개념에서 등장한 것이 바로 In-memory Database(Cache) 인 Redis&lt;/strong&gt; 입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;redis-의-필요성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1&quot; aria-label=&quot;redis 의 필요성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis 의 필요성&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; redis &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 자바에서 이런 HashMap 을 데이터베이스로 사용해서 저장해도 memory 데이터베이스이지 않을까요? 따라서 Redis 가 아닌 자바의 HashMap 을 사용해도 되지 않나라는 생각이 들 수 있습니다.&lt;/p&gt;
&lt;p&gt;그러나 자바를 데이터베이스로 택했을때 다음과 같은 문제점이 발생합니다.&lt;/p&gt;
&lt;h3 id=&quot;consistency-일관성-문제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#consistency-%EC%9D%BC%EA%B4%80%EC%84%B1-%EB%AC%B8%EC%A0%9C&quot; aria-label=&quot;consistency 일관성 문제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Consistency (일관성) 문제&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a2ff7e7d-9b43-4fa9-b8e9-508061a1d557/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;자바 HashMap 을 사용시, 서버가 여러대인 경우 각 HashMap 각자 다른 데이터를 보유하고 있기 때문에 Consistency 문제가 발생합니다. 실제로 Session(세션) 같은 것들을 이렇게 자바의 객체로 저장한다면 다른 서버에서는 해당 세션이 없으므로 문제가 발생할 수 있죠.&lt;/p&gt;
&lt;h3 id=&quot;race-condition-문제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#race-condition-%EB%AC%B8%EC%A0%9C&quot; aria-label=&quot;race condition 문제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Race Condition 문제&lt;/h3&gt;
&lt;p&gt;또 멀티 쓰레드 환경에서 Race Condition 문제가 발생할 수 있습니다.&lt;/p&gt;
&lt;p&gt;특정 코드 묶음이 있다고 하죠. 그 묶음에는 Write 연산을 해야하는 명령어가 여려개 존재하는데, 그 코드를 어떤 프로세스나 쓰레드가 실행하는가에 따라 결과가 달라지는 상황을 Race 이라고 합니다.&lt;/p&gt;
&lt;p&gt;여러개의 쓰레드가 경합하면서 Context Switching 이 주기적으로 발생하다보면 원치않은 결과가 발생하는 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;race-라는게-이해가-잘-안가요&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#race-%EB%9D%BC%EB%8A%94%EA%B2%8C-%EC%9D%B4%ED%95%B4%EA%B0%80-%EC%9E%98-%EC%95%88%EA%B0%80%EC%9A%94&quot; aria-label=&quot;race 라는게 이해가 잘 안가요 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Race 라는게 이해가 잘 안가요!&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/901d7377-0b9f-4b13-b9a7-0c6fa363ca55/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;예를들어 위와같은 멀티 쓰레드 환경이 있다고 해봅시다. 멀티 쓰레드 환경에서는 모든 쓰레드들이 같은 코드를 공유하는 상황입니다. 그런데 위와같은 for문을 실행할경우 저희가 원하는 count 변수의 결과값은 6000 으로 변하길 원하나, 막상 실제 결과값은 6000 이 안될수도 있다는 것입니다.&lt;/p&gt;
&lt;p&gt;count 라는 것이 전역변수라면 모든 쓰레드들이 함꼐 공유하는 변수가 됩니다. 따라서 어떤 한 쓰레드가 count 값에 대해서 write 연산 (count++) 연산을 하고있는 도중에 다른 쓰레드들도 동시에 write 하려고 하는 상황이 발생한다면 정상적으로 count 변수에 대해 write 연산이 반영되지 않는것이죠.&lt;/p&gt;
&lt;p&gt;좀 더 풀어쓰자면, 현재 count 값이 2000 인 상황을 가정해봅시다. Thread1 이 count++ 연산(write)을 진행하고 저장(save) 을 하기 이전에, 그 사이에 Thread2 가 count++ 연산을 진행해버린다면 2001 아닌 2000 이라는 값에 대해서 또 연산을 진행해버리는 상황이 발생해버리는 것입니다.
즉, 저장을 아직 못해서 count 변수가 아직 값이 1이 증가하지 못한 상황에서 1증가 연산을 또 다른 쓰레드가 하려고하니 결과값이 비정상적으로 도출되는 것이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;싱글-쓰레드-critical-section-동기화-환경-제공&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%B1%EA%B8%80-%EC%93%B0%EB%A0%88%EB%93%9C-critical-section-%EB%8F%99%EA%B8%B0%ED%99%94-%ED%99%98%EA%B2%BD-%EC%A0%9C%EA%B3%B5&quot; aria-label=&quot;싱글 쓰레드 critical section 동기화 환경 제공 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;싱글 쓰레드, Critical Section 동기화 환경 제공&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/cc112bf1-fe56-4db4-8c30-b190bcb66fa7/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위와 같은 Race 상황을 방지하도록 Redis 는 &lt;strong&gt;싱글 쓰레드(Single Thread) 환경에 기반&lt;/strong&gt;하여 동작합니다. 또 Redis 의 자료구조는 &lt;strong&gt;Cirtical Section 이라는 영역에 대해 동기화(Synchornization) 환경을 제공&lt;/strong&gt;해줍니다.&lt;/p&gt;
&lt;h3 id=&quot;critical-section-이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#critical-section-%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;critical section 이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Critical Section 이란?&lt;/h3&gt;
&lt;p&gt;Critical Section 이란 동시에 여러 프로세스가 접근하면 안되는 영역입니다. 여러 Read, Write 연산들에 대한 동기화를 시켜줌으로써 Race 와 같이 원치않는 결과를 막아주도록 Redis 의 자료구조가 구현되어 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;redis-를-언제쓰지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-%EB%A5%BC-%EC%96%B8%EC%A0%9C%EC%93%B0%EC%A7%80&quot; aria-label=&quot;redis 를 언제쓰지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis 를 언제쓰지?&lt;/h2&gt;
&lt;p&gt;세션과 같은 &lt;strong&gt;여러 서버에서 같은 데이터를 공유해야하는 상황&lt;/strong&gt;에서 사용하면 됩니다.&lt;/p&gt;
&lt;p&gt;또한 몰론 단일 서버 1개이더라도 &lt;strong&gt;캐시(Cache) 의 기능으로 빠른 연산 및 접근이 가능하도록 사용&lt;/strong&gt;할 수도 있죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;redis-사용시-주의점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redis-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%A3%BC%EC%9D%98%EC%A0%90&quot; aria-label=&quot;redis 사용시 주의점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redis 사용시 주의점&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/19cf52a1-03f2-423d-b29c-82027dcdd174/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;앞서 언급드렸듯이 Redis는 Single Thread 서버 환경에서 동작하므로 반드시 시간 복잡도를 고려해서 사용해야 합니다.&lt;/p&gt;
&lt;h3 id=&quot;1-on-시간복잡도가-걸리는-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-on-%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84%EA%B0%80-%EA%B1%B8%EB%A6%AC%EB%8A%94-%EC%83%81%ED%99%A9&quot; aria-label=&quot;1 on 시간복잡도가 걸리는 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1) O(n) 시간복잡도가 걸리는 상황&lt;/h3&gt;
&lt;p&gt;만일 Redis 에서 제공하는 기능중에서 특정 데이터를 찾는 연산을 사용한다고 해봅시다. 그렇다면 처음부터 차례대로 모든 원소들을 다 뒤져봐야할텐데, 이 경우는 최악의 경우에 O(n) 시간이 걸리겠죠? 1천만개의 데이터가 있다고 하면, 1천만개에 대해 모두 연산을 해야하는 것이라 시간이 오래걸릴겁니다.&lt;/p&gt;
&lt;p&gt;대표적으로 모든 key 를 가져오는 연산이나, Flush, GetAll 과 같은 명령어들이 이에 해당합니다. 모든 데이터를 다루는 연산이므로 주의해야겠죠?&lt;/p&gt;
&lt;h3 id=&quot;2-서버가-다운되는-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EC%84%9C%EB%B2%84%EA%B0%80-%EB%8B%A4%EC%9A%B4%EB%90%98%EB%8A%94-%EC%83%81%ED%99%A9&quot; aria-label=&quot;2 서버가 다운되는 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2) 서버가 다운되는 상황&lt;/h3&gt;
&lt;p&gt;Redis 는 네트워크로부터 요청을 받아서 명령어를 처리하는데, O(n)이 걸리는 명령어를 실해아고 처리하는 과정이 싱글 쓰레드 방식입니다. 때문에 이 명령어가 오랜 시간이 걸릴경우 나머지 요청들이 모두 거절되고 서버가 다운되는 상황이 발생할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3-memory-fragmentation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-memory-fragmentation&quot; aria-label=&quot;3 memory fragmentation permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3) Memory Fragmentation&lt;/h2&gt;
&lt;p&gt;또한 Redis 사용할시 &lt;strong&gt;메모리 단편화(Memory Fragmentation) 문제를 고려해서 메모리를 적당히, 여유롭게 사용&lt;/strong&gt;하는것이 좋습니다.&lt;/p&gt;
&lt;p&gt;Memory Fragmentation 에는 크게는 2가지로 나뉩니다.
Internal Fragmentation, External Fragmentation 으로 나뉘는데, 저희는 External Fragmentation 를 중점으로 알아보겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;external-fragmentation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#external-fragmentation&quot; aria-label=&quot;external fragmentation permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;External Fragmentation&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1fea6d7c-fd78-45f0-a7a6-0c96beed426f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;메모리 단편화, 즉 External Fragmentation 이란 할당된 메모리 사이의 중간중간에 사용하지 않는 작은 메모리 공간이 여럿 생겨나게 되어, 총 메모리 공간은 충분하지만 실제로는 더 많은 데이터를 할당할 수 없는 현상을 의미합니다.&lt;/p&gt;
&lt;p&gt;위와 같이 메모리 공간이 아직 여유가 있는데도, 핑크색 데이터가 들어갈 수 있는 공간이 없습니다. 이렇게 빈공간이 중간중간에 생겨서 그런것입니다.&lt;/p&gt;
&lt;p&gt;이런 문제 때문에 &lt;strong&gt;실제 사용하는 것보다 더 많은 메모리를 사용하고 있는거처럼 컴퓨터가 인식하고 이 과정에서 프로세스가 죽는 현상이 발생할 수도 있습니다.&lt;/strong&gt; 실제로 사용하는 공간보다 더 많은 메모리를 사용하고 있는 것처럼 컴퓨터가 인식하고 이 과정에서 프로세스가 죽는 현상이 발생할 수도 있는 것이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;4-replication--forking&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-replication--forking&quot; aria-label=&quot;4 replication  forking permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4) Replication : Forking&lt;/h2&gt;
&lt;p&gt;마지막으로 레플리케이션입니다. Redis 는 아까 말했듯이 &lt;strong&gt;휘발성을 가지고있는 메모리상의 데이터 저장소이므로 항상 데이터가 유실될 문제를 염려&lt;/strong&gt;해야합니다.&lt;/p&gt;
&lt;p&gt;이 문제를 해결하도록 Redis 는 데이터 복사 기능(Forking) 을 제공합니다. 부모 프로세스(Redis-Server) 에서 자식 프로세스(Child-Process) 로의 Forking 기능을 제공하는 것이죠.&lt;/p&gt;
&lt;p&gt;Redis 서버의 데이터를 복사해서 디스크와 같은 곳으로 전송해서 저장하는 방식입니다. 이때 Redis 서버의 프로세스를 동일하게 복제하여(Forking) 메모리 상에서 복제한 후 사용하는 방식을 사용하는데, 이 과정에서 메모리가 가득 차있다면 이 복사본이 제대로 생성되지않고 서버가 다운되는 현상이 발생할 수 있습니다.&lt;/p&gt;
&lt;p&gt;따라서 이런 Forking 연산 사용시에 메모리를 반드시 여유있게 확보해야 서버가 다운되는 현상을 방지할 수 있다는 점을 유의하는 것이 좋겠죠?&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금까지 Redis 의 자세한 내부 메커니즘에 대해 살펴봤습니다. CS 지식이 많이 요구되는 내용이였는데, 이해가 잘 안되시는 부분이 있다면 충분한 CS 학습을 하신후 다시 읽어보시는 것을 권장드립니다!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://redis.io/&quot;&gt;Redis Document&lt;/a&gt;
&lt;a href=&quot;https://velog.io/@yu-jin-song/CS-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B3%84%EC%B8%B5-%EA%B5%AC%EC%A1%B0#-%EA%B2%BD%EC%A0%9C%EC%84%B1&quot;&gt;[OS] 메모리 계층 구조(Memory Hierachy)&lt;/a&gt;
&lt;a href=&quot;https://rastalion.me/redis-summary/&quot;&gt;Redis 요약 정리&lt;/a&gt;
&lt;a href=&quot;https://steady-coding.tistory.com/586&quot;&gt;[데이터베이스] Redis란?&lt;/a&gt;
&lt;a href=&quot;https://aws.amazon.com/ko/elasticache/what-is-redis/&quot;&gt;[AWS] Redis란 무엇입니까?&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[스프링부트 OpenAI 를 적용한 AI 채팅 서비스 개발하기]]></title><description><![CDATA[시작에 앞서 요즘 가장 핫한 ChatGPT AI Model (정확히는 ChatGPT 모델과 동일한 모델에 기반한 davinci-003 모델입니다.)을 활용해 애플리케이션에서 AI와 통신하는 RestAPI…]]></description><link>https://haon.site/haon/spring/chatgpt/</link><guid isPermaLink="false">https://haon.site/haon/spring/chatgpt/</guid><pubDate>Fri, 17 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;시작에-앞서&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%9E%91%EC%97%90-%EC%95%9E%EC%84%9C&quot; aria-label=&quot;시작에 앞서 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시작에 앞서&lt;/h2&gt;
&lt;p&gt;요즘 가장 핫한 ChatGPT AI Model (정확히는 ChatGPT 모델과 동일한 모델에 기반한 davinci-003 모델입니다.)을 활용해 애플리케이션에서 AI와 통신하는 RestAPI 를 개발하는 과정을 한번 만들어보고자 이렇게 포스트를 다루게 되었습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;api-key&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#api-key&quot; aria-label=&quot;api key permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;API Key&lt;/h2&gt;
&lt;p&gt;본격적인 시작전에 사전 준비물이 하나 있습니다. 바로 OpenAI 를 사용하기 위한 API Key를 발급받는 것이죠. &lt;a href=&quot;https://openai.com/api/&quot;&gt;OpenAI API&lt;/a&gt; 에 접속하셔서 API-Key 를 발급받고 스프링부트 애플리케이션에서 적용시켜야 인증된 유저로써 OpenAI 를 원활하게 사용 가능합니다.
&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/78efeb7e-d338-480f-aab4-615becbc9a12/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위와 같이 Api Key 를 발급받으면 성공한 것입니다. 발급받은 키를 복사하신후 따로 저장해둡시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;buildgradle&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#buildgradle&quot; aria-label=&quot;buildgradle permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;build.gradle&lt;/h2&gt;
&lt;p&gt;우선 다음과 같이 gradle 파일에서 의존성을 주입해줍시다. 이때 annotationProcessor 는 사실 꼭 의존성 주입을 하지 않아도 되지만, 저는 spring-boot-starter 에 있는 코드를 조금 더 변경하고 응용하면서 자세한 기능들을 소개해볼까 합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;implementation &apos;io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flashvayne&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;chatgpt&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;boot&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.0&lt;/span&gt;&apos;
annotationProcessor &lt;span class=&quot;token string&quot;&gt;&quot;org.springframework.boot:spring-boot-configuration-processor&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;gradle 파일에 의존성 코드까지 추가해주셨다면, bootJar 를 통해 빌드 테스트도 한번 실행해보시길 바랍니다. BUILD SUCCESSFUL 에 성공하도록 말이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;applicationyml--api-key-적용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#applicationyml--api-key-%EC%A0%81%EC%9A%A9&quot; aria-label=&quot;applicationyml  api key 적용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;application.yml : API-Key 적용&lt;/h2&gt;
&lt;p&gt;이제 발급받은 API-key 를 스프링부트 애플리케이션에 적용시켜 줍시다. 아래와 같이 yml 내에 명시해주시면 됩니다. 앞서 말씀드린 내용이지만, 만일 API-Key 를 명시해주지 않는다면 인가되지 않은 유저로써 401 UnAuhorized 에러 메세지를 리턴받게 될겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;spring&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  profiles&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    include&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;API&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;KEY&lt;/span&gt;

openai&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  api&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; qowjfkopqw&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UWnein12321313&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// api key&lt;/span&gt;

chatgpt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  api&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; qowjfkopqw&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UWnein12321313&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// api key&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;api-key-사용시-유의사항&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#api-key-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%9C%A0%EC%9D%98%EC%82%AC%ED%95%AD&quot; aria-label=&quot;api key 사용시 유의사항 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;API-Key 사용시 유의사항&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/cc197819-569b-40d9-b6c8-c7eb237df337/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;API-Key 를 애플리케이션 단에 적용시킬 때 꼭 주의하셔할 사항이 있습니다.&lt;br&gt;
API-Key 를 방금 말씀드렸듯이 특정 사용자가 OpenAI 를 원활하게 사용하기 위해 인증받는 키로써, &lt;strong&gt;절대 퍼블릭으로 유출되어선 안됩니다. 예를들어 깃허브 레포지토리에 공개로 절대 유출시키면 안되는 것이죠.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;만일 깃허브 레포지토리에 public 으로 그대로 API-Key 를 유출시킨다면 몇시간후에 보안을 위해 OpenAI 사이트에서 자동으로 해당 API-Key 를 위처럼 만료시켜주긴 합니다.&lt;/p&gt;
&lt;p&gt;그래도 최대한 보안 문제를 위해 본인 로컬에서만 사용하시거나, 또는 레포지토리에 소스코드를 올린다면 .git ignore 로 yml 파일을 제외시켜 주시야겠죠?&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;restcontroller&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#restcontroller&quot; aria-label=&quot;restcontroller permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RestController&lt;/h2&gt;
&lt;p&gt;이제 본격적인 스프링부트 RestAPI 개발 내용을 시작해봅시다. 우선 컨트롤러를 먼저 소개해볼까 합니다.
질문 하나를 사용자로 부터 입력받으면, chatGPT 에 해당 질문을 던지고 그에대한 대답을 리턴받는 API 입니다.&lt;/p&gt;
&lt;p&gt;이때 저는 qreQuestion 을 보시듯이 &quot;안녕, ChatGPT! 나 질문이 있어.&quot; 이라는 디폴트 질문과 함께, 사용자로부터 입력받는 Request 문자열을 합쳐서 chatGPT 에게 질문을 던지는 방식으로 개발을 진행했습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/chatGPT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyChatGPTController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; qreQuestion &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;안녕, ChatGPT! 나 질문이 있어.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyChatGPTService&lt;/span&gt; chatGPTService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JwtService&lt;/span&gt; jwtService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyChatGPTController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MyChatGPTService&lt;/span&gt; chatGPTService&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JwtService&lt;/span&gt; jwtService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;chatGPTService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chatGPTService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jwtService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; jwtService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ResponseBody&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@PostMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/askChatGPT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseResponse&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ChatGptRes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;askToChatGPT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestBody&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChatGptReq&lt;/span&gt; chatGptReq&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; userIdx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; jwtService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; resultQuestion &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; qreQuestion &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; chatGptReq&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getQuestion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;ChatGptRes&lt;/span&gt; chatGptRes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chatGPTService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getChatResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resultQuestion&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;chatGptRes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BaseException&lt;/span&gt; baseException&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;baseException&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;chatgptreq&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#chatgptreq&quot; aria-label=&quot;chatgptreq permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ChatGptReq&lt;/h3&gt;
&lt;p&gt;위 컨트롤러 단에서 @RequestBody로 입력받는 클래스입니다. 보시듯이 질문란 필드 하나를 정의해두었습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Data&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@AllArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChatGptReq&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; question&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;chatgptservice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#chatgptservice&quot; aria-label=&quot;chatgptservice permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ChatGptService&lt;/h2&gt;
&lt;p&gt;다음으로는 ChatGPT 를 사용하기 위한 핵심적인 비즈니스 로직인 Service 단입니다. ChatgptService 를 생성자 의존관계 주입을 진행해주시고, sendMessage() 메소드를 호출하여 ChatGPT 에게 질문을 보내는 로직을 만들어 봤습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyChatGPTService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChatgptService&lt;/span&gt; chatgptService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyChatGPTService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ChatgptService&lt;/span&gt; chatgptService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;chatgptService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chatgptService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChatGptRes&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getChatResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; prompt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// ChatGPT 에게 질문을 던집니다.&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; responseMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chatgptService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prompt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChatGptRes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;responseMessage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; category&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BaseResponseStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SERVER_ERROR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;chatgptservice-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#chatgptservice-1&quot; aria-label=&quot;chatgptservice 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ChatgptService&lt;/h3&gt;
&lt;p&gt;이때 MyChatGPTService 의 생성자에서 @Autowired 로 의존관계를 주입한 ChatgptService 가 무엇인지 궁금하실겁니다. 이것은 아까 저희가 gradle 파일에서 의존성 주입을 진행했던 중에 하나인 인터페이스이죠. 그에 대한 구성내용은 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;당연한 말이지만, 이미 gradle 을 통해 저희 스프링부트 애플리케이션 내에 존재하게 되었으므로, 아래처럼 별도의 선언은 하지 않으셔도됩니다!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flashvayne&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;chatgpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flashvayne&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;chatgpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ChatRequest&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token import&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flashvayne&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;chatgpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ChatResponse&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChatgptService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;ChatResponse&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendChatRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ChatRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1차-실행결과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1%EC%B0%A8-%EC%8B%A4%ED%96%89%EA%B2%B0%EA%B3%BC&quot; aria-label=&quot;1차 실행결과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1차 실행결과&lt;/h2&gt;
&lt;p&gt;이렇게까지 만드시고 API 테스트를 진행해봅시다. 그러면 아래와 같이 ChatGP 에게 질문을 보내게되고, 그에맞는 대답을 Response 로 받을 수 있는 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/abac5695-1fc5-4612-85e2-8c5ca84a450a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;요청에 성공했습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;answer&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;chatgpt는 최근 인공지능 기술의 발전에 따라 만들어진 챗봇입니다. 이 챗봇은 사람들이 챗봇과 대화하는 것처럼 사람과 대화할 수 있는 기술을 사용합니다. 이는 사람들이 챗봇과&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;response-개선하기--parsing-진행&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#response-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0--parsing-%EC%A7%84%ED%96%89&quot; aria-label=&quot;response 개선하기  parsing 진행 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Response 개선하기 : Parsing 진행&lt;/h2&gt;
&lt;p&gt;그런데 조금 아쉬운 점이 하나 있습니다. chatGPT 로 부터 Response 로 받은 대답 내용이 중간에 조금 잘려있죠? 이는 저희가 gradle 에서 의존성 주입한 내용에서 설정된 내용중에 max_token 값이 300으로 제한되어 있기 때문입니다.&lt;/p&gt;
&lt;p&gt;이대로 끝나면 너무 부자연스러우니, 성능을 더 개선해봅시다. 저희는 크게 2가지 타이틀을 가지고 성능을 개선해 볼겁니다. 이번에는 2가지 중에 하나만 진행하고, 나머지는 추후 새로운 포스팅으로 다루어볼까 합니다.
(다뤄야하는 내용이 너무 길어서, 나중에 다루어볼까 합니다)&lt;/p&gt;
&lt;h3 id=&quot;문자열-파싱&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%9E%90%EC%97%B4-%ED%8C%8C%EC%8B%B1&quot; aria-label=&quot;문자열 파싱 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문자열 파싱&lt;/h3&gt;
&lt;p&gt;문자열 파싱을 진행해, 적절한 문장 개수(2개)로 대답을 끊어줍시다.&lt;/p&gt;
&lt;p&gt;즉 chatGPT 로 부터 Response 로 받는 문장의 수가 2개 이상일때, 앞에서부터 2개의 문장만 대답으로 받을 수 있도록 하는 것이죠. (2개의 문장 뒤의 여러 문장들은 Response 에서 제외하는 것 )&lt;/p&gt;
&lt;h3 id=&quot;max_token-값을-직접-조정하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#max_token-%EA%B0%92%EC%9D%84-%EC%A7%81%EC%A0%91-%EC%A1%B0%EC%A0%95%ED%95%98%EA%B8%B0&quot; aria-label=&quot;max_token 값을 직접 조정하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;max_token 값을 직접 조정하기&lt;/h3&gt;
&lt;p&gt;말그대로 max_token 옵션을 직접 조정할겁니다. chatGPT 의 대답이 중간에 부자연스럽게 끊기는 것 없이, chatGPT 의 대답 길이 제한을 대폭 늘려주는 것이죠.&lt;/p&gt;
&lt;p&gt;단, 이 방법은 경우에 따라 chatGPT 의 대답 내용이 지나치게 길 경우에 대답 길이의 제한이 대폭 늘어납니다. 따라서 응답받는 속도가 더 늘려질 수 있다는 단점이 존재하긴 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;chatgptservice-문자열파싱-기능추가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#chatgptservice-%EB%AC%B8%EC%9E%90%EC%97%B4%ED%8C%8C%EC%8B%B1-%EA%B8%B0%EB%8A%A5%EC%B6%94%EA%B0%80&quot; aria-label=&quot;chatgptservice 문자열파싱 기능추가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ChatGptService 문자열파싱 기능추가&lt;/h2&gt;
&lt;p&gt;다음과 같이 기존 Service 의 성능을 개선해줍시다. 보시듯이 splitMessage 라는 새로운 메소드가 추가되었습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyChatGPTService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChatgptService&lt;/span&gt; chatgptService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyChatGPTService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ChatgptService&lt;/span&gt; chatgptService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;chatgptService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chatgptService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChatGptRes&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getChatResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; prompt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; resultMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chatgptService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prompt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; splitResultMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;splitMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resultMessage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChatGptRes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;splitResultMessage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; category&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BaseResponseStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SERVER_ERROR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;splitmessage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#splitmessage&quot; aria-label=&quot;splitmessage permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;splitMessage&lt;/h3&gt;
&lt;p&gt;문자열 파싱을 진행하는 메소드입니다. 문장 단위를 구분짓기 위해 점 &quot;.&quot; 을 기준으로 한 문장씩 잘라줬으며, 잘라진 각 문자열은 tempArr 이라는 문자열 배열에 할당되도록 했습니다.&lt;/p&gt;
&lt;p&gt;또 앞에서부터 2개의 문장을 resultArr 문자열 배열에 할당해줬으며, 배열의 각 문장을 하나로 합쳐서 리턴시켜주는 로직을 구현해줬습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;splitMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; tempArr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;[.]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; resultArr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;tempArr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            resultArr&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tempArr&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; res &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resultArr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; splitResult &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; realSplitResult &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; splitResult&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; splitResult&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; realSplitResult&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금까지 ChatGPT를 애플리케이션에 적용시켜서 직접 Response 까지 받을 수 있는 방법에 대해 자세히 다루어봤습니다.&lt;/p&gt;
&lt;p&gt;앞서 언급드렸듯이, 추후 포스팅에서는 max_token 값을 직접 조절하여 ChatGPT 로 부터 받는 응답값을 자유롭게 조절하는 과정을 다루어보겠습니다. 또 그 과정속에서 어떤 로직을 통해 ChatGPT 로 부터 응답값을 받을 수 있는 것인지 자세히 이해할 수 있도록 다루어볼까 합니다. 😉&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://platform.openai.com/docs/introduction&quot;&gt;OpenAI Documentation&lt;/a&gt;
&lt;a href=&quot;https://okky.kr/articles/1367408&quot;&gt;오픈AI ChatGPT 사용후기&lt;/a&gt;
&lt;a href=&quot;https://morioh.com/p/e7c9852e417d&quot;&gt;How to Use ChatGPT in Springboot Project Easily&lt;/a&gt;
&lt;a href=&quot;https://github.com/flashvayne/chatgpt-spring-boot-starter&quot;&gt;chatgpt-spring-boot-starter&lt;/a&gt;
&lt;a href=&quot;https://community.openai.com/t/how-does-chatgpt-have-such-massive-token-limit/25738&quot;&gt;How does ChatGPT have such massive token limit?&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[AWS CloudFront로 CDN 환경 구축하기]]></title><description><![CDATA[CDN이란? CDN(Content Delivery Network)은 지리적 제약 없이 전 세계 사용자에게 빠르고 안전하게 콘텐츠를 전송할 수 있는 콘텐츠 전송 기술을 의미합니다. CDN 은 전세계 지역에 골구로 캐시 서버(PoP…]]></description><link>https://haon.site/haon/server/cloudfront-cdn/</link><guid isPermaLink="false">https://haon.site/haon/server/cloudfront-cdn/</guid><pubDate>Thu, 09 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;cdn이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cdn%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;cdn이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CDN이란?&lt;/h2&gt;
&lt;p&gt;CDN(Content Delivery Network)은 &lt;strong&gt;지리적 제약 없이 전 세계 사용자에게 빠르고 안전하게 콘텐츠를 전송&lt;/strong&gt;할 수 있는 콘텐츠 전송 기술을 의미합니다.&lt;/p&gt;
&lt;p&gt;CDN 은 &lt;strong&gt;전세계 지역에 골구로 캐시 서버(PoP) 를 분산 배치&lt;/strong&gt;하여 서버와 사용자간의 물리적인 거리를 줄임으로써, 컨텐츠를 불러오는 소요시간을 최소화하는 캐싱 방식입니다.
예를 들어 미국에 있는 사용자가 한국에 호스팅 된 웹 사이트에 접근하는 경우 미국에 위치한 PoP 서버에서 웹사이트 콘텐츠를 사용자에게 전송하는 방식이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;cloudfront-란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cloudfront-%EB%9E%80&quot; aria-label=&quot;cloudfront 란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CloudFront 란&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0071cba9-6ca9-4f54-8c74-a77843f33b2a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;CloudFront 는 AWS 에서 CDN 서비스를 제공하기 위한 기능입니다.
즉 CloudFront 는 html, css, js 및 이미지 파일과 같은 정적 및 동적 웹 컨텐츠를 사용자에게 더 빨리 배포할 수 있도록 지원하는 웹 서비스입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;aws-s3-와-cloudfront-의-조합&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#aws-s3-%EC%99%80-cloudfront-%EC%9D%98-%EC%A1%B0%ED%95%A9&quot; aria-label=&quot;aws s3 와 cloudfront 의 조합 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AWS S3 와 CloudFront 의 조합&lt;/h2&gt;
&lt;p&gt;결국 S3 저장소를 사용시 CloudFront 를 사용하면 S3 에 직접 접근하는 경우를 줄여서 &lt;strong&gt;S3 저장소에 대한 자체적인 부하를 줄일 수 있습니다.&lt;/strong&gt; S3 에서 데이터를 매번 직접 추출해내는 방식이 아닌, CloudFront 네트워크에 캐싱해 놓은 데이터를 추출하면 되기 때문이죠.&lt;/p&gt;
&lt;p&gt;또 이외에 &lt;strong&gt;안정성, 가용성, 속도&lt;/strong&gt;등 개발자가 고려해야할 성능개선 부분들을 AWS CloudFront 에서 모두 제공해주니, 저희는 S3 와 CloudFront 를 연결해두고 파일을 불러서 사용하기만 하면 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;s3-버킷생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#s3-%EB%B2%84%ED%82%B7%EC%83%9D%EC%84%B1&quot; aria-label=&quot;s3 버킷생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;S3 버킷생성&lt;/h2&gt;
&lt;p&gt;S3 저장소 환경 셋팅과정은 어렵지 않습니다. S3 서비스에 접속하셔서 아래와 같이 &quot;버킷 만들기&quot; 를 누르고 버킷을 생성해주도록 합시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/504df221-7c09-4a45-a28f-5cbb074408b3/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이때 신경써야할 점은 현재 생성할 버킷에 대한 퍼블릭 엑세스 차단 설정을 해제해주는 것입니다. &lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.SimpleDistribution.html&quot;&gt;AWS 공식 가이드라인&lt;/a&gt; 에 따르면, CloudFront 가 S3 버킷안의 컨텐츠를 제공하려면 모든 퍼블릭 엑세스를 차단 해제해주어야 한다고 합니다.&lt;/p&gt;
&lt;p&gt;몰론 이 설정으로 인해 S3 저장소가 퍼블릭 상태가 되어서 유출될 수 있으나, CloudFront 가 이 저장소에 접근하려면 공개상태로 바꿔야하는 것이죠.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d493eae0-ac95-4998-8a0a-983cb24e0e2b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;cloudfront-배포-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cloudfront-%EB%B0%B0%ED%8F%AC-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;cloudfront 배포 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CloudFront 배포 설정&lt;/h2&gt;
&lt;p&gt;다음으로 CloudFront 에 대한 배포도 설정해봅시다. CloudFront 를 검색한후, 아래와 같이 CloudFront 배포 생성 버튼을 누릅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c58caf8e-b640-4353-8152-f57705d9aa7a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그에 대한 세부사항은 모두 기본설정으로 진행하되, 아래와 같은 선택란만 별도로 신경써서 설정해줍시다. 원본 도메인에는 저희가 앞서 생성한 S3 버킷을 선택해줍시다. 또 S3 버킷 엑세스 권한은 &quot;원본 엑세스 제어 설정&quot; 을 선택한 후 CloudFront 로만 접근 가능하도록 제한해줍시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/982f10d5-5f61-41f5-bd89-6cf069ffcfc6/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;web-url-로-s3의-이미지-접근해보기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#web-url-%EB%A1%9C-s3%EC%9D%98-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%A0%91%EA%B7%BC%ED%95%B4%EB%B3%B4%EA%B8%B0&quot; aria-label=&quot;web url 로 s3의 이미지 접근해보기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Web URL 로 S3의 이미지 접근해보기&lt;/h2&gt;
&lt;p&gt;이렇게 셋팅이 완료되었다면, CloudFront 도메인 주소에 기반해서 S3 버킷의 특정 이미지 데이터에 접근해봅시다. 접근 형식은 아래와 같습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;CloudFront 도메인 주소&quot;/&quot;S3버킷 이미지 데이터 이름&quot;

ex) https://qwer.cloudfront.net/스크린샷%202023-02-05%20오후%201.28.08.png&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 문제는 이렇게 접근시 아래처럼 Access Denied 가 뜹니다. 왜 이렇게 되는걸까요?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/07b01206-fee9-45a8-8e8c-c5513c885fbd/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;s3-권한-정책수정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#s3-%EA%B6%8C%ED%95%9C-%EC%A0%95%EC%B1%85%EC%88%98%EC%A0%95&quot; aria-label=&quot;s3 권한 정책수정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;S3 권한 정책수정&lt;/h2&gt;
&lt;p&gt;Access Denied 가 뜨는 이유는 S3 자체적인 정책이 AWS 사용자만 파일에 접근할 수 있도록 제어하고 있기 떄문입니다.
S3 의 디폴트 정책은 특정 파일만 외부로 접근이 가능하게 하는것입니다. 따라서 특정 파일에 대해 외부접근 권한을 열어주는 것이 아닌, 특정 폴더에 대한 권한을 열어주거나, 또는 모든 권한을 열어주는 방식으로 &lt;strong&gt;외부접근 권한 범위를 넓혀줍시다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;아래처럼 기존에 생성한 S3 버킷을 선택하고 편집을 해줍시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/97f51df3-d9ed-4880-9c89-7f180365801d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;버킷정책-권한-편집&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B2%84%ED%82%B7%EC%A0%95%EC%B1%85-%EA%B6%8C%ED%95%9C-%ED%8E%B8%EC%A7%91&quot; aria-label=&quot;버킷정책 권한 편집 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;버킷정책 권한 편집&lt;/h3&gt;
&lt;p&gt;아래처럼 권한을 수정해줍시다. 이때 유심하게 살펴볼 부분은 Principal 과 Resource 란입니다. Principal 은 * 와일드타입으로 열어줘서, 외부에서 어디든 접근이 가능하도록 열어주는 것입니다.&lt;/p&gt;
&lt;p&gt;또 Resource 부분에서는 [버킷 이름 + 특정 폴더] 해당 부분을 버킷 이름으로 수정해주면 됩니다. 또는 특정 폴더 경로까지 적어주면 되는데 저는 s3의 버킷 명만 적어주었습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Sid&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;AddPerm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Principal&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3:GetObject&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:s3:::[버킷 이름 + /특정 폴]/*&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;결과확인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EA%B3%BC%ED%99%95%EC%9D%B8&quot; aria-label=&quot;결과확인 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결과확인&lt;/h3&gt;
&lt;p&gt;버킷 설정이 끝났다면 다시 URL 로 이미지 데이터에 접근해봅시다. 그러면 아래처럼 정상적으로 접근되는 모습을 확인할 수 있습니다!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/76203dc3-552f-4f68-aa59-cac5a631373b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이렇게 AWS CloudFront, S3 에 기반한 CDN 환경을 어떻게 구축할 수 있을지에 대해 알아봤습니다. 다음 포스팅에서는 스프링부트 애플리케이션에 기반해 S3 스토리지에 어떻게 데이터를 업로드및 내려받을 수 있을지를 알아보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.SimpleDistribution.html&quot;&gt;AWS 공식 가이드라인&lt;/a&gt;
&lt;a href=&quot;https://dev.classmethod.jp/articles/restrict-s3-access-through-cloudfront-oai/&quot;&gt;CloudFront OAI(origin access identity)를 통해 S3 버킷 콘텐츠에 대한 액세스&lt;/a&gt;
&lt;a href=&quot;https://medium.com/mindful-engineering/today-we-will-learn-about-cloudfront-690bf3a8819a&quot;&gt;What is Amazon CloudFront and How Does It Work?&lt;/a&gt;
&lt;a href=&quot;https://velog.io/@ililil9482/S3-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%98%AC%EB%A6%AC%EA%B3%A0-url%EB%A1%9C-%EC%A0%91%EA%B7%BC%ED%95%98%EA%B8%B0-w.-CloudFront&quot;&gt;S3 이미지 올리고 url로 접근하기 (w. CloudFront)&lt;/a&gt;
&lt;a href=&quot;https://jirak.net/wp/amazon-cloudfront-%EC%98%A4%EB%A6%AC%EC%A7%84-%EC%95%A1%EC%84%B8%EC%8A%A4-%EC%A0%9C%EC%96%B4oac%EB%A1%9C-s3-%EC%98%A4%EB%A6%AC%EC%A7%84-%EB%B3%B4%ED%98%B8%ED%95%98%EA%B8%B0/&quot;&gt;Amazon CloudFront 오리진 액세스 제어(OAC)로 S3 오리진 보호하기&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[AWS S3 에 스프링부트 파일 업로드 하기]]></title><description><![CDATA[IAM 계정 발급 및 accessKey 생성 IAM 계정 생성 본격적인 API 개발이전에 사전 준비물이 있습니다. 바로 AWS 에서 S3 버킷 접근 권한을 가진 IAM 계정을 하나 생성하는 것입니다. 또 해당 계정에 대해 accessKey…]]></description><link>https://haon.site/haon/server/s3-upload/</link><guid isPermaLink="false">https://haon.site/haon/server/s3-upload/</guid><pubDate>Thu, 09 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;iam-계정-발급-및-accesskey-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#iam-%EA%B3%84%EC%A0%95-%EB%B0%9C%EA%B8%89-%EB%B0%8F-accesskey-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;iam 계정 발급 및 accesskey 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;IAM 계정 발급 및 accessKey 생성&lt;/h2&gt;
&lt;h3 id=&quot;iam-계정-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#iam-%EA%B3%84%EC%A0%95-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;iam 계정 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;IAM 계정 생성&lt;/h3&gt;
&lt;p&gt;본격적인 API 개발이전에 사전 준비물이 있습니다. 바로 AWS 에서 S3 버킷 접근 권한을 가진 IAM 계정을 하나 생성하는 것입니다. 또 해당 계정에 대해 accessKey 과 secretKey 를 발급받아야 합니다.&lt;/p&gt;
&lt;p&gt;IAM 을 검색하고 아래와 같이 계정을 하나 생성해줍시다. 저는 이미 springboot-s3 라는 계정을 하나 생성한 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/85f9afab-b339-40a8-8e3e-5419fcb440ef/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;과정을 간단히 나열해보자면 다음과 같습니다. 우선 사용자 이름을 설정하신다면 아래와 같은 페이지로 넘어오게 됩니다. 여기서 권한 옵션을 &quot;직접 정책 연결&quot;을 선택하시고, 말그대로 직접 권한정책을 지금 생성할 IAM 계정에 대해서 지정해주시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7d00b036-4ff7-41d6-8f4d-de09d48c3851/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;다음처럼 권한 정책을 검색하면 지정해주시면 되는데, S3 와의 원활한 연동을 진행하기 위해선 &quot;IAMFullAccess&quot; 와 &quot;AmazonS3FullAccess&quot; 정책을 선택해주시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/9487e9bb-2cd5-4acf-8e83-fcbefbb0adf4/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;결과적으로 IAM 계정 생성 완료후 선택한 정책을 조회해보면 아래와 같이 나오게됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/3365f619-488d-4550-aaa7-f119fa553092/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;accesskey-활성화발급&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#accesskey-%ED%99%9C%EC%84%B1%ED%99%94%EB%B0%9C%EA%B8%89&quot; aria-label=&quot;accesskey 활성화발급 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;accessKey 활성화(발급)&lt;/h3&gt;
&lt;p&gt;방금 생성한 IAM 계정을 조회해보시면 엑세스 키(accessKey) 가 아래와 같이 활성화되지 않은 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;추후 스프링부트 애플리케이션에서 IAM 계정에 기반한 엑세스 키를 활용해서 S3 에 파일을 읽고 쓰는 작업을 진행할 것인데, 엑세스 키가 활성화 되지 않으면 S3 에 대한 접근권한이 없어서 작업 진행이 안됩니다. 따라서 엑세스 키를 활성화해줘야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6a5792f7-4ad3-4ef0-a675-aedad7cfe918/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&quot;보안 자격 증명&quot; 란을 택하면 아래처럼 엑세스 키를 생성할 수 있게 나오는데 생성을 해주시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e37c07e8-c275-4d5a-a3a2-f24d989d46e1/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;엑세스 키를 생성하면 아래와 같이 발급받을 수 있게 되는데, 이때 시크릿키(비밀 엑세스 키) 의 경우에는 지금 발급받은 직후로는 다시 조회가 불가능하니, 따로 메모를 해두시거나 csv 파일을 다운받으시는걸 권장 드립니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/79e48453-289e-42cf-88f9-0ed3f8a9d3b1/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;스프링부트-애플리케이션--s3-연동&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98--s3-%EC%97%B0%EB%8F%99&quot; aria-label=&quot;스프링부트 애플리케이션  s3 연동 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프링부트 애플리케이션 &amp;#x26; S3 연동&lt;/h2&gt;
&lt;h3 id=&quot;spring-cloud-의존성-추가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#spring-cloud-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%B6%94%EA%B0%80&quot; aria-label=&quot;spring cloud 의존성 추가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;spring cloud 의존성 추가&lt;/h3&gt;
&lt;p&gt;이제 본격적으로 스프링부트 애플리케이션 코드 작성을 시작해봅시다. 우선 spring cloud AWS 외부 라이브러리를 활용해서 쉽게 S3 를 통한 파일 업로드 기능을 구현해볼겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//  build.gradle&lt;/span&gt;
implementation &apos;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;springframework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cloud&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;spring&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;cloud&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;starter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;aws&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.2&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RELEASE&lt;/span&gt;&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;yml-파일구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#yml-%ED%8C%8C%EC%9D%BC%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;yml 파일구성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;yml 파일구성&lt;/h3&gt;
&lt;p&gt;또 application.yml 파일에서 앞서 IAM 생성했던 계정 정보와 access &amp;#x26; secret Key 를 등록해줍시다. 이 설정정보에 기반하여 spring cloud 라이브러리를 활용해 s3 버킷에 파일을 업로드하는 것이죠.&lt;/p&gt;
&lt;p&gt;또 기본적으로 Multipart 로 보낼수 있는 파일의 최대크기의 default 값은 1MB 입니다. 사진이 1MB 가 넘을 수도 있다는 점을 감안하여, 아래와 같이 10MB 로 최대크기를 다시 지정해줬습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// applcation.yml&lt;/span&gt;
cloud&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  aws&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    credentials&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      access&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;AWIUEJWQIUWJEJIOWJIO&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// IAM 계정의 accessKey&lt;/span&gt;
      secret&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BqwulzzZqiZw&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;wwW0ifeweqDmiz&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LfAlp&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// IAM 계정의 secretKey&lt;/span&gt;
    region&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ap&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;northeast&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 버킷의 리전&lt;/span&gt;
    s3&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      bucket&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; my&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;bucket   &lt;span class=&quot;token comment&quot;&gt;//  버킷 이름&lt;/span&gt;
    stack&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      auto&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;

spring&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  servlet&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    multipart&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      max&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;file&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;size&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MB&lt;/span&gt;
      max&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;request&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;size&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MB&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;config-파일&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#config-%ED%8C%8C%EC%9D%BC&quot; aria-label=&quot;config 파일 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Config 파일&lt;/h3&gt;
&lt;p&gt;S3 용 config 파일을 생성후, 앞서 yml 파일에서 만든 설정정보에 기반하여 자세한 설정을 진행해주는 것입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;현재 작업(읽기,쓰기,삭제 등등)하려는 파일의 경우 AWS S3 에 접근시 어떤 IAM 계정으로 접근하며, 어떤 region 과 accessKey, secretKey 로 접근하는지를 명시하는 것이죠.
이에 관한것을 amazonS3Client 라는 이름을 가진 스프링빈으로 관리하는 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;@Value 어노테이션의 모든 변수 값들에는 application.yml 에서 기제한 내용에 기반하여 값이 할당되는 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;참고로 Spring Boot Cloud AWS를 사용하면 AmazonS3Client와 같은 S3 관련 Bean들이 자동 생성됩니다. 즉 여기서는 AmazonS3Client 라는 스프링빈을 재정의한것이죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Slf4j&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AwsS3Config&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;${cloud.aws.credentials.access-key}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// application.yml 에 명시한 내용&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; accessKey&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;${cloud.aws.credentials.secret-key}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; secretKey&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;${cloud.aws.region.static}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; region&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AmazonS3Client&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;amazonS3Client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;BasicAWSCredentials&lt;/span&gt; awsCreds &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BasicAWSCredentials&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accessKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; secretKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AmazonS3Client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AmazonS3ClientBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;standard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withRegion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;region&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withCredentials&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AWSStaticCredentialsProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;awsCreds&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;스프핑부트-애플리케이션-api-코드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%94%84%ED%95%91%EB%B6%80%ED%8A%B8-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-api-%EC%BD%94%EB%93%9C&quot; aria-label=&quot;스프핑부트 애플리케이션 api 코드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스프핑부트 애플리케이션 API 코드&lt;/h2&gt;
&lt;h3 id=&quot;amazons3controller&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#amazons3controller&quot; aria-label=&quot;amazons3controller permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AmazonS3Controller&lt;/h3&gt;
&lt;p&gt;이번 포스팅의 가장 핵심인 API 코드 내용입니다. 저는 업로드, 삭제 2가지 기능을 구현했으며, 업로드 기능은 한번에 여러개의 파일 업로드 가능하도록 구현했으며, 삭제는 하나만 삭제 가능하도록 구현했습니다.&lt;/p&gt;
&lt;p&gt;uploadFile 의 경우 인풋으로 MultipartFile 을 받아오지만, deleteFile 의 경우는 파일이름에 기반하여 S3 에 올라가있는 파일을 삭제를 한다는 점에 유의합시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/file&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AmazonS3Controller&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AwsS3Service&lt;/span&gt; awsS3Service&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@PostMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/uploadFile&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;uploadFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MultipartFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; multipartFiles&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;awsS3Service&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;uploadFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;multipartFiles&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@DeleteMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/deleteFile&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;deleteFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestParam&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; fileName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        awsS3Service&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deleteFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseEntity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;awss3service&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#awss3service&quot; aria-label=&quot;awss3service permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AwsS3Service&lt;/h3&gt;
&lt;p&gt;다음으로는 Service 단입니다. 여기서 실질적으로 S3에 파일을 업로드하거나 삭제하는 요청들이 일어나는 것이죠.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AwsS3Service&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;${cloud.aws.s3.bucket}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; bucket&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AmazonS3&lt;/span&gt; amazonS3&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;uploadFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MultipartFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; multipartFiles&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; fileNameList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// forEach 구문을 통해 multipartFiles 리스트로 넘어온 파일들을 순차적으로 fileNameList 에 추가&lt;/span&gt;
        multipartFiles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; fileName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOriginalFilename&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;ObjectMetadata&lt;/span&gt; objectMetadata &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectMetadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            objectMetadata&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setContentLength&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            objectMetadata&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setContentType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getContentType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InputStream&lt;/span&gt; inputStream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInputStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                amazonS3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;putObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PutObjectRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bucket&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fileName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; inputStream&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; objectMetadata&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withCannedAcl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CannedAccessControlList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PublicRead&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IOException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseStatusException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;INTERNAL_SERVER_ERROR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;파일 업로드에 실패했습니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            fileNameList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; fileNameList&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 먼저 파일 업로드시, 파일명을 난수화하기 위해 UUID 를 활용하여 난수를 돌린다.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; fileName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;randomUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFileExtension&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// file 형식이 잘못된 경우를 확인하기 위해 만들어진 로직이며, 파일 타입과 상관없이 업로드할 수 있게 하기위해, &quot;.&quot;의 존재 유무만 판단하였습니다.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getFileExtension&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; fileName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; fileName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lastIndexOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StringIndexOutOfBoundsException&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResponseStatusException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BAD_REQUEST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;잘못된 형식의 파일&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; fileName &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;) 입니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;deleteFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; fileName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        amazonS3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deleteObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DeleteObjectRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bucket&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fileName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bucket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드에서 핵심적인 코드들만 잠깐 짚어봅시다.&lt;/p&gt;
&lt;h4&gt;1. createFileName&lt;/h4&gt;
&lt;p&gt;이때 createFileName 을 보시면 UUID 를 활용한 모습을 볼 수 있습니다.
S3 에 같은 이름의 파일을 업로드하게 된다면 먼저 업로드된 파일이 나중에 업로드한 파일로 덮어씌어지는 문제가 발생합니다. 이를 해결하도록 S3 에는 실제 파일명이 아닌 별도의 파일 이름으로 저장하기위해 UUID 를 활용한 것입니다.&lt;/p&gt;
&lt;h4&gt;2. withCannedAcl 메소드&lt;/h4&gt;
&lt;p&gt;이 메소드를 통해 업로드한 파일을 모두가 읽을 수 있게 설정합니다.&lt;/p&gt;
&lt;h4&gt;3. objectMetadata.setContentLength(file.getSize());&lt;/h4&gt;
&lt;p&gt;ObjectMetadata 객체에 content-length를 지정하지 않으면 warning 로그가 발생합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;api-테스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#api-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;api 테스트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;API 테스트&lt;/h2&gt;
&lt;p&gt;아래와 같이 uploadFile API 를 요청하면 Response 로 업로드한 파일명이 리턴되는 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0af01794-b0a4-4b54-a25a-3b27004bb2b6/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;S3 에서도 조회해보면 파일 여러개가 정상적으로 업로드된 모습을 확인할 수 있습니다!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1d3c95ad-5cd6-4b9d-83fb-948f8acae51c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금까지 IAM 계정을 생성 및 엑세스&amp;#x26;시크릿 키를 발급하고, 스프링부트 애플리케이션에서 Spring Cloud 를 활용해 다중 파일을 업로드 및 삭제하는 API 를 구현하는 방법에 대해 알아봤습니다. 다소 긴 내용의 포스팅이였으나 끝까지 읽어주신 모든 분들이 많은 내용을 얻어갔으면 하는 포스팅이네요 🙂&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://spring.io/projects/spring-cloud&quot;&gt;Spring Cloud Docs&lt;/a&gt;
&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/id_credentials_access-keys.html&quot;&gt;Aws Documentation : IAM 사용자의 액세스 키 관&lt;/a&gt;
&lt;a href=&quot;https://www.44bits.io/ko/post/publishing_and_managing_aws_user_access_key&quot;&gt;아마존 웹 서비스 IAM 사용자의 액세스 키 발급 및 관리&lt;/a&gt;
&lt;a href=&quot;https://jhleeeme.github.io/create-access-key-in-aws/&quot;&gt;AWS Access Key 생성하기&lt;/a&gt;
&lt;a href=&quot;https://velog.io/@louie/S3%EB%A5%BC-%EC%97%B0%EB%8F%99%ED%95%9C-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C#%EC%8A%A4%ED%94%84%EB%A7%81-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%B6%94%EA%B0%80&quot;&gt;Spring Boot와 S3를 연동한 파일 업로드&lt;/a&gt;
&lt;a href=&quot;https://leveloper.tistory.com/46&quot;&gt;[스프링] AWS S3에 이미지 업로드 하기&lt;/a&gt;
&lt;a href=&quot;https://earth-95.tistory.com/117#AwsS-Service-java&quot;&gt;[SpringBoot] SpringBoot 를 활용한 AWS S3에 파일 업로드 구현&lt;/a&gt;
&lt;a href=&quot;https://jojoldu.tistory.com/300&quot;&gt;SpringBoot &amp;#x26; AWS S3 연동하기&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Docker Swarm의 Cluster 환경을 구축하는 동작원리]]></title><description><![CDATA[Scale Up(수직확장)  하나의 호스트 OS 에서 컨테이너가 많아져 CPU…]]></description><link>https://haon.site/haon/server/docker-swarm/</link><guid isPermaLink="false">https://haon.site/haon/server/docker-swarm/</guid><pubDate>Sun, 05 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;scale-up수직확장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#scale-up%EC%88%98%EC%A7%81%ED%99%95%EC%9E%A5&quot; aria-label=&quot;scale up수직확장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Scale Up(수직확장)&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d379243c-0db2-4ce8-821d-0dcfa4413dcd/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;하나의 호스트 OS 에서 컨테이너가 많아져 CPU, 메뢰, 디스크 용량과 같은 자원이 부족해졌다고 해봅시다. 새로운 컨테이너를 추가하려는데, 자원 부족으로 추가하지 못하는 상황이면 이를 어떻게 해결할 수 있을까요?&lt;/p&gt;
&lt;p&gt;하지만 자원의 확장성, 비용 측면에서 좋지않은 해답입니다. 자원이 부족해질때마다 자원을 더 사야하거나 기껏 자원을 사놨는대, 그만큼의 자원이 필요하지 않을수도 있기 때문이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;scale-out-수평확장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#scale-out-%EC%88%98%ED%8F%89%ED%99%95%EC%9E%A5&quot; aria-label=&quot;scale out 수평확장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Scale Out (수평확장)&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/3df80f26-bcec-4aa5-8a46-45cd97cdb0e0/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Scale In 방식보다 더 좋은 방법은 여러대의 서버를 클러스터로 만들어서, 자원을 병렬롤 확장하는 것입니다. 이를 Scale Out 이라고합니다. (클러스터는 뒤이어서 계속 설명할겁니다!)&lt;/p&gt;
&lt;p&gt;예를들어 4GB 의 메모리가 탑제된 서버 1대에 도커엔진을 설치해 실제 운영 환경에서 사용한다고 해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5a84400b-8d05-40e0-8c78-34ba2a2dd4df/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이 1대의 서버에 컨테이너가 너무 많이 생성되있어 더 이상 컨테이너를 사용할 수 없다고 판단되면, 새로운 서버를 추가해서 자원을 늘리는 방식입니다. 만약, 놀고있는 자원이 많다면 다시 줄이면 되는것이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;클러스터cluster&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0cluster&quot; aria-label=&quot;클러스터cluster permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클러스터(Cluster)&lt;/h2&gt;
&lt;h3 id=&quot;클러스터란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%9E%80&quot; aria-label=&quot;클러스터란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클러스터란?&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/32fc9c0b-e150-4ee0-aafa-5c7d9e1f08aa/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;앞서 게속 클러스터란 용어가 등장했는데, 이것은 무엇일까요?
서버 클러스터란, &lt;strong&gt;각기 다른 서버들을 하나로 묶어서 하나의 시스템같이 동작하게 함으로써, 클라이언트들에게 고가용성의 서비스를 제공하는 것을 말합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;동일한 네트워크 풀을 쓰면서, 요청에 따라 트래픽을 분산시킬 수 있는것이죠.&lt;/p&gt;
&lt;h3 id=&quot;클러스터-도입의-어려움&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EB%8F%84%EC%9E%85%EC%9D%98-%EC%96%B4%EB%A0%A4%EC%9B%80&quot; aria-label=&quot;클러스터 도입의 어려움 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클러스터 도입의 어려움&lt;/h3&gt;
&lt;p&gt;하지만 여러대의 서버를 하나의 자원 풀로 만드는 것은 어렵습니다. &lt;strong&gt;새로운 서버나 컨테이너가 추가됐을 때 이를 발견하는 작업부터, 어떤 서버에 어떤 컨테이너를 할당할 것인지에 대한 작업들 등 처리해야할 작업이 많습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;다행히 이러한 문제들을 해결하는 여러 솔루션을 오픈소스로 활용할 수 있습니다. 그 중 대표적인 것이 바로 도커스웜입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;도커-스웜docker-swarm&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%BB%A4-%EC%8A%A4%EC%9B%9Cdocker-swarm&quot; aria-label=&quot;도커 스웜docker swarm permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도커 스웜(Docker Swarm)&lt;/h2&gt;
&lt;p&gt;도커스웜은 스웜모드를 지원합니다. 스웜모드는 마이크로 서비스 아키텍처의 컨테이너를 다루기위한 클러스터링 기능에 초점을 맞추고 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;같은 컨테이널을 동시에 여러개 생성해, 필요에 따라 유동적으로 컨테이너 수를 조절할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;컨테이너로의 연결을 분산하는 &lt;strong&gt;로드밸런싱 기능을 자체적으로 지원&lt;/strong&gt; 하고 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;또한 &lt;strong&gt;Scale Out 은 기본적으로 지원하&lt;/strong&gt;지만, &lt;strong&gt;자체적으로 인스턴스를 늘리거나 줄이거나는 할 수 없고 개발자가 직접 늘려줘야&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;스웜은 도커 엔진 자체에 내장되어 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;swarm-모드의-구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#swarm-%EB%AA%A8%EB%93%9C%EC%9D%98-%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;swarm 모드의 구성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Swarm 모드의 구성&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e76063fd-8ffa-4131-88db-b3db3a216c42/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;스웜모드는 매너지 모드와 워커 모드로 구성되어있습니다. 각 모드를 설명드리자면 다음과 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;워커 노드 : 실제로 컨테이너가 생성되고 관리되는 Docker 서버&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;매너저 노드 : 워커 노드를 관리하기 위한 Docker 서버&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;매너저 노드는 기본적으로 워커 노드의 역할을 포함하며, 운영환경에서 다중화 (여러개를 구축) 를 하는것이 좋습니다. 왜냐면 매너지 노드의 부하를 분산하고, 특정 워커 노드가 다운됐을 떄 정상적으로 스웜 클러스터를 유지할 수 있기 때문이죠.&lt;/p&gt;
&lt;h3 id=&quot;클러스터-구축&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EA%B5%AC%EC%B6%95&quot; aria-label=&quot;클러스터 구축 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클러스터 구축&lt;/h3&gt;
&lt;p&gt;그렇다면 클러스터 구축은 어떻게 이루어질까요? AWS EC2 인스턴스의 예를 들어보죠. 4개의 인스턴스가 준비됐다고 가정해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/339f0f6e-1180-49d7-bb32-a06144d77866/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;우선 매니저 노드의 역할을 한 인스턴스, 노드의 역할을 할 인스턴스를 지정합니다. 저는 인스턴스 1을 매너저 노드로 지정하고, 2,3,4를 워커 노드로 지정하겠습니다.
노드를 지정했다면, 역할을 할당해줘야겠죠?&lt;/p&gt;
&lt;p&gt;도커 명령어인 docker swarm init 을 통해 매니저 노드를 설정합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/f33b54e2-4c5e-4ba0-a562-f89bce1a68ae/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이제 워커 노드를 추가할 때입니다. docker swarm join 을 통해 노드를 추가할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4406c125-0140-49d2-ade9-3c33ec0ba111/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이렇게 docker swarm join 으로 워커 노드들을 추가하면서, 하나의 클러스터 단위를 완성시키게 됩니다. 이때 몰론 매너저 노드 딱 1개만으로도 클러스터라고도 표현할 수는 있긴합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/34dbd3ac-b815-46ce-b3b7-0ba85e11bd9b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이로써 저희는 이제 서비스를 시작할 수 있게 되었습니다. (서비스란 무엇인지에 대해 바로 이어서 설명하겠습니다.)&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;서비스란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B9%84%EC%8A%A4%EB%9E%80&quot; aria-label=&quot;서비스란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서비스란&lt;/h2&gt;
&lt;p&gt;일반적인 도커 명령어의 제어 단위는 컨테이너입니다. 이는 도커 클라이언트(CLI) 가 제어하는 컨테이너라는 말입니다.&lt;/p&gt;
&lt;h3 id=&quot;서비스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B9%84%EC%8A%A4&quot; aria-label=&quot;서비스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서비스&lt;/h3&gt;
&lt;p&gt;도커 스웜에서 제어단위는 서비스입니다. &lt;strong&gt;서비스는 같은 이미지에서 생성되는 컨테이너의 집합을 의미합니다.&lt;/strong&gt; 기본적인 배포 단위로써, 기동할 이미지, 컨테이너 수, 설정등을 정의한 것이죠.
아래와 같이 서비스를 제어하면 해당 서비스내의 컨테이너에 같은 명령이 수행됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/3005a8a5-da58-4abb-ae9b-baca28f5f5a8/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;task&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#task&quot; aria-label=&quot;task permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Task&lt;/h3&gt;
&lt;p&gt;이때 서비스내의 동일한 이미지로부터 생성된 컨테이너 각각을 Task(테스크) 라 부릅니다. 서비스의 정의에따라 테스크를 할당할 적합한 노드를 선정하고, 선택된 노드에 테스크를 분배하는 방식인것이죠.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;즉 하나의 서비스는 여러개의 테스크를 실행할 수 있는것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/65df270c-8ba6-434e-b514-72b42e1ca705/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;레플리카replica&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A0%88%ED%94%8C%EB%A6%AC%EC%B9%B4replica&quot; aria-label=&quot;레플리카replica permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;레플리카(Replica)&lt;/h3&gt;
&lt;p&gt;또, 생성된 테스크를 한묶음으로 묶어서 레플리카(Replica) 라고하고, 개발자가 서비스에 설정한 레플리카의 수만큼 테스크가 스웜 클러스터 내에 존재해야합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;--replicas 옵션을 사용하여 동일 이미지를 가동할 컨테이너 수를 지정할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/2008fdbc-782e-4dd0-af5d-45e0445d9e42/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;예제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%88%EC%A0%9C&quot; aria-label=&quot;예제 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;예제&lt;/h3&gt;
&lt;p&gt;예제를 더 확인하며 이해해봅시다. 저는 아래와 같이 스프링부트, Nginx 2개의 서비스를 정의했습니다. 앞서 말씀드렸듯이 &quot;서비스&quot;란 동일한 이미지로부터 생성되는 컨테이너의 집합이라고 했었죠?
또 테스크(Task) 란 동일한 이미지(스프링부트 이미지, Nginx 이미지 등등)로 부터 생성되는 컨테이너들을 의미했습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/65bd27e7-fca6-40ba-b783-f0124822c6d2/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;스프링부트는 모든 노드에 생성될 테스크의 수를 3개로 설정했고, Nginx 는 2개로 설정한 것입니다. 클러스터안에 스프링부트 컨테이너가 3개, Nginx 컨테이너는 2개 할당된 것을 볼 수 있죠.&lt;/p&gt;
&lt;h3 id=&quot;swarm-의-서비스와-테스크의-필요성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#swarm-%EC%9D%98-%EC%84%9C%EB%B9%84%EC%8A%A4%EC%99%80-%ED%85%8C%EC%8A%A4%ED%81%AC%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1&quot; aria-label=&quot;swarm 의 서비스와 테스크의 필요성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Swarm 의 서비스와 테스크의 필요성&lt;/h3&gt;
&lt;p&gt;그렇다면 이러한 도커 스웜에서 서비스, 테스크라는 것은 왜 필요한걸까요? 이들이 있어야지 &lt;strong&gt;여러 대의 노드에서 같은 역할을 하는 컨테이너를 실행하여 로드밸런싱 하거나, 장애 상황에 대비한 이중화 구성을 할 수 있는것이죠.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;swarm-모드에서-제공하는-기능&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#swarm-%EB%AA%A8%EB%93%9C%EC%97%90%EC%84%9C-%EC%A0%9C%EA%B3%B5%ED%95%98%EB%8A%94-%EA%B8%B0%EB%8A%A5&quot; aria-label=&quot;swarm 모드에서 제공하는 기능 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Swarm 모드에서 제공하는 기능&lt;/h2&gt;
&lt;p&gt;이러한 스웜모드가 제공하는 기능들은 정말 다양합니다. 여러 기능들중 메인 기능만을 나열해보면 다음과 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;클러스터 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;서비스 관리&lt;/li&gt;
&lt;li&gt;네트워크 관리&lt;/li&gt;
&lt;li&gt;노드 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 기능들은 도커 엔진에서 제공하는 기능들과 많이 비슷하죠? 이 또한 내용이 넓어서 자세한 내용은 스킵하고 넘어가겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;특정-노드에-장애가-발생한경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B9%EC%A0%95-%EB%85%B8%EB%93%9C%EC%97%90-%EC%9E%A5%EC%95%A0%EA%B0%80-%EB%B0%9C%EC%83%9D%ED%95%9C%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;특정 노드에 장애가 발생한경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;특정 노드에 장애가 발생한경우&lt;/h3&gt;
&lt;p&gt;만약 스웜모드에서 노드에 장애가 발생하면 어떻게될까요? 스웜은 서비스의 테스크들에 대한 상테를 계속 확인하다가 &lt;strong&gt;서비스내에다 정의한 레플리카 수만큼 스웜 클러스터 내에 존재하지 않으면, 새로운 테스크 레플리카를 생성&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;p&gt;예를들어 앞선 예제에서 첫번째 워커노드의 서버가 다운되었다고 해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/eab9eb44-44a5-4ad3-abfe-fe020a118cb1/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이렇게 노드 하나가 다운되면, 정상적으로 돌아가고있는 또 다른 노드에다 테스크를 생성하는 것입니다. 몰론, 이때 매너저모드가 다운돼버리면 클러스터가 사라져버립니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/bbad91f6-62e5-4b97-a066-cd853fdbfa88/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;swarm-모드의-필요성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#swarm-%EB%AA%A8%EB%93%9C%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1&quot; aria-label=&quot;swarm 모드의 필요성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Swarm 모드의 필요성&lt;/h2&gt;
&lt;p&gt;스웜모드를 왜 쓰는것이 좋을까요? 그것은 바로 &lt;strong&gt;&quot;서비스 확장/관리를 편하게&quot; 하기위함&lt;/strong&gt;입니다. 이에 관하여, 컨테이너를 어떻게 효율적으로 생성할 수 있을지에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;각각의 서비스(스프링부트, Nginx 서비스) 가 동작하기 위해선 컨테이너가 필요하겠죠. 서비스에 필요한 컨테이너가 1~2개 정도라면, 도커 명령어를 직접 입력해 생성할 수 있지만 여러개라면 말이 달라집니다. 명령어를 일일이 다 쳐서 컨테이너(테스크) 여러개를 매번 생성하자니 불편해지죠.&lt;/p&gt;
&lt;p&gt;만일 아래처럼 하나의 프로젝트 단위를 클러스터 내에 묵었고, 프로젝트가 구동되기 위해서는 스프링부트, Nginx 컨테이너가 필요합니다. 이 컨테이너들을 기반으로 테스크가 생성되기 때문이죠.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/9d2977c7-898a-40ba-9215-08517e520158/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;docker-compose&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#docker-compose&quot; aria-label=&quot;docker compose permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Docker Compose&lt;/h2&gt;
&lt;p&gt;앞선 설명처럼 매번 명령어를 쳐서 테스크(Task) 를 여러개 생성하는 작업은 불편합니다. 이는 도커 컴포즈(Compose) 를 통해서 해결이 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;컴포즈는 여러개의 컨테이너를 하나의 서비스로 정의하고 실행합니다.&lt;/strong&gt; 스웜 모드의 서비스와 비슷하게 설&lt;strong&gt;정파일에 정의된 서비스의 컨테이너 수를 유동적으로 조절&lt;/strong&gt;할 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;도커 컴포즈는 도커 엔진밖에 위치해있습니다. 따라서 도커에 내장되있지 않으므로 별도로 컴포즈를 직접 설치해줘야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;도커-컴포즈-파일-작성법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%BB%A4-%EC%BB%B4%ED%8F%AC%EC%A6%88-%ED%8C%8C%EC%9D%BC-%EC%9E%91%EC%84%B1%EB%B2%95&quot; aria-label=&quot;도커 컴포즈 파일 작성법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도커 컴포즈 파일 작성법&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ab7fa973-7b79-4b9b-9869-4ef2e9a27249/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;1.도커 컴포즈는 컨테이너의 설정이 정의된 yml 파일(docker-compose.yml)을 읽어 도커엔진을 통해 컨테이너를 생성합니다. 따라서 도커 컴포즈를 사용하려면 yml 파일을 먼저 작성해야하죠.&lt;/p&gt;
&lt;p&gt;2.파일을 작성후 docker --compose 명령어를 통해 도커 컨테이너를 생성할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;도커-컴포즈-내부구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%BB%A4-%EC%BB%B4%ED%8F%AC%EC%A6%88-%EB%82%B4%EB%B6%80%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;도커 컴포즈 내부구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도커 컴포즈 내부구조&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/3f87c064-3253-45fd-99fe-a38b6e24431a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;도커 컴포즈는 기본적으로 docker --compose.yml 에 위치한 디렉토리 이름을 프로젝트 이름으로 사용합니다. 즉 yml 이 저장된 디렉토리의 이름에 따라 프로젝트의 이름이 달라집니다. (몰론 직접 설정도 가능합니다)&lt;/p&gt;
&lt;p&gt;하나의 프로젝트는 여러개의 서비스로 이루어지고, 각 서비스에는 여러개의 컨테이너가 존재할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이렇게 도커의 클러스터부터 시작해 도커 스웜모드, 그리고 컴포즈까지 컨테이너 확자엥 대한 상세한 내용을 상세하게 다루었습니다. 이번 포스팅과 관련한 내용중 가장 핫한 기법이 바로 쿠버네티스인데, 이번 내용을 이해하셨다면 쿠버네티스 또한 쉽게 적용할 수 있을겁니다. 쿠버네티스를 비롯해 도커를 학습하시는 분들에게 이번 포스팅이 도움이 되었으면 합니다 😉&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.docker.com/&quot;&gt;Docker Docs&lt;/a&gt;
&lt;a href=&quot;https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html&quot;&gt;초보를 위한 도커 안내서 - 도커란 무엇인가? SERIES 1/3&lt;/a&gt;
&lt;a href=&quot;https://sarc.io/index.php/cloud/1705-docker-15-swarm&quot;&gt;Docker 가상 환경 구축 입문 (15) - Swarm의 서비스와 태스크&lt;/a&gt;
&lt;a href=&quot;https://selfish-developer.com/entry/%EA%B0%80%EC%83%81%ED%99%94-%EA%B8%B0%EC%88%A0%EC%9D%98-%EC%9C%A0%ED%98%95&quot;&gt;가상화 기술의 유형&lt;/a&gt;
&lt;a href=&quot;https://velog.io/@korjsh/Docker-%EC%BB%B4%ED%8F%AC%EC%A6%88-%EA%B8%B0%EC%B4%88-%EB%B0%8F-%EB%AC%B8%EB%B2%95&quot;&gt;도커 컴포즈 및 기본문법&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[도커의 개념, 하이퍼바이저 및 컨테이너 기술의 장배경]]></title><description><![CDATA[가상화, 도커의 등장배경에 대해 다루어보고자 합니다. 혼자 공부하며 작성한 것이므로 틀린 내용이 많을 수 있어요! 시작에 앞서 : 왜 글을 작성하는가? "배포" 라는 말을 들어봤을때, 대부분 서버 개발자분들이 Docker…]]></description><link>https://haon.site/haon/server/docker-concept/</link><guid isPermaLink="false">https://haon.site/haon/server/docker-concept/</guid><pubDate>Sat, 04 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;가상화, 도커의 등장배경에 대해 다루어보고자 합니다. 혼자 공부하며 작성한 것이므로 틀린 내용이 많을 수 있어요!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;시작에-앞서--왜-글을-작성하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%9E%91%EC%97%90-%EC%95%9E%EC%84%9C--%EC%99%9C-%EA%B8%80%EC%9D%84-%EC%9E%91%EC%84%B1%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;시작에 앞서  왜 글을 작성하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시작에 앞서 : 왜 글을 작성하는가?&lt;/h2&gt;
&lt;p&gt;&quot;배포&quot; 라는 말을 들어봤을때, 대부분 서버 개발자분들이 Docker 를 한번쯤은 들어보셨거나 사용해보셨을 겁니다. &lt;strong&gt;그런데 많은 분들이 도커를 왜 써야하며, 어떤 메커니즘으로 돌아가는 것인지 잘 모릅니다.&lt;/strong&gt; 저희는 도커라는게 그저 다들 사용하는거니까, 이유도 모르고 사용해야할까요?&lt;/p&gt;
&lt;p&gt;많은 분들이 제 포스팅을 보시고 도커를 깊게 학습하시는데 도움이 되셨으면 합니다 😀&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;도커란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%BB%A4%EB%9E%80&quot; aria-label=&quot;도커란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도커란?&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/30bda8ad-63b3-400e-8282-9ca22fa2d38f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;많은 분들에게 도커가 무엇인지 물어보면 아래와 같이 대답할겁니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;도커란 컨테이너 기반의 가상화 도구입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;여기서 컨테이너란 무엇이고, 가상화란 무엇일까요? 이들을 먼저 이해해야합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;가상화-등장배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%80%EC%83%81%ED%99%94-%EB%93%B1%EC%9E%A5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;가상화 등장배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;가상화 등장배경&lt;/h2&gt;
&lt;h3 id=&quot;서비스-플랫폼-사이의-충돌&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B9%84%EC%8A%A4-%ED%94%8C%EB%9E%AB%ED%8F%BC-%EC%82%AC%EC%9D%B4%EC%9D%98-%EC%B6%A9%EB%8F%8C&quot; aria-label=&quot;서비스 플랫폼 사이의 충돌 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서비스 플랫폼 사이의 충돌&lt;/h3&gt;
&lt;p&gt;가상화를 설명하기전에, 가상화가 필요한 이유에 대해 알아봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/9be16346-c9b7-482f-816a-1bf552c75e1f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;여기 가격이 천만원이나 하는 엄청 좋은 서버가 있습니다. 이 서버가 직접 개발한 쇼핑몰 사이트를 돌리기로 했습니다. 고정 사용자가 1천명정도나 되죠 그러나 기껏 비싸게 구매했더니, 막상 서버의 성능의 반의 반도 사용하지 않고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/60344571-4362-4823-9fd5-45c6a8317909/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이대로는 서버 비용 및 유지가 부담스러워서 새로운 금융 서비스 프로젝트를 올리기로합니다. 그런데 이러면 문제가 발생하겠죠? 개발을 하고 적용하려니, 기존의 유기견 서비스에서 사용하고 있는 &lt;strong&gt;기술들과 충돌이 발생&lt;/strong&gt;할겁니다. 결국 금융 서비스를 도입하지 못하는 상황이 발생합니다.&lt;/p&gt;
&lt;h3 id=&quot;가상화의-등장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%80%EC%83%81%ED%99%94%EC%9D%98-%EB%93%B1%EC%9E%A5&quot; aria-label=&quot;가상화의 등장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;가상화의 등장&lt;/h3&gt;
&lt;p&gt;그러다 문득, &lt;strong&gt;서버의 성능을 나눠서 사용하면 되지 않을까?&lt;/strong&gt; 라는 생각을 하게됩니다. 이것이 바로 가상화라는 개념이 등장하게 된 것이죠.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;하나의 서버 자원을 나눠서 가지며, 성능을 분산 시키고, 분산된 서버들은 각기 다른 서비스를 수행할 수 있게합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;가상화를 통해 사용자가 많은 자원을 할당해주고, 적은 서비스에는 적게 할당할 수 있게 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;서버-가상화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B2%84-%EA%B0%80%EC%83%81%ED%99%94&quot; aria-label=&quot;서버 가상화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서버 가상화&lt;/h2&gt;
&lt;p&gt;이어서 가상화의 종류에 대해 알아봅시다. 여러 종류중에 먼저 서버 가상화를 알아봅시다.&lt;/p&gt;
&lt;h3 id=&quot;서버-가상화란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B2%84-%EA%B0%80%EC%83%81%ED%99%94%EB%9E%80&quot; aria-label=&quot;서버 가상화란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서버 가상화란?&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0a53dfa2-60e4-49fd-9305-ad6a09ca2ad0/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;서버 가상화란 &lt;strong&gt;하나의 물리적 서버 호스트에서 여러개의 서버 운영체제를 게스트로 실행할 수 있게 해주는 아키텍쳐&lt;/strong&gt;입니다. 보시듯이 하나의 호스트에 여러개의 게스트 OS 가 할당된 것을 확인할 수 있죠?&lt;/p&gt;
&lt;h3 id=&quot;하이퍼바이저&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%98%EC%9D%B4%ED%8D%BC%EB%B0%94%EC%9D%B4%EC%A0%80&quot; aria-label=&quot;하이퍼바이저 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;하이퍼바이저&lt;/h3&gt;
&lt;p&gt;이렇게 생성된 여러개의 운영체제는 가상머신이라는 단위로 구분됩니다. 각 가상머신에는 여러 운영체제가 설치되어 사용되고요.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/9cfd0249-61f4-421e-81a0-89f2714ad06c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;하이퍼바이저에 의해 생성되고 관리되는 운영체제는 게스트 운영체제(Guest OS) 라고하며, 각 게스트 운영체제는 다른 게스트 운영체제와는 완전히 독립된 공간과 시스템 자원을 할당받아 사용합니다.&lt;/p&gt;
&lt;p&gt;하이퍼바이저의 역할을 정리해보면, &lt;strong&gt;하이퍼바이저는 Guest OS들을 생성하고, 각 OS 들에게 자원을 적절히 나누어주며 조율&lt;/strong&gt;합니다. 그리고 &lt;strong&gt;각 OS 들의 커널을 번역해서 하드웨어에게 전달&lt;/strong&gt;하고요. 대표적인 가상화 툴로 Virtual Box, VMware 등이 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;하이퍼바이저의-한계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%98%EC%9D%B4%ED%8D%BC%EB%B0%94%EC%9D%B4%EC%A0%80%EC%9D%98-%ED%95%9C%EA%B3%84&quot; aria-label=&quot;하이퍼바이저의 한계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;하이퍼바이저의 한계&lt;/h3&gt;
&lt;p&gt;이렇게 각종 시스템자원을 가상화하고, 독립된 공간을 생성하는 작업은 반드시 하이퍼바이저를 거치기 때문에, &lt;strong&gt;일반 호스트에 비해 성능 손실이 발생&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;p&gt;또한 &lt;strong&gt;가상 머신에는 게스트 OS를 사용하기 위한 라이브러리, 커널등을 전부 포함&lt;/strong&gt;하기 때문에 배포하기 위한 이미지로 만들었을 때 크기 또한 커집니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;즉, 가상 머신에는 완벽한 운영체제를 생성할 수 있는 장점은 있지만 성능이 느리고, 용량상으로 부담이 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;컨테이너-기반-가상화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EA%B8%B0%EB%B0%98-%EA%B0%80%EC%83%81%ED%99%94&quot; aria-label=&quot;컨테이너 기반 가상화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨테이너 기반 가상화&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/b3d3309b-b4c7-4e36-bcca-c72927598dfc/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;앞서 살펴본 서버 가상화 방법은 작은 애플리케이션을 구동하는데 OS 까지 새로 띄우는 것이 부담스러울 수 있습니다. 이를 해결하기 위해 컨테이너가 등장했습니다.&lt;/p&gt;
&lt;h3 id=&quot;프로세스-단위-격리환경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8B%A8%EC%9C%84-%EA%B2%A9%EB%A6%AC%ED%99%98%EA%B2%BD&quot; aria-label=&quot;프로세스 단위 격리환경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프로세스 단위 격리환경&lt;/h3&gt;
&lt;p&gt;컨테이너는 가상화된 공간을 생성하기 위해 리눅스 자체 기능으로 chroot, 네임스페이스, cgroup 을 사용함으로써 &lt;strong&gt;프로세스 단위의 격리 환경을 만듭니다.&lt;/strong&gt;
보시듯이 도커 엔진 위에 컨테이너가 할당된 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;컨테이너 안에는 애플리케이션을 구동하는데 필요한 &lt;strong&gt;라이브러리 및 실행 파일만 존재&lt;/strong&gt;합니다. 따라서 이미지로 만들었을 때 이미지의 용량 또한 가상머신에 비해서 대폭 줄어들죠.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;따라서 이미지를 만들어 배포하는 시간이 가상머신에 비해 빠르며, 가상화된 공간을 사용할 때 성능 손실도 거의 없다는 장점이 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;컨테이너&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88&quot; aria-label=&quot;컨테이너 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨테이너&lt;/h2&gt;
&lt;h3 id=&quot;컨테이너를-왜-써야할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%A5%BC-%EC%99%9C-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;컨테이너를 왜 써야할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨테이너를 왜 써야할까?&lt;/h3&gt;
&lt;p&gt;앞서 서버 가상화와, 컨테이너 가상화의 장단점을 살펴봤습니다. &quot;컨테이너를 왜 써야할까? 라는 질문에 어떻게 대답할 수 있을까요?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;음.. &quot;가상머신에 비해 가볍고, 빠르니까 쓰지 않을까?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이렇게 대답할 수 있을것 같은데, 아직 컨테이너가 무엇인지 제대로 햇갈릴겁니다. 조금 더 자세히 알아봅시다.&lt;/p&gt;
&lt;h3 id=&quot;컨테이너란--사전적-의미&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%9E%80--%EC%82%AC%EC%A0%84%EC%A0%81-%EC%9D%98%EB%AF%B8&quot; aria-label=&quot;컨테이너란  사전적 의미 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨테이너란 : 사전적 의미&lt;/h3&gt;
&lt;p&gt;컨테이너는 사전적 의미로 어떤 물체를 격리하는 공간을 뜻합니다. &lt;strong&gt;각각의 컨테이너는 격리된 상태로 다른 컨테이너들과 분리되어있죠.&lt;/strong&gt; 그렇다면 기술적 의미에서의 컨테이너는 무엇을 의미할까요?&lt;/p&gt;
&lt;h3 id=&quot;컨테이너란--기술적-의미&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%9E%80--%EA%B8%B0%EC%88%A0%EC%A0%81-%EC%9D%98%EB%AF%B8&quot; aria-label=&quot;컨테이너란  기술적 의미 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨테이너란 : 기술적 의미&lt;/h3&gt;
&lt;p&gt;저희가 흔히 알고있는 컨테이너는, &lt;strong&gt;컨테이너에 담긴 것들의 생명주기(life cycle) 들을 관리&lt;/strong&gt;합니다. 무언가의 생성, 운영, 제거까지의 생명주기를 얘기합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;가상화 관점에서의 컨테이너란 이미지의 목적에 따라 생성되는 프로세스 단위의 격리 환경입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;이미지는 간단히 컨테이너를 만들기위한 틀이라고 생각하고 넘어갑시다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;컨테이너-구조-뜯어보기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EA%B5%AC%EC%A1%B0-%EB%9C%AF%EC%96%B4%EB%B3%B4%EA%B8%B0&quot; aria-label=&quot;컨테이너 구조 뜯어보기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨테이너 구조 뜯어보기&lt;/h2&gt;
&lt;p&gt;컨테이너는 환경을 제공하며 프로세스의 생명주기를 관리합니다. 예를 들어봅시다. 스프링부트 애플리케이션과 Nginx 애플리케이션을 컨테이너를 통해 실행해보려고 합니다. 여기서 이미지의 목적은 스프링부트 애플리케이션, Nginx 애플리케이션입니다.&lt;/p&gt;
&lt;p&gt;보시듯이 컨테이너는 파일시스템과 &lt;strong&gt;격리된 시스템 자원 및 네트워크를 사용할 수 있는 독립된 공간을 가집니다.&lt;/strong&gt; 컨테이너가 실행되며, 프로세스가 실행되기에 필요한 자원들을 할당받고 프로세스를 실행합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/57b13568-ddc7-414d-b73c-02d2dac02536/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;또한 프로세스라는 것은 운영체제 위에서 실행되겠죠? 이 운영체제를 Host OS 이라고 표현합니다.&lt;/p&gt;
&lt;h3 id=&quot;host-os&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#host-os&quot; aria-label=&quot;host os permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Host OS&lt;/h3&gt;
&lt;p&gt;그렇다면 Host OS 입장에서는 컨테이너를 어떻게 바라볼까요? 앞서 말했듯이 &lt;strong&gt;컨테이너는 프로세스의 생명주기를 관리하며 실행하는 하나의 프로세스&lt;/strong&gt;라고 말할 수 있습니다. 즉 스프링부트 애플리케이션 프로세스를 직접 실행하나, 컨테이너로 실행하나 Host OS 입장에서는 똑같은 평범한 프로세스로 본다는 말입니다.&lt;/p&gt;
&lt;p&gt;마치 앞선 컨테이너 구조는, Host OS 입장에서는 아래와 같은 상황과 동일한것이죠.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/66a7e5e6-f988-4630-8289-479dc5e13017/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;docker-engine도커-엔진&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#docker-engine%EB%8F%84%EC%BB%A4-%EC%97%94%EC%A7%84&quot; aria-label=&quot;docker engine도커 엔진 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Docker Engine(도커 엔진)&lt;/h2&gt;
&lt;p&gt;그렇다면 컨테이너를 어떻게 관리할 수 있을까요? 사용자는 도커엔진이라는 것을 통해 컨테이너를 관리할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;도커엔진이란 유저가 컨테이너를 쉽게 사용할 수 있게해주는 주체입니다.&lt;/strong&gt;
역할을 정리해보자면 다음과 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;컨테이너 관리 : 컨테이너를 생명주기를 관리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;이미지 관리 : 컨테이너를 생성하기 위한 이미지를 관리합니다.&lt;/li&gt;
&lt;li&gt;볼륨 관리 : 컨테이너의 데이터를 저장하기 위한 저장소 역할을 하는 볼륨을 관리합니다.&lt;/li&gt;
&lt;li&gt;네트워크 관리 : 컨테이너의 접속을 관리하기 위한 네트워크를 관리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;도커엔진--명령어-입력시-로직&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%BB%A4%EC%97%94%EC%A7%84--%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%9E%85%EB%A0%A5%EC%8B%9C-%EB%A1%9C%EC%A7%81&quot; aria-label=&quot;도커엔진  명령어 입력시 로직 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도커엔진 : 명령어 입력시 로직&lt;/h2&gt;
&lt;p&gt;사용자가 도커 관련 명령어를 입력했을때 어떠한 플로우로 진행될까요?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/27ad5f3f-03d6-4e9f-af0f-6aa343e443d5/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;1.먼저 사용자는 도커 명령어로 도커 엔진에게 명령어를 보냅니다.&lt;/p&gt;
&lt;p&gt;2.이를 전달받은 도커 클라이언트는 /var/run/docker.sock 에 위치한 유닉스 소켓을 통해 도커 데몬의 API 를 호출합니다.&lt;/p&gt;
&lt;p&gt;3.도커 데몬은 명령어에 해당하는 작업을 수행하고, 도커 데몬의 API 를 호출합니다.&lt;/p&gt;
&lt;p&gt;4.도커 데몬은 명령어에 해당하는 작업을 수행하고, 수행 결과를 도커 클라이언트에게 반환하면 사용자에게 결과를 출력합니다.&lt;/p&gt;
&lt;h3 id=&quot;도커-데몬-dockerd-이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%BB%A4-%EB%8D%B0%EB%AA%AC-dockerd-%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;도커 데몬 dockerd 이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도커 데몬, dockerd 이란?&lt;/h3&gt;
&lt;p&gt;이때 dockerd 는 컨테이너를 생성하고 실행하며 이미지를 관리하는 주체입니다.
또 도커 프로세스가 실행되어 입력을 받을 준비가 된 상태를 도커 데몬이라고 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;지금까지 알아본 것중 핵심만을 정리해봅시다. 저희는 가상화의 필요성과 종류에 대해 알아봤고, 가상화 종류중에 컨테이너 가상화를 사용하는 이유에 대해 알아봤습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4a405bf7-221a-4c75-8cd3-e000f8f9a23f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;또한 프로세스의 생명주기를 관리하는 가상화 기술을 컨테이너에 대해 알아봤습니다. 이어서 컨테이너의 생명주기를 관리하는 도커엔진에 대해 알아봤습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금까지 가상화란 무엇이며, 가상화 종류중 컨테이너 가상화를 자세히 다루어보면서 도커 명령어를 입력시 어떻게 플로우가 돌아가는 것인지에 대해 내부 메커님즘을 알아봤습니다.&lt;/p&gt;
&lt;p&gt;또한 지금까지는 단인 서버에서의 단일 도커 엔진에 대해 다루어봤는데, 다음 포스팅에서는 여러개의 서버의 도커 엔진에 대해 다루어볼까합니다 😁&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.docker.com/&quot;&gt;https://docs.docker.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Nginx 의 keep-alive 를 조정하여 성능을 개선해보자!]]></title><description><![CDATA[TCP 4-way Handshaking 우선 저희는 TCP 4-way Handshaking 가 무엇인지 먼저 알아야합니다.  3-way handshake 가 클라이언트와 서버가 TCP 연결을 맺을때 사용한다면, 4-way handshake…]]></description><link>https://haon.site/haon/infra/nginx/keep-alive/</link><guid isPermaLink="false">https://haon.site/haon/infra/nginx/keep-alive/</guid><pubDate>Mon, 30 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;tcp-4-way-handshaking&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tcp-4-way-handshaking&quot; aria-label=&quot;tcp 4 way handshaking permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TCP 4-way Handshaking&lt;/h2&gt;
&lt;p&gt;우선 저희는 TCP 4-way Handshaking 가 무엇인지 먼저 알아야합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/06070a51-cafe-4136-9385-d34da31bec01/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;3-way handshake 가 클라이언트와 서버가 TCP 연결을 맺을때 사용한다면, &lt;strong&gt;4-way handshake 는 연결을 끊을때, 즉 세션을 종료할때 수행되는 절차입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;만약 3-way handshake 를 잘 모르시는분들은 &lt;a href=&quot;https://velog.io/@msung99/IP-TCP-UDP&quot;&gt;IP 프토로콜의 한계, TCP 3-way-handshake, Port&lt;/a&gt; 를 참고하시면 좋겠습니다.
또한 소켓이 생성되고, 소켓을 통해 handshaking 을 수행하는 것입니다.&lt;/p&gt;
&lt;p&gt;이때 소켓이 생성될 때는 3-way handshaking 을 통해서 생성되지만, 종료될때는 4-way handshaking 을 거쳐서 종료됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;4-way-handshaking-과정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-way-handshaking-%EA%B3%BC%EC%A0%95&quot; aria-label=&quot;4 way handshaking 과정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4-way handshaking 과정&lt;/h2&gt;
&lt;p&gt;소켓이 종료될때는, 즉 클라이언트와 서버가 연결이 끊어질때는 4단계를 거쳐서 종료됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1.클라아이언트가 서버에게 연결 종료를 요청합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;2.서버는 바로 종료시키는 것이 아니라, 단순히 ACK 만을 날립니다. 서버는 종료시키기 전에 본인도 마저 할일이 있기 때문에 바로 FIN 을 날리지 않고, 단순히 ACK를 날리고 CLOSE_WAIT 상태로 넘어갑니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3.서버는 본인이 할일을 다 마쳤다면 그제서야 FIN 을 날리고 연결을 종료하고자 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;4.클라이언트는 서버의 FIN 을 잘 받았다는 ACK 를 서버에게 보내게되고, 서버는 ACK 를 전달받으면 소켓을 timewait 시간동안 기다린 후, timewait 이 끝나면 그때서야 종료시킵니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그런데 이때 4번 과정에서 &lt;strong&gt;클라이언트는 ACK 를 날리고 난후 소켓이 종료(제거)될때 까지 TIME_WAIT 상태에 있게 된다는 점&lt;/strong&gt;을 주목하셔야 합니다.&lt;/p&gt;
&lt;p&gt;timewait 이 뭘까요? 지금부터 알아봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;timewait&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#timewait&quot; aria-label=&quot;timewait permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TimeWait&lt;/h2&gt;
&lt;h3 id=&quot;timewait-이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#timewait-%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;timewait 이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TimeWait 이란?&lt;/h3&gt;
&lt;p&gt;4-way handshake 에서 소켓이 바로 종료(제거)되지 않고, 일정시간동안 대기한 후 제거되어야 합니다. 이때 이 일정 대기시간을 TimeWait 이라고 부릅니다.&lt;/p&gt;
&lt;h3 id=&quot;timewait-은-왜-필요한가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#timewait-%EC%9D%80-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%9C%EA%B0%80&quot; aria-label=&quot;timewait 은 왜 필요한가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TimeWait 은 왜 필요한가?&lt;/h3&gt;
&lt;p&gt;time_wait 에 있는 동안에는 OS 커널이 IP 주소와 PORT 바인딩합니다. 때문에 해당 소켓을 종료시키려 했던것을 다시 취소하고 재활용하려고 해도 사용이 불가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/564ed27e-0ce4-482c-8f13-ae73b9393551/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그렇다면 이렇게 불편한 대기시간은 왜 필요할까요?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;요약 : 네트워크 상의 문제로 클라이언트가 ACK 를 보내주지 못하는 경우가 발생할 수 있다. 이런 경우를 대비해, 서버가 다시 FIN 을 보내서 클라이언트로부터 ACK 를 확실히 응답받을 때 까지 혹시모를 대기시간(timewait) 이 필요한것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;만약 time_wait 없이 클라이언트가 서버로 보낸 마지막 ACK 종료메시지 이후 바로 소켓을 종료했다고 가정해봅시다. 그런데 라우터 문제오 같이 네트워크 상에서 발생하는 어떤 원인에 의해 마지막 종료 메시지 ACK 가 서버에 도착하지 않을 수도 있습니다.&lt;/p&gt;
&lt;p&gt;이러면 서버는 소켓을 종료하지 못하고, 불안한 마음에 다시 한번 클라이언트에게 FIN 을 날립니다.&lt;/p&gt;
&lt;p&gt;그런데 timewait 대기시간없이 바로 종료되었다면, 이 마지막 FIN 역시 무했을테고 &lt;strong&gt;서버는 여전히 소켓이 종료되었는지, 또는 살아있는지 알지못하고 안절부절 못하고 있게 될 겁니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;하지만 TIME_WAIT에 있는 클라이언트의 소켓은 여전히 주소와 포트를 바인딩 하고 있는 상태이기 때문에, timewait 대기시간동안에 다시 날아 오는 서버의 FIN을 감지하고 다시한번 마지막 ACK를 날려 줄 수 있는 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx-를-통한-로드밸런싱&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EB%A5%BC-%ED%86%B5%ED%95%9C-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1&quot; aria-label=&quot;nginx 를 통한 로드밸런싱 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 를 통한 로드밸런싱&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/33d918ca-51ae-41f2-b304-7cab54a1bcc0/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Nginx-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1-%ED%99%98%EA%B2%BD%EC%9D%84-%EA%B5%AC%EC%B6%95%ED%95%B4-%ED%8A%B8%EB%9E%98%ED%94%BD-%EB%B6%84%EC%82%B0%EC%8B%9C%ED%82%A4%EA%B8%B0-feat.-%EB%AC%B4%EC%A4%91%EB%8B%A8%EB%B0%B0%ED%8F%AC&quot;&gt;[Nginx] 로드밸런싱 환경을 구축해 트래픽 분산시키기 (feat. 무중단배포)&lt;/a&gt; 에서도 보았듯이, 보통 proxy_pass 지시자를 통해 Nginx 서버에서 받은 요청을 넘겨줄 서버를 정의하는 지시자가 upstream 입니다.&lt;/p&gt;
&lt;h3 id=&quot;문제점1-34-way-handshaking-으로-인한-성능저하&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%A0%9C%EC%A0%901-34-way-handshaking-%EC%9C%BC%EB%A1%9C-%EC%9D%B8%ED%95%9C-%EC%84%B1%EB%8A%A5%EC%A0%80%ED%95%98&quot; aria-label=&quot;문제점1 34 way handshaking 으로 인한 성능저하 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문제점1) 3,4-way-handshaking 으로 인한 성능저하&lt;/h3&gt;
&lt;p&gt;겉보기에는 Nginx 서버를 중간에 배치하는데 별 문제가 없어보일 수 있지만, 사실 &lt;strong&gt;로드밸런서로 인해 성능 저하&lt;/strong&gt;의 문제를 불러볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/98bdd301-906a-4c25-94d0-e17afe79faa2/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위와 같이 클라이언트가 서버에게 요청을 보낸다면, 3-way handshake 가 2번 발생합니다. 당연한 말이죠?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/adcec2ea-0935-427e-b230-f754c0cfeac4/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;또한 4-way handshaking 까지 발생하면서, HTTP 통신을 하는데 handshaking 이 자주 발생하다보니 **네트워크 자원 낭비가 심해서, 성능이 저하될 수밖에 없죠. **&lt;/p&gt;
&lt;h3 id=&quot;문제점2-timewait-대기상태에-있는-소켓이-많아지는-경우--로컬포트-고갈&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%A0%9C%EC%A0%902-timewait-%EB%8C%80%EA%B8%B0%EC%83%81%ED%83%9C%EC%97%90-%EC%9E%88%EB%8A%94-%EC%86%8C%EC%BC%93%EC%9D%B4-%EB%A7%8E%EC%95%84%EC%A7%80%EB%8A%94-%EA%B2%BD%EC%9A%B0--%EB%A1%9C%EC%BB%AC%ED%8F%AC%ED%8A%B8-%EA%B3%A0%EA%B0%88&quot; aria-label=&quot;문제점2 timewait 대기상태에 있는 소켓이 많아지는 경우  로컬포트 고갈 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문제점2) Timewait 대기상태에 있는 소켓이 많아지는 경우 : 로컬포트 고갈&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;또한 4-way handshaking 이 자주 발생한다면, 대규모 트래픽이 발생한 경우 timewait 대기시간동안 제거되지 않고 있는 소켓이 대거 발생할 수 있습니다.&lt;/strong&gt; 이는 곧 로컬포트(local Port)가 고갈될 위험으로 이어지게 되죠.&lt;/p&gt;
&lt;p&gt;대규모 트래픽이 발생해서 로컬포트가 고갈되는 경우, 다시 로컬포트를 사용하고 싶어도 timewait 대기시간 60초가 지나고 나서야 다시 사용 가능해집니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;해결방안--keepalive-를-설정해주자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B4%EA%B2%B0%EB%B0%A9%EC%95%88--keepalive-%EB%A5%BC-%EC%84%A4%EC%A0%95%ED%95%B4%EC%A3%BC%EC%9E%90&quot; aria-label=&quot;해결방안  keepalive 를 설정해주자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;해결방안 : KeepAlive 를 설정해주자&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1ef76b8e-c230-4d02-92e3-de736c62352e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;http-의-connectionless비연결성-특징을-해결해보자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-%EC%9D%98-connectionless%EB%B9%84%EC%97%B0%EA%B2%B0%EC%84%B1-%ED%8A%B9%EC%A7%95%EC%9D%84-%ED%95%B4%EA%B2%B0%ED%95%B4%EB%B3%B4%EC%9E%90&quot; aria-label=&quot;http 의 connectionless비연결성 특징을 해결해보자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP 의 Connectionless(비연결성) 특징을 해결해보자.&lt;/h3&gt;
&lt;p&gt;저희는 HTTP 의 특징을 잘 파악하고, upstream 서버를 잘 최적화 해놓는다면 트래픽 처리에 대한 문제가 해결될 수 있습니다.&lt;/p&gt;
&lt;p&gt;HTTP 통신의 특징은 요청-응답이 오갈때 &lt;strong&gt;한번 연결을 맺은뒤로 데이터를 주고받은후에, 바로 연결을 끊어버린다는 특성(비연결성) 을 지닙니다.&lt;/strong&gt; 즉, 계속해서 실시간으로 클라이언트와 서버가 주고받을 데이터가 많아도, 매번 3,4-way handshaking 이 발생한다는 점입니다.&lt;/p&gt;
&lt;p&gt;결국 클라이언트는 데이터를 계속 주고받고 싶다면, TCP 연결(handshaking) 을 처음부터 또 해야하며, 이 과정에 불필요하게 TCP handshake 가 한번 더 발생합니다.&lt;/p&gt;
&lt;h3 id=&quot;keepalive-로-연결을-계속-유지시켜보자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#keepalive-%EB%A1%9C-%EC%97%B0%EA%B2%B0%EC%9D%84-%EA%B3%84%EC%86%8D-%EC%9C%A0%EC%A7%80%EC%8B%9C%EC%BC%9C%EB%B3%B4%EC%9E%90&quot; aria-label=&quot;keepalive 로 연결을 계속 유지시켜보자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Keepalive 로 연결을 계속 유지시켜보자.&lt;/h3&gt;
&lt;p&gt;저희는 Nginx 의 Keepalive 라는것을 별도로 설정해주면 불필요한 handshake 과정을 줄일 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;keepalive 는 클라이언트의 연결 요청을 처리한 후 바로 연결을 끊는것이 아니라, 연결을 끊지않고 일정시간 대기하는 시간&lt;/strong&gt;을 의미합니다. 이 keepAlive 는 keepalive timeout 설정으로 대기시간을 지정해줄 수 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;keepalive 를 지정해주면 이렇게 클라이언트의 요청이 여러번 있을때, 클라이언트는 한번만 TCP 세션을 연결하고 이후로는 Keepalive 시간동안 동일한 세션으로 데이터를 계속 주고받을 수 있게됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;결국 불필요하게 TCP 세션 연결을 하지 않아도되고, 더 빠르게 통신이 가능해집니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;upstream-서버가-keepalive-설정이-없다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#upstream-%EC%84%9C%EB%B2%84%EA%B0%80-keepalive-%EC%84%A4%EC%A0%95%EC%9D%B4-%EC%97%86%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;upstream 서버가 keepalive 설정이 없다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Upstream 서버가 keepAlive 설정이 없다면?&lt;/h2&gt;
&lt;p&gt;앞서 설명드린 내용들은 다시 리마인딩 느낌으로 설명드리면서, keepalive 설정이 없는 경우를 설명드리겠습니다.&lt;/p&gt;
&lt;p&gt;보통 upstream 서버에 대한 설정은 아래와 같이 진행할겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;upstream backend&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  server &lt;span class=&quot;token number&quot;&gt;111.11&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  server &lt;span class=&quot;token number&quot;&gt;222.22&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/dd1a2ea6-4802-46b1-abd6-f72a04137305/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이런 기본 설정을 하게딜 경우의 문제점을 뭘까요? 앞서 계속 말한 내용입니다.
바로 Nginx 와 WAS 를 연결하는 내부 통신에도 매 요청마다 TCP 세션을 만들고 handshake 가 일어난다는 점입니다.&lt;/p&gt;
&lt;p&gt;Nginx 에서 WAS 로 넘기는 것도 똑같은 TCP 통신이기 때문에 handshake 를 하게되고, keepalive 가 켜져있기 않기 때문에 한번의 요청만 처리한 후 소켓이 끊어집니다.&lt;/p&gt;
&lt;p&gt;결국 문제점이 발생합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;keepalive 를 통해 소켓이 유지되지 않기 때문에 다수의 timewait 소켓이 만들어져서 로컬포트가 고갈된 다는점,&lt;/li&gt;
&lt;li&gt;또 불필요한 handshake 가 일어남으로써 응답속도 지연을 일으킬 수 있다는 점입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;=&gt; 사실 timewait 이 생긴다고 해서 성능에 큰 지장은 없습니다. 다만 Nginx 입장에서는 로컬포트를 사용하게 되고 초당 요청 빈도수가 높은 서버는 로컬포트의 고갈로 이어지게 되는 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;upstream-서버에-keepalive-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#upstream-%EC%84%9C%EB%B2%84%EC%97%90-keepalive-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;upstream 서버에 keepalive 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Upstream 서버에 keepalive 설정&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;upstream backend &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	server &lt;span class=&quot;token number&quot;&gt;111.11&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  	server &lt;span class=&quot;token number&quot;&gt;222.22&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
    keepalive &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// keepalive 갯수&lt;/span&gt;
    keepalive_timeout &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// keeplive의 생존 시간 (초)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;여러 번의 GET 요청 등이 있을 때 클라이언트는 한 번만 TCP 세션을 연결하고 생존 시간동안 동일한 세션으로 데이터를 주고 받을 수 있습니다.&lt;/p&gt;
&lt;p&gt;결론적으로 불필요한 timewait 대기상태의 소켓이 생성되지 않게 막아줍니다. Nginx 와 WAS 서버간에 불필요한 통신을 최소화해주는 것이죠.&lt;/p&gt;
&lt;h3 id=&quot;keepalive-의-개수는-얼만큼-설정할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#keepalive-%EC%9D%98-%EA%B0%9C%EC%88%98%EB%8A%94-%EC%96%BC%EB%A7%8C%ED%81%BC-%EC%84%A4%EC%A0%95%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;keepalive 의 개수는 얼만큼 설정할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;keepalive 의 개수는 얼만큼 설정할까?&lt;/h3&gt;
&lt;p&gt;keepalive 의 개수는 얼마나 설정하고, 생존시간은 얼마로 설정할지 고민될 수 있습니다. 이에대한 정해진 정답은 없습니다. 서비스마다 처리해야할 트래픽양에 따라서 결정하면 되죠. &lt;strong&gt;keepalive 수는 성능 테스트를 하며 조절하면 됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;보통은 16~32개를 넘어갈 일은 없어보입니다. 하지만 트래픽, 서버 사양에 따라서 1000,2000개를 넘게 설정해야 할수도 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;기타-유의사항&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%ED%83%80-%EC%9C%A0%EC%9D%98%EC%82%AC%ED%95%AD&quot; aria-label=&quot;기타 유의사항 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기타 유의사항&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;upstream backend &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	server &lt;span class=&quot;token number&quot;&gt;111.11&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.111&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  	server &lt;span class=&quot;token number&quot;&gt;222.22&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.222&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
    keepalive &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// keepalive 갯수&lt;/span&gt;
    keepalive_timeout &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// keeplive의 생존 시간 (초)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

server&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
  location &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;http&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    proxy_pass http&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;backend&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    proxy_http_version &lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    proxy_set_header &lt;span class=&quot;token class-name&quot;&gt;Connection&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;유의사항이 있다면 HTTP 버전은 1.1로 지정되어야하고, Connection header 는 빈값으로 전달해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;또한 웹소켓과 관련한 애플리케이션에서는 작동하지 않을 수 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;자세한 설정정보는 Nginx 공식문서 &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive&quot;&gt;Nginx Docs&lt;/a&gt; 를 참고하자!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;성능차이&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B1%EB%8A%A5%EC%B0%A8%EC%9D%B4&quot; aria-label=&quot;성능차이 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;성능차이&lt;/h2&gt;
&lt;p&gt;upstream keepalive 를 적용하면, Nginx 를 중간에 웹서버로 배치하면서 발생하는 성능 저하가 발생하긴합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;결국 WAS 서버만을 사용하는 것보다 성능 저하가 일어나긴 하지만, upstream keepalive 를 적용하지 않았을 때 보다는 더 좋은 성능을 보입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;keepalive-의-단점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#keepalive-%EC%9D%98-%EB%8B%A8%EC%A0%90&quot; aria-label=&quot;keepalive 의 단점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Keepalive 의 단점&lt;/h2&gt;
&lt;p&gt;몰론 Keepalive 에도 장점만 있는것은 아닙니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;메모리 사용의 증가 : KeepAlive를 사용 하는 것은 서버의 메모리 사용을 늘리게 됩니다. Nginx 프로세스들은 연결된 접속(Connections)에서 새로운 요청이 있을 때까지 접속(Connection)을 열고 기다려야만 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;요청을 받아들이지 못하는 경우 발생 : 사용자가 많다면 연결이 늘어나서 새로운 사용자를 받아들이지 못하는 경우가 빈번히 일어납니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그래서 사용자가 많고 유동이 많은 서비스에서는 사용이 권장되지 않습니다.&lt;/p&gt;
&lt;p&gt;프로세스들이 대기하는 동안 그들은 다른 사용자(Clients)들이 서비스를 위해 사용 해야 하는 RAM을 차지 하고 있는겁니다.&lt;/p&gt;
&lt;p&gt;만약 KeepAlive를 끄게 된다면 소수의 Nginx 프로세스들은 남아서 활동 할 수 있게 됩니다. 메모리 사용을 줄이는 동시에 Nginx 하여금 더 많은 사용자들에게 서비스를 제공 할 수 있게 되는 것이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive&quot;&gt;Nginx Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@kimjiwonpg98/serverkeepalive%EC%99%80-timewait%EC%9D%98-%EC%83%81%EA%B4%80%EA%B4%80%EA%B3%84&quot;&gt;[server]keepalive와 timewait의 상관관계&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devahea.github.io/2018/04/23/server-ec-97-90-ec-84-9c-eb-a7-8c-eb-82-a8-ec-9d-84-ec-b6-94-ea-b5-ac-ed-95-98-eb-a9-b4-ec-95-88-eb-90-98-eb-8a-94-ea-b1-b8-ea-b9-8c/&quot;&gt;2018 세미나 - Server에서 만남을 추구하면 안되는 걸까&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://brunch.co.kr/@alden/11&quot;&gt;nginx upstream 성능 최적화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kamang-it.tistory.com/599&quot;&gt;[Web]서버와의 연결을 계속? Keep Alive!&lt;/a&gt;
&lt;a href=&quot;https://colinch4.github.io/2020-07-30/t-14/&quot;&gt;14. 서버를 어떻게 세팅해야 할까?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bangu4.tistory.com/74&quot;&gt;[네트워크] 3-way / 4-way Handshake 란?
&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Nginx로 http event 를 영역을 튜닝하여 성능 개선하기]]></title><description><![CDATA[아래의 명령으로 현상태를 조회해봅시다. 참고로 저는 2Core CPU 를 사용하고 있습니다.  보듯이 1개의 Master Process 가 존재하며, 2개의 Worker Process 를 사용하고 있음을 알 수 있습니다. CPU…]]></description><link>https://haon.site/haon/infra/nginx/improvement/</link><guid isPermaLink="false">https://haon.site/haon/infra/nginx/improvement/</guid><pubDate>Sun, 29 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;아래의 명령으로 현상태를 조회해봅시다. 참고로 저는 2Core CPU 를 사용하고 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;systemctl nginx status&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5e8c4c48-7759-4ac9-b87c-ca38d33af73e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;보듯이 1개의 Master Process 가 존재하며, 2개의 Worker Process 를 사용하고 있음을 알 수 있습니다. CPU 코어 한개당 하나의 Worker Process 가 생성되고 작업을 수행하기 때문이죠.&lt;/p&gt;
&lt;h3 id=&quot;1worker-process-는-몇개가-좋을까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1worker-process-%EB%8A%94-%EB%AA%87%EA%B0%9C%EA%B0%80-%EC%A2%8B%EC%9D%84%EA%B9%8C&quot; aria-label=&quot;1worker process 는 몇개가 좋을까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.Worker Process 는 몇개가 좋을까?&lt;/h3&gt;
&lt;p&gt;Worker Process 는 무작정 많이 할당한다고해서 좋은 것이 아닙니다. 오히려 자원만 낭비하고, 성능저하의 문제까지 불러올 수도 있기 때문이죠.&lt;/p&gt;
&lt;p&gt;따라서 &lt;strong&gt;물리적인 CPU 코어 개수만큼 Worker Process 를 할당하는 것이 가장 이상적&lt;/strong&gt;이며, nginx.conf 에서 auto 를 지정해주면 자동으로 CPU 코어 개수에 알맞게 프로세스를 생성해줍니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;각 코어는 서로 프로세스를 공유하지 않기때문에, 모든 코어당 1개의 Worker Process 를 사용하면 전체 서버의 리소스를 최대로 사용하는 셈입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그런데 때로는 10&lt;del&gt;20% 정도의 CPU 코어는 운영체제를 위해 남겨두는 방법도 좋습니다. 예를들어 20core CPU 를 구성했다면, Nginx 에 대해 사용할 코어수를 16&lt;/del&gt;18개 정도로 할당하고, 나머지 2~4 는 OS 용으로 남겨두는 것도 좋습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/24794db8-8763-4152-924d-a2af76176f0a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-커넥션-허용-개수&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EC%BB%A4%EB%84%A5%EC%85%98-%ED%97%88%EC%9A%A9-%EA%B0%9C%EC%88%98&quot; aria-label=&quot;2 커넥션 허용 개수 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 커넥션 허용 개수&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;각 Worker 프로세스 별 최대 허용 커넥션 개수&lt;/strong&gt; 또한 설정해줄 수 있습니다. Nginx 의 Worker.프로세스들이 커넥션을 더 수용할 수 있음에도 불구하고 이를 설정하지 않는다면 최적화 면에서 크게 아쉬울겁니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;각 Worker Process 의 최대 허용 커넥션 수 : 1024개&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;아래 명령으로 CPU 코어의 커넥션 허용수를 눈으로 확인해봅시다. 보듯이 1024개 인것을 확인 가능합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ulimit -n&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/dfc2c774-5475-41fa-9024-1ad9ba0840f3/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;core의 최대 처리수가 1024이므로, nginx의 max 값도 1024로 설정해서 각 Worker 프로세스들이 커넥션을 처리하도록 해주는것이 가장 이상적입니다. worker connection 변수 값은 event content 안에 작성합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;worker_processes auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
pid &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;run&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
include &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;modules&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;enabled&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;

events &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        worker_connections &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        # multi_accept on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

http &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;최대 Request 동시 처리수는?&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;위와 같이 1024 로 지정해준다면, &lt;strong&gt;Worker Process 개수 x 1024&lt;/strong&gt; 개의 요청을 동시에 처리할 수 있는겁니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-worker_rlimit_nofile&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-worker_rlimit_nofile&quot; aria-label=&quot;3 worker_rlimit_nofile permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. worker_rlimit_nofile&lt;/h3&gt;
&lt;p&gt;worker_rlimit_nofile 이란 Worker Process 가 열수있는 최대로 열수있는 파일 수에 대한 제한을 설정하여 처리량을 늘려주는 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;worker_processes auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
worker_rlimit_nofile &lt;span class=&quot;token number&quot;&gt;204800&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
pid &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;run&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
include &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;modules&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;enabled&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;

events &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;event-관련&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#event-%EA%B4%80%EB%A0%A8&quot; aria-label=&quot;event 관련 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Event 관련&lt;/h2&gt;
&lt;h3 id=&quot;epoll&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#epoll&quot; aria-label=&quot;epoll permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;epoll&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;worker_processes auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
pid &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;run&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
include &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;etc&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nginx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;modules&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;enabled&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;

events &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        worker_connections &lt;span class=&quot;token number&quot;&gt;2048&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        # multi_accept on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        use epoll&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

http &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Nginx 처리 속도 관련해서 설정할 수 있는 옵션입니다.&lt;/p&gt;
&lt;h3 id=&quot;nginx-의-singlethread&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EC%9D%98-singlethread&quot; aria-label=&quot;nginx 의 singlethread permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 의 SingleThread&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c2babc47-6088-4dfd-8d52-e64b76977f5f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;non-blocking I/O를 사용하는 Single Thread 방식의 Nginx에서는 자신에게 연결되어 있는 socket에 읽을 데이터가 있는지 계속 확인하는데 자원을 낭비할 수 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;요약 : Linux 2.6 이상에서 사용하는 효율적인 Event 처리방식
epoll 방식은 nginx의 모든 소켓 파일을 찾아보는게 아니라 &lt;strong&gt;현재 활성화 된 소켓만 확인해서 연결을 설정&lt;/strong&gt;해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;epoll은 linux 소켓을 관리하는 방법중 하나인데 이외에도 poll, select 방식이 있습니다. 모든 소켓파일을 확인하는 poll 방식에 비해서 업그레이드 된 버전입니다.&lt;/p&gt;
&lt;p&gt;이 중 poll과 select는 해당 프로세스에 연결된 모든 connection file을 스캔하지만, epoll은 수천개의 file descriptor를 처리할 수 있도록 보다 효율적인 알고리즘을 사용하고 있어 대량 요청이 발생하는 시스템에 적합하다고 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;http-영역&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-%EC%98%81%EC%97%AD&quot; aria-label=&quot;http 영역 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;http 영역&lt;/h2&gt;
&lt;p&gt;http 블록은 nginx로 들어오는 웹 트래픽에 대한 처리 방법과 방향을 설정해주는 블록입니다. 이에 관한 튜닝도 어떻게 진행할지 알아봅시다.&lt;/p&gt;
&lt;h3 id=&quot;1keepalive-관련&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1keepalive-%EA%B4%80%EB%A0%A8&quot; aria-label=&quot;1keepalive 관련 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.keepalive 관련&lt;/h3&gt;
&lt;p&gt;keepalive 에 대한 이론 설명은 &lt;a href=&quot;https://velog.io/@msung99/Nginx-upstream-%EC%84%9C%EB%B2%84%EC%9D%98-KeepAlive-%EB%A5%BC-%EC%A1%B0%EC%A0%88%ED%95%B4-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94%ED%95%98%EA%B8%B0&quot;&gt;[Nginx] upstream 서버의 KeepAlive 를 조절해 성능 최적화하기&lt;/a&gt; 에서 설명했습니다.&lt;/p&gt;
&lt;p&gt;keepalive 를 nginx 와 upstream 서버간에 조절 가능한것처럼, 클라이언트와 nginx 사이에서도 조절가능합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;upstream 과 달라진게 있다면, keepalive_requests 옵션입니다.
keepalive_requests 이란 클라이언트가 커넥션을 계속 유지하며 수행할 수 있는 요청 수입니다. (upstream 서버에서는 keepalive 라는 옵션을 사용)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;http&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  keepalive_requests &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (keepalive 갯수)&lt;/span&gt;
  keepalive_timeout &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (keeplive의 생존 시간)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// =&gt; 최대 1만개의 클라이언트 요청을 유지한다. 각 커넥션은 30초동안 유지된다.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-reset_timedout_connection&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-reset_timedout_connection&quot; aria-label=&quot;2 reset_timedout_connection permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. reset_timedout_connection&lt;/h3&gt;
&lt;p&gt;서버가 응답하지 않는 클라이언트에 대한 커넥션을 닫을 수 있도록 허용하는 옵션입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;http&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  keepalive_requests &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  keepalive_timeout &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  reset_timedout_connection on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 응답하지 않는 클라이언트에 대한 연결을 자동으로 닫아버림&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;3-send_timeout&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-send_timeout&quot; aria-label=&quot;3 send_timeout permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. send_timeout&lt;/h3&gt;
&lt;p&gt;클라이언트에다 Response 을 전송할때 제한된 시간입니다. 응답이 없거나 느린 클라이언트를 제한하는 것입니다.
send_timeout에 지정한 시간안에 client가 아무것도 받지 못하면 connection은 닫힙니다. 디폴트 값은 60초입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;http&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  keepalive_requests &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  keepalive_timeout &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  reset_timedout_connection on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  send_timeout &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 클라이언트에게 Response 를 보내주는 시간이 2초 이상 걸리면&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 커넥션을 종료한다.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;4-client_header_timeout&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-client_header_timeout&quot; aria-label=&quot;4 client_header_timeout permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. client_header_timeout&lt;/h3&gt;
&lt;p&gt;request 의 Header 정보를 읽는데 설정된 timeout 시간입니다. client_header_timeout에 지정한 시간안에 client가 헤더를 전송하지 않으면 요청은 408(Request Time-out)로 끝나며, 디폴트 값은 60초입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;http&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  keepalive_requests &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  keepalive_timeout &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  reset_timedout_connection on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  send_timeout &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  client_header_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 클라이언트가 60초안에 Header 를 전송 안하면&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 408 에러를 리턴&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;5-client_body_timeout&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-client_body_timeout&quot; aria-label=&quot;5 client_body_timeout permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. client_body_timeout&lt;/h3&gt;
&lt;p&gt;반대로 request 의 Body 정보를 읽는데 설정된 timeout 시간입니다. (디폴트 60초)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;http&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  keepalive_requests &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  keepalive_timeout &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  reset_timedout_connection on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  send_timeout &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  client_header_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  client_body_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 클라이언트가 60초안에 Body 를 전송 안하면&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 408 에러를 리턴&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;6-proxy_read_timeout&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-proxy_read_timeout&quot; aria-label=&quot;6 proxy_read_timeout permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. proxy_read_timeout&lt;/h3&gt;
&lt;p&gt;proxied server 로부터 응답을 읽는데 설정한 timeout 시간입니다.
전체 응답 전송 timeout 시간이 아니라 두개의 연속적인 읽기 작업 사이의 timeout 시간입니다.&lt;/p&gt;
&lt;p&gt;proxy_read_timeout에 지정한 시간안에 proxied server가 아무것도 전송하지 않으면 connection은 닫히며, 디폴트 값은 60초이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;http&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  keepalive_requests &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  keepalive_timeout &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  reset_timedout_connection on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  send_timeout &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  client_header_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  client_body_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  proxy_read_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;7-proxy_send_timeout&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#7-proxy_send_timeout&quot; aria-label=&quot;7 proxy_send_timeout permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7. proxy_send_timeout&lt;/h3&gt;
&lt;p&gt;반대로 proxied server로 요청을 전송하는데 설정한 timeout 시간입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;http&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  keepalive_requests &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  keepalive_timeout &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  reset_timedout_connection on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  send_timeout &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  client_header_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  client_body_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  proxy_read_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  proxy_send_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;8-sendfile&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#8-sendfile&quot; aria-label=&quot;8 sendfile permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;8. sendfile&lt;/h3&gt;
&lt;p&gt;nginx에서 정적파일을 보내도록 설정하는 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;http&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  keepalive_requests &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  keepalive_timeout &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  reset_timedout_connection on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  send_timeout &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  client_header_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  client_body_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  proxy_read_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  proxy_send_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  sendfile on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;9-tcp_nopush&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#9-tcp_nopush&quot; aria-label=&quot;9 tcp_nopush permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;9. tcp_nopush&lt;/h3&gt;
&lt;p&gt;클라이언트로 패킷이 전송되기 전에 버퍼가 가득 찼는지 확인하여, 다 찼으면 패킷을 전송하도록 하여 네트워크 오버헤드를 줄이도록 설정하는 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;http&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  keepalive_requests &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  keepalive_timeout &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  reset_timedout_connection on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  send_timeout &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  client_header_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  client_body_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  proxy_read_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  proxy_send_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  sendfile on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  tcp_nopush on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;10-tcp_nodelay&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#10-tcp_nodelay&quot; aria-label=&quot;10 tcp_nodelay permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;10. tcp_nodelay&lt;/h3&gt;
&lt;p&gt;소켓이 패킷 크기에 상관없이 버퍼에 데이터를 보내도록합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;http&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  keepalive_requests &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  keepalive_timeout &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  reset_timedout_connection on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  send_timeout &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  client_header_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  client_body_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  proxy_read_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  proxy_send_timeout &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  sendfile on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  tcp_nopush on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 tcp_nodeplay on&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;적용한-모습&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%81%EC%9A%A9%ED%95%9C-%EB%AA%A8%EC%8A%B5&quot; aria-label=&quot;적용한 모습 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;적용한 모습&lt;/h3&gt;
&lt;p&gt;최종적으로 nginx.conf 의 http 영역을 아래와 같이 적용해봤습니다. 위에서 다 살펴본 내용들이죠?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/19f6d3c1-a5af-465d-b306-5deab2613100/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Nginx 의 동작원리와 등장배경, 역사에 대해 알아보자!]]></title><description><![CDATA[시작에 앞서 : 왜 글을 작성하는가? 서버 개발자자면 웹서버(WS) 의 세계 1,2위를 차지하고 있는 Nginx 와 Apache 를 꼭 한번씩 사용해보셨을 겁니다. 그런데 많은 분들은 왜 Nginx…]]></description><link>https://haon.site/haon/infra/nginx/core-concept/</link><guid isPermaLink="false">https://haon.site/haon/infra/nginx/core-concept/</guid><pubDate>Wed, 25 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;시작에-앞서--왜-글을-작성하는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%9E%91%EC%97%90-%EC%95%9E%EC%84%9C--%EC%99%9C-%EA%B8%80%EC%9D%84-%EC%9E%91%EC%84%B1%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-label=&quot;시작에 앞서  왜 글을 작성하는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시작에 앞서 : 왜 글을 작성하는가?&lt;/h2&gt;
&lt;p&gt;서버 개발자자면 웹서버(WS) 의 세계 1,2위를 차지하고 있는 Nginx 와 Apache 를 꼭 한번씩 사용해보셨을 겁니다. 그런데 많은 분들은 왜 Nginx를 써야하는지를 잘 모르실겁니다. &lt;strong&gt;저희는 그저 대중적으로 Nginx 를 많이 쓰니까 그냥 써야할까요?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;심지어 왜 Apache 와 Nginx 의 등장배경과 내부동작 구조를 잘 모르신다면, 오해하고, 잘못된 사실을 제대로 알고있다고 착각하고 웹서버를 사용할일이 허다할겁니다. &lt;strong&gt;예를들어 Apache 와 Nginx 가 서로 우호적인 관계이면서 동시에 상호작용을 할 수 있는 관계에 있음에도 불구하고, 적대관계에 있다고 오해하고 계신분들도 정말 많습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이번 포스팅에서는 1995년부터 시작하는 아파치 서버의 등장과 한계점, 그리고 Nginx 의 등장과 내부 메커니즘을 깊게 이해하는데 초점을 두었습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;많은 분들이 제 포스팅 내용을 보시고, Nginx 에 대해 다시 깊게 학습하시는 계기가 되었으면 합니다 😉&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;어떤-내용을-배워갈-수-있을까--키워드를-중점으로&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%96%A4-%EB%82%B4%EC%9A%A9%EC%9D%84-%EB%B0%B0%EC%9B%8C%EA%B0%88-%EC%88%98-%EC%9E%88%EC%9D%84%EA%B9%8C--%ED%82%A4%EC%9B%8C%EB%93%9C%EB%A5%BC-%EC%A4%91%EC%A0%90%EC%9C%BC%EB%A1%9C&quot; aria-label=&quot;어떤 내용을 배워갈 수 있을까  키워드를 중점으로 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어떤 내용을 배워갈 수 있을까? : 키워드를 중점으로&lt;/h2&gt;
&lt;p&gt;여러분들은 이번 제 포스팅을 통해 다음과 같은 내용을 얻어갈 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1995년부터 시작한 아파치 서버의 등장과, 2023년 현재에 이르기까지 왜 아파치와 Nginx 서버가 대중적으로 널리 사용하는 것인지 &quot;근본적인 이유를&quot; 깊게 이해할 수 있습니다!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Nginx 의 중점을 두었지만, Apache 서버의 등장배경과 내부 메커니즘 또한 동시에 소개합니다. 이 둘을 비교하면서 왜 Nginx 가 동시성 처리에 유리한 것인지를 알려드립니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;로드밸런싱, C10K, 쓰레드풀, Nginx의 Process 구조, SSL 터미네이션, 부하와 분산처리 등 다양한 문제를 해결할 수 있는 Nginx 의 내부 메커니즘을 이해할 수 있습니다!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;기초적인 CS 지식을 잘 모르시는 분들이 보기엔 다소 어려운 포스팅일 수 있습니다. 일단 포스팅을 읽어보신 후, 키워드들을 추가적으로 학습하시면 CS 학습에도 도움이 되실겁니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1995년--apache-서버의-탄생&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1995%EB%85%84--apache-%EC%84%9C%EB%B2%84%EC%9D%98-%ED%83%84%EC%83%9D&quot; aria-label=&quot;1995년  apache 서버의 탄생 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1995년 : Apache 서버의 탄생&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1ccd3bf9-73a6-41f0-8e8e-9ca53df0df9e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;최초의 웹서서 NSCA 와 HTTPd 에서 발생하는 버그에 대한 불편함으로, 대안으로 등장한 것이 Apache 서버이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;아파치 서버가 처음열린 1995년으로 가봅시다. 그 당시에는 유닉스 기반으로 만들어진 최초의 웹서버(WS)인 NSCA 와 HTTPd 가 있었습니다. 그런데 이 프로그램은 버그가 굉장히 많아서 개발자들이 사용할 때 불편함을 걲였죠.&lt;/p&gt;
&lt;p&gt;그래서 뛰어난 몇몇 개발자들이 그 버그를 수정하기 시작했습니다. 그러면서 그 구조를 변경하고, 기능을 추가해서 만든것이 바로 아파치 서버입니다. 그러면 이아파치 서버의 구조가 어떤지 간단하게 알아봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;apache-서버의-구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#apache-%EC%84%9C%EB%B2%84%EC%9D%98-%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;apache 서버의 구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Apache 서버의 구조&lt;/h2&gt;
&lt;p&gt;아파치 서버는 요청이 들어오면 커넥션(Connection) 을 생성하기 위해 프로세스를 생성합니다. 그래서 &lt;strong&gt;새로운 클라이언트의 요청이 들어올 때마다 새로운 프로세스를 만듭니다.&lt;/strong&gt; 이는 유닉스 계열의 OS 가 네트워크 커넥션을 형성하는 모델을 그대로 적용한 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;prefork-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#prefork-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;prefork 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PreFork 방식&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/2f545b06-5bd0-4ba8-bbeb-a5615fcef9d0/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PreFork : 새로운 클라이언트로부터 요청이 들어오면 미리 만들어놓은 프로세르를 가져다 사용하는 방식&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;그런데 프로세스를 만드는 것은 작업이 정말 오래걸리다보니, 요청이 들어오기전에 프로세스를 미리 만들어주는 PreFork 방식을 사용했습니다.&lt;/p&gt;
&lt;p&gt;그래서 새로운 클라이언트로부터 요청이 들어올떄마다 미리 만들어준 프로세스를 가져다 사용하는 방식을 사용했습니다. 만일 만들어놓은 모든 프로세스가 할당된다면, 추가로 프로새스를 만들었죠.&lt;/p&gt;
&lt;h3 id=&quot;prefork-의-장점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#prefork-%EC%9D%98-%EC%9E%A5%EC%A0%90&quot; aria-label=&quot;prefork 의 장점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Prefork 의 장점&lt;/h3&gt;
&lt;p&gt;이러한 구조를 개발하기 쉬워서, 개발자들은 다양한 모듈을 만들고 아파치 서버에 빠르게 기능을 추가할수 있습니다. 이렇게 &lt;strong&gt;아파치 서버는 동적 컨텐츠를 쉽게 처리할 수 있게됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;또한 &lt;strong&gt;확장성이 뛰어납니다.&lt;/strong&gt; 요청을 받고 응답을 처리하는 과정을 하나의 서버에서 해결하기 좋습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1999년--c10k-처리에-대한-한계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1999%EB%85%84--c10k-%EC%B2%98%EB%A6%AC%EC%97%90-%EB%8C%80%ED%95%9C-%ED%95%9C%EA%B3%84&quot; aria-label=&quot;1999년  c10k 처리에 대한 한계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1999년 : C10K 처리에 대한 한계&lt;/h2&gt;
&lt;p&gt;이렇게 문제없이 사용하던중, 아파치 서버는 C10K 라는 문제를 맞딱뜨리게 됩니다. 1999년 이 시기에는 인터넷 트레픽이 계속 증가하는 상황이였습니다. 점점 컴퓨터 보급량이 많아지고 요청이 많아짐에 따라, &lt;strong&gt;서버가 많은양의 커넥션을 수용하지 못하는 것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이전에는 서버가 처리할 요청양이 그 당시 기술로 감당할 수 있었는데, 이때부터 아차피 서버의 한계가 두각됩니다.&lt;/p&gt;
&lt;h3 id=&quot;c10k&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#c10k&quot; aria-label=&quot;c10k permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;C10K&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/3a936f71-9858-4076-85db-cc885c06eece/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;C10K 란 Connection 10000 Problem 의 약자로, 즉 커넥션 1만개에 대해 발생한 문제를 뜻합니다. &lt;strong&gt;서버에 동시에 연결된 커넥션이 많아졌을때 더 이상 커넥션을 만들지 못하는 문제가 발생&lt;/strong&gt;한 것이죠.&lt;/p&gt;
&lt;p&gt;게다가 한 클라이언트는 하나의 커넥션을 통해 여러개의 요청을 보낼수도 있습니다. 또 커넥션 하나를 생성하는데는 오랜 시간이 걸립니다. 여러 절차가 따르기 때문이죠.&lt;/p&gt;
&lt;p&gt;이러한 동시간대에 연결된 커넥션의 개수는 정말 많았고, 서버는 커넥션 수가 1만 단위가 넘어가는 순간 더 이상 커넥션을 생성하지 못하는 상황에 놓이게 된것입니다.&lt;/p&gt;
&lt;h3 id=&quot;apache-서버가-c10k-를-해결하지-못한-이유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#apache-%EC%84%9C%EB%B2%84%EA%B0%80-c10k-%EB%A5%BC-%ED%95%B4%EA%B2%B0%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%9D%B4%EC%9C%A0&quot; aria-label=&quot;apache 서버가 c10k 를 해결하지 못한 이유 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Apache 서버가 C10K 를 해결하지 못한 이유&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;프로세스가 너무 많이 생성되어, 메모리 부족현상으로 이어졌기 때문이다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;C10K의 문재는 하드웨어는 문제가 될 것이 없었습니다. 하드웨어는 그 당시의 컨텐츠 용량을 담는데 성능이 충분히 좋았죠.&lt;/p&gt;
&lt;p&gt;C10K 를 해결하지 못했던 원인은 아파치 서버의 구조에 있었습니다. 아까 설명드린 서버 구조를 다시 리마인딩 해봅시다. &lt;strong&gt;아파치 서버의 구조는 커넥션이 생성될 때마다 한 프로세스가 할당되기 때문에, 동시에 처리하고 있는 커넥션이 많아지면 그만큼 생성되는 프로세스가 많아집니다.&lt;/strong&gt; 결국 프로세스가 많아지면 메모리 부족현상으로 이어지게 된 것이였죠.&lt;/p&gt;
&lt;h3 id=&quot;apache-서버의-한계점-정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#apache-%EC%84%9C%EB%B2%84%EC%9D%98-%ED%95%9C%EA%B3%84%EC%A0%90-%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;apache 서버의 한계점 정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Apache 서버의 한계점 정리&lt;/h3&gt;
&lt;p&gt;Apache 서버의 한계점을 정리해보자면 다음과 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;메모리 부족 : 프로세스가 너무 많이 생성된다. 커넥션이 1만개이면 프로세스도 1만개 생성된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;무겁다 : 감당해야할 프로세스가 많아질 수 있고, 다양한 모듈을 설치하다보면 서버가 정말 무거워진다.&lt;/li&gt;
&lt;li&gt;CPU가 부하된다 : CPU 코어 하나당 동시적으로 처리해야 할 프로세스의 양이 많아서 부하된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2004년--nginx-의-등장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2004%EB%85%84--nginx-%EC%9D%98-%EB%93%B1%EC%9E%A5&quot; aria-label=&quot;2004년  nginx 의 등장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2004년 : Nginx 의 등장&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6e7102ea-5e3f-45ab-a65e-070488264fe4/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;시간이 흐르고 2004년이 되었을떄, 아파치 서버의 구조를 보완하기 위한 소프트웨어가 나옵니다. 그게 바로 Nginx 입니다.&lt;/p&gt;
&lt;p&gt;초창기의 Nginx 는 아파치와 함께 사용하기 위해 만들어졌습니다. 웹서버이긴 하지만, 아파치 서버를 완전히 대체하기 위한 목적은 아니였습니다. 아파치 서버가 지닌 구조적 한계를 Nginx 를 사용함으로써 극복하려고 했습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx-와-apache-서버와의-협력관계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EC%99%80-apache-%EC%84%9C%EB%B2%84%EC%99%80%EC%9D%98-%ED%98%91%EB%A0%A5%EA%B4%80%EA%B3%84&quot; aria-label=&quot;nginx 와 apache 서버와의 협력관계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 와 Apache 서버와의 협력관계&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ed1c277b-1a48-4185-963c-a22ee79e16f7/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그렇다면 아파치 서버의 한계를 어떻게 Nginx 로 극복할 수 있을까요?
간단히 말하면, &lt;strong&gt;아파치 서버에다 Nginx 를 올려두면 됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이렇게하면 &lt;strong&gt;기존의 아파치 서버가 감당해야했던 수많은 동시 커넥션을 Nginx 가 대신해서 유지해줍니다.&lt;/strong&gt; 동시 커넥션을 유지못하는 어파피 서버의 부하 문제점을 Nginx 로 크게 줄일 수 있게 된것이죠.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/b3a47f3b-fc58-4748-a9f8-b01fd8fb25e2/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nginx 는 웹서버답게, 정적파일에 대한 요청을 본인이 모두 처리&lt;/strong&gt;하고, 동적인 파일을 요청받았을 때만 뒤에 있는 아파치 서버와 커넥션을 형성하는 것입니다.&lt;/p&gt;
&lt;p&gt;아파치 서버는 리소스를 커넥션을 유지하는데 쓰지않고, Nginx 가 처리하지 못하는 동적 파일을 처리하거나, 개발자가 설계해둔 로직 처리에만 신경쓰면 되는것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx-의-내부-동작-메커니즘&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EC%9D%98-%EB%82%B4%EB%B6%80-%EB%8F%99%EC%9E%91-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98&quot; aria-label=&quot;nginx 의 내부 동작 메커니즘 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 의 내부 동작 메커니즘&lt;/h2&gt;
&lt;p&gt;그렇다면 Nginx 는 어떤 구조이길래 많은 동시 커넥션을 유지할 수 있을까요? 비결은 바로 &lt;strong&gt;적게 생성되는 프로세스 수에 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;worker-process&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#worker-process&quot; aria-label=&quot;worker process permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Worker Process&lt;/h3&gt;
&lt;p&gt;Nginx 의 핵심 구조를 뜯어보면, Master Process 와 Worker Process 라는 것이 있습니다. 각 프로세스의 역할을 요약해보면 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5ebc29a6-a985-40b5-be35-ca7852db4a1e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Master Process : 저희가 Nginx.conf 라는 설정파일에 작성한 내용을 읽고, 설정에 알맞게 Worker Process 를 생성하는 프로새스&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Worker Process : 실제로 일을 하는 프로세스&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;쉽게말해 Master Process 로 부터 Worker Process 가 생성되고, 이 프로세스가 작업을 수행하는 것이죠.&lt;/p&gt;
&lt;p&gt;이 Worker 프로세스가 만들어질 때 각자 지정된 Listen Socket 을 지정받습니다. 그리고 그 소켓에 새로운 클라이언트로부터 받은 요청이 들어오면 커넥션을 생성하고, 그 요청을 처리합니다.&lt;/p&gt;
&lt;p&gt;그러고 연결된 커넥션은 HTTP 프로토콜에 지정된 keep-alive 시간만큼 끊기지않고 유지됩니다. 그런데 커넥션이 생성되었다고 해서 Worker 프로세스가 해당 커넥션 하나만 담당하지는 않습니다.&lt;/p&gt;
&lt;h3 id=&quot;event&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#event&quot; aria-label=&quot;event permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Event&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/51f4441d-f793-48ef-b3d8-9fa45e1d9902/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Worker 프로세스는 생성된 커넥션에 현재 아무런 요청이 없는 상태라면 &lt;strong&gt;새로운 커넥션이 들어오면 또 연결을 가능하게 합니다.&lt;/strong&gt; 또는 이미 연결된 다른 커넥션이 요청을 시도한 경우, 해당 요청에 대해 처리를합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이렇게 커넥션의 생성 및 제거, 그리고 요청을 처리하는 행위들을 모두 Event 라고 부릅니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;발생한-event-비동기-처리-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%9C%EC%83%9D%ED%95%9C-event-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;발생한 event 비동기 처리 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;발생한 Event 비동기 처리 방식&lt;/h2&gt;
&lt;h3 id=&quot;asynchronous-비동기-적으로-처리한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#asynchronous-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%B2%98%EB%A6%AC%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;asynchronous 비동기 적으로 처리한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Asynchronous (비동기) 적으로 처리한다&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/b23984af-6c9c-44d6-9737-321ec4209150/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이런 Event 들은 OS 커널이 큐(Queue) 형태로 Worker 프로세스에게 전달해주는 겁니다. 이 이벤트들은 큐에 담긴 상태에서 Worker 프로세스가 처리하기 전까지는 비동기 방식(Asynchronous) 이 되게합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;한 이벤트가 완전히 처리되고 난후에, 큐의 그 다음 이벤트를 꺼내서 그 다음것을 순차적으로 처리하는 방식&lt;/strong&gt;이라는 것입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;쉽게말해, 한 이벤트가 처리되기 전까지 다른 이벤트들은 동시에 처리되는 것이 아니라, 계속 대기하고 있어야함!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이때 Worker 프로세스는 하나의 쓰레드로 이벤트를 순차적으로 꺼내서 처리해나가는 방식입니다.&lt;/p&gt;
&lt;p&gt;이러면 &lt;strong&gt;worker 프로세스가 쉬지않고 계속해서 일을 한다&lt;/strong&gt;는 장점이있습니다.&lt;/p&gt;
&lt;h3 id=&quot;아파치-서버와의-비교&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%84%ED%8C%8C%EC%B9%98-%EC%84%9C%EB%B2%84%EC%99%80%EC%9D%98-%EB%B9%84%EA%B5%90&quot; aria-label=&quot;아파치 서버와의 비교 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;아파치 서버와의 비교&lt;/h3&gt;
&lt;p&gt;아파치 서버와 구조를 비교해봅시다. 아파치 서버는 요청이 없다면 방치되는 프로세스들이 가득했죠? 반면 서버 자원을 훨씬 효율적으로 쓰는 셈입니다.&lt;/p&gt;
&lt;p&gt;아파치 서버는 한 프로세스가 커넥션을 딱 하나만 수용가능해서 낭비되는 프로세스가 많이생길 가능성이 정말 크죠. 반면 Nginx 에서는 Worker Process 라는 딱 하나의 프로세스만 생성하고 여러 커넥션을 수용 가능하며, 이 프로세스 하나로 이벤트 큐를 보내준다면 모두 처리가 가능해지는 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx-의-thread-pool--비동기-방식의-해결방안&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EC%9D%98-thread-pool--%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%B0%A9%EC%8B%9D%EC%9D%98-%ED%95%B4%EA%B2%B0%EB%B0%A9%EC%95%88&quot; aria-label=&quot;nginx 의 thread pool  비동기 방식의 해결방안 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 의 Thread Pool : 비동기 방식의 해결방안&lt;/h2&gt;
&lt;h3 id=&quot;문제상황--blocking&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B8%EC%A0%9C%EC%83%81%ED%99%A9--blocking&quot; aria-label=&quot;문제상황  blocking permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;문제상황 : Blocking&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1b1dd045-ed19-4923-93ae-9d545e7f39ae/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;다만 위 방식으로 이벤트를 하나씩 순차적으로 처리하면 아쉬운 점이 몇가지 있습니다. 그것은 바로 &lt;strong&gt;요청이 Blocking 될수도 있다는 점입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;쉽게말해, 요청을 비동기 방식으로 계속 차근차근 처리해나가면 처리 시간이 오래걸릴 수 있습니다. 문제가 발생할 수 있는 더 다양한 케이스를 나열해보자면 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;이벤트 큐(Event Queue) 중에서 시간이 오래 걸리는 작업을 수행해야할 이벤트가 존재할때 ex) 디스크에 읽고쓰는 작업&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;동기(Synchornization) 방식으로 DB 에서 응답을 받는 경우&lt;/li&gt;
&lt;li&gt;Mutex, 또는 라이브러리 함수를 호출하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위와 같은 요청들을 처리하는 동안 Worker porcess는 다른 작업을 수행할 수 없으므로, 이벤트를 처리할 수 없어 성능이 매우 저하된다는 점이 발생합니다. 그래서 Nginx는 Thread pool 개념을 도입했습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;nginx-thread-pool&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-thread-pool&quot; aria-label=&quot;nginx thread pool permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx Thread pool&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5283b40c-4f69-44b6-82e8-c66e46c18a33/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Worker process가 요청을 이벤트 큐에 넣으면, 각 요청들을 Thread pool에 있는 Thread들이 골구로 수행합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;그러고 수행 결과를 다시 worker process에게 전달해주는 방식입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예를들어 디스크에 읽고쓰는 작업을 해야한다면 그 뒤에 있는 이벤트는 요청을 처리하는 긴 시간동안 블로킹(blocking) 되는 상태입니다. Nginx 는 이런 상황을 방지하기 위해 그렇게 시간이 오래걸리는 작업을 따로 수행하는 쓰레드 풀을 만듭니다.&lt;/p&gt;
&lt;p&gt;그리고 worker 프로세스는 지금 처리할 요청이 시간이 오래걸릴 것 같다 싶으면 해당 쓰레드 풀에게 그 이벤트를 위임하고, 큐 안에 있는 다른 이벤트를 처리하러갑니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Thread pool 개념을 도입했다고 해서 디폴트로는 적용되지 않는 상태입니다.
1.7.11 버전 이상에서부터는 별도의 설정이 필요하다는 점에 유의합시다!
(적용 방법은 공식문서에 나와있다.)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;context-switching&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#context-switching&quot; aria-label=&quot;context switching permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Context Switching&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/12e37a50-c3b8-4a53-af77-2bf4d8287ed0/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이러한 &lt;strong&gt;Worker 프로세스는 Nginx 가 존재하는 해당 서버의 CPU 코어 개수만큼 생성&lt;/strong&gt;합니다. 이러면 코어가 담당하는 프로세스를 바꾸는 횟수를 대폭 줄일 수 있습니다. CPU 가 굳이 부가적인 일을 하지 않아도 되는 것이죠.&lt;/p&gt;
&lt;p&gt;다시말해, &lt;strong&gt;CPU 의 Context Switching 사용을 줄이는 것입니다.&lt;/strong&gt; 이게 바로 Nginx 가 택한 Event 기반 구조입니다. 또 이게 바로 아파치 서버와의 가장 큰 차이점입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;동적으로-설정-정보를-바꾸는데-용이하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%84%A4%EC%A0%95-%EC%A0%95%EB%B3%B4%EB%A5%BC-%EB%B0%94%EA%BE%B8%EB%8A%94%EB%8D%B0-%EC%9A%A9%EC%9D%B4%ED%95%98%EB%8B%A4&quot; aria-label=&quot;동적으로 설정 정보를 바꾸는데 용이하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동적으로 설정 정보를 바꾸는데 용이하다&lt;/h2&gt;
&lt;p&gt;이렇게 수많은 동시 커넥션 양을 처리하는데에 있어 프로세스를 적게 만들다보니 가볍기까지 합니다. &lt;strong&gt;프로세스를 적게 만드는 이 구조는 Nginx 의 설정을 동적으로 서버를 중단하지 않고도 바꾸는 것을 가능하게 합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/91be92e7-f11a-4725-a225-06a7830411b9/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;개발자가 nginx.conf 라는 설정파일에서 Nginx에 대한 설정을 변경하고, Nginx에 대한 설정을 적용하면 Master 프로세스는 그 설정에 맞는 Worker 프로세스를 따로 생성합니다.&lt;/p&gt;
&lt;p&gt;그리고 기존에 있는 Worker 프로세스가 더 이상 커넥션을 생성하지 않도록 합니다. 그러고 시간이 지나, 기존 worker 프로세스가 감당하던 이벤트 처리가 모두 끝나면 해당 프로세스를 종료합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;로드밸런싱이-가능하다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1%EC%9D%B4-%EA%B0%80%EB%8A%A5%ED%95%98%EB%8B%A4&quot; aria-label=&quot;로드밸런싱이 가능하다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로드밸런싱이 가능하다&lt;/h2&gt;
&lt;p&gt;그런데 이런 동적 설정 변경은 언제쓸까요?&lt;/p&gt;
&lt;p&gt;아주 대표적인 경우로, Nginx 가 여러 동시 커넥션을 관리하는 도중에 뒷단에 서버가 추가되는 경우가 있습니다. 그때는 Nginx 가 로드밸런서의 역할을 담당하게 되는 것입니다. 로드 벨런서는 요청을 여러 서버로 분산하는 작업을 수행합니다.&lt;/p&gt;
&lt;p&gt;로드밸런서에 대해 잘 모르시는 분들은 제 지난 포스팅 &lt;a href=&quot;https://velog.io/@msung99/%ED%94%84%EB%A1%9D%EC%8B%9C-%EC%84%9C%EB%B2%84Forward-Proxy-Reverse-Proxy-%EC%99%80-Load-Balancer-%EB%9E%80&quot;&gt;Proxy Server와 로드밸런싱, 수평확장(Scale Out)과 수직확장(Scale Up)에 대해&lt;/a&gt; 을 참고하세요!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/96e828fe-94d6-4ddd-b2af-4eddb12d06f5/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;로드밸런싱을 하기위해선, 설정정보 파일 nginx.conf 로 들어가서 분산작업을 처리해줄 뒷단의 서버들을 추가적으로 적어줘야합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;그런데 만일 Nginx 는 수많은 동시 커넥션을 감당하고 있었더라면, 설정을 바꾸기 위해 Nginx 를 종료하는 상황이 좀 어려웠을 겁니다.&lt;/p&gt;
&lt;p&gt;그런데 동시적으로 설정을 변경할 수 있다면 어떨까요? 동시 커넥션을 유지한체 그 기존 요청을 계속해서 처리하면서, 별도로 뒷단에 서버를 동 추가할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;기존 요청들을 수행하던 Worker 프로세스들은 요청을 완료하면 종료되고, Master Process 는 로드밸런싱되는 설정 정보에 기반하여 Worker 프로세스들을 생성할겁니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Nginx 는 이런 설정변경을 초당 수십번을 해도 무리없이 커넥션을 관리하고 요청을 서버에 전달합니다. 이벤트 기반 구조라서 가능한 방식인것이겠죠?&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2008년&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2008%EB%85%84&quot; aria-label=&quot;2008년 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2008년&lt;/h2&gt;
&lt;p&gt;그런데 2008년 부터 아파치 서버는 점유율이 점점 낮아지기 시작하고, Nginx 는 빠른 속도로 점유율을 치고 올라오기 시작합니다. 도대체 2008년에 무슨일이 있었을까요?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d56c3c6e-6357-46c5-88d9-b3aa9d16f5a8/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;2008년에는 스마트폰이 나오고 인터넷 환경을 바꾸기 시작했습니다. 스마트폰은 사람들이 인터넷을 더 많이 사용하게 된 계기이기도 하지만, 동시 커넥션을 훨씬 더 많이 생성하는 계기가 되기도 했습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/8213cf9b-776c-4130-8129-eef4a26579cc/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;사람들은 다양한 정보를 실시간으로 제공받고 싶어하는 것입니다.&lt;/p&gt;
&lt;p&gt;그리고 웹에 담기는 컨텐츠가 다양해지고, 그 용량이 커지면서 브라우저도 리소스를 빨리 가져오기 위해 여러 TCP 커넥션을 동시에 형성하기 시작했습니다. 그리고 각각의 커넥션은 모두 keep-alive 설정으로 유지 되었고요. 결국 동시 커넥션 문제를 처리해야 할 서버가 날이 갈수록 많아졌습니다.&lt;/p&gt;
&lt;p&gt;회사들은 빠르게 Nginx 라는 대체제에 눈을 돌리기 시작했습니다. 특히 Nginx 는 대규모 사이트를 운영하고 있는 큰 회사들이 좋아할만한 솔루션입니다.&lt;/p&gt;
&lt;p&gt;덕분에 Nginx 가 인터넷 트래픽을 관여하는 비중은 멈추지 않고 계속 증가했고, 현재 23년도에도 꾸준히 증가하는 추세입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx-vs-apache&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-vs-apache&quot; aria-label=&quot;nginx vs apache permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx VS Apache&lt;/h2&gt;
&lt;h3 id=&quot;nginx-의-강점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EC%9D%98-%EA%B0%95%EC%A0%90&quot; aria-label=&quot;nginx 의 강점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 의 강점&lt;/h3&gt;
&lt;p&gt;아파치 서버도 성능 개선을 많이 하는중입니다. 그러나 동시 커넥션 관리에선 Nginx 가 크게 앞서는중입니다. Nginx 는 동시 커넥션수가 많아져도 메모리 사용률이 낮고 일정한 비율이 나오는 반면, 아파치는 많이 사용합니다.&lt;/p&gt;
&lt;h3 id=&quot;apache-의-강점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#apache-%EC%9D%98-%EA%B0%95%EC%A0%90&quot; aria-label=&quot;apache 의 강점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Apache 의 강점&lt;/h3&gt;
&lt;p&gt;하지만 아파치는 지금껏 오랜기간 버그가 발생되지 않도록 수많은 개선이 이루어졌기 때문에 다양한 OS 에서 안정적이라는 특징을 지닙니다.&lt;/p&gt;
&lt;p&gt;또한 앞서 설명드렸듯이, 아파치는 모듈을 추가해서 확장하기 쉽다는 특징도 지닙니다. 모듈의 종류도 아파치 서버가 Nginx 보다 훨씬 많죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;nginx-의-기타-기능&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nginx-%EC%9D%98-%EA%B8%B0%ED%83%80-%EA%B8%B0%EB%8A%A5&quot; aria-label=&quot;nginx 의 기타 기능 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx 의 기타 기능&lt;/h2&gt;
&lt;h3 id=&quot;ssl-터미네이션-기능-reverserroxy-일때&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ssl-%ED%84%B0%EB%AF%B8%EB%84%A4%EC%9D%B4%EC%85%98-%EA%B8%B0%EB%8A%A5-reverserroxy-%EC%9D%BC%EB%95%8C&quot; aria-label=&quot;ssl 터미네이션 기능 reverserroxy 일때 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SSL 터미네이션 기능 (ReverseRroxy 일때)&lt;/h3&gt;
&lt;p&gt;사실 Nginx 는 웹서버로써 동시 커넥션 문제해결과 로드밸런싱으로써의 메인 기능외에도 다양한 기능을 제공합니다. 그 중 SSL 터미네이션 기능을 수행할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SSL 터미네이션이란 Nginx가 클라이언트와는 https 통신을하고, 서버와는 http 통신을 하는것을 말합니다.&lt;/strong&gt;
이 구조를 만들어서 서버가 복구화 과정을 감당하지 않을 수 있도록 하는것입니다. 비즈니스 로직 처리에 리소스를 사용할 수 있도록 비용을 줄여주는 것이죠.&lt;/p&gt;
&lt;h3 id=&quot;캐싱&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BA%90%EC%8B%B1&quot; aria-label=&quot;캐싱 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;캐싱&lt;/h3&gt;
&lt;p&gt;두번째로 Nginx 는 http 프로토콜을 사용해서 전달하는 컨텐츠를 캐싱할 수 있습니다.&lt;/p&gt;
&lt;p&gt;이와 관련한 내용은 제 지난 포스팅 &lt;a href=&quot;https://velog.io/@msung99/%ED%94%84%EB%A1%9D%EC%8B%9C-%EC%84%9C%EB%B2%84Forward-Proxy-Reverse-Proxy-%EC%99%80-Load-Balancer-%EB%9E%80&quot;&gt;Proxy Server와 로드밸런싱, 수평확장(Scale Out)과 수직확장(Scale Up)에 대해&lt;/a&gt; 를 참고하시면 좋을듯합니다!&lt;/p&gt;
&lt;h3 id=&quot;추가적인-기능&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B6%94%EA%B0%80%EC%A0%81%EC%9D%B8-%EA%B8%B0%EB%8A%A5&quot; aria-label=&quot;추가적인 기능 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;추가적인 기능&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;이 외에도 CORS 처리, HTTP/2 지원 등 다양한 기능을 지원해줍니다 (그저갓..) Nginx를 서비스에 도입한다면 어떤 기능이 있는지를 알아보고 적극적으로 적용해보면 좋을겁니다 😉&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금까지 1995 ~ 현재까지 이르기까지의 Nginx 의 역사를 깊게 알아보고, 해당년도에 어떤 한계를 부딪히고 왜 이런 Nginx 웹서버가 탄생했는지를 알아봤습니다.&lt;/p&gt;
&lt;p&gt;또한 Nginx 의 내부 메커니즘을 최대한 유지적으로 설명드렸는데, CS 지식이 다소 포함되어 있어 최대한 쉽게 설명하는데 신경을 많이 쓴것 같네요!&lt;/p&gt;
&lt;p&gt;이번 포스팅이 Nginx 를 정확히 이해하지 못하고 사용하시는 분들, 또 깊게 학습을 원하시는 분들에게 큰 도움이 되었으리라 믿습니다. 궁금하신 점이 있다면 댓글로 알려주세요. 제 포스팅을 읽어주신 모든 분들에게 감사드립니다! 😆&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고--출처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0--%EC%B6%9C%EC%B2%98&quot; aria-label=&quot;참고  출처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 &amp;#x26; 출처&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=6FAwAXXj5N0&quot;&gt;[10분 테코톡] 🤫 피케이의 Nginx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.nginx.com/&quot;&gt;Nginx Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://httpd.apache.org/docs/&quot;&gt;Apache Server HTTP Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@znftm97/Nginx%EB%9E%80-%EB%82%B4%EB%B6%80-%EB%8F%99%EC%9E%91-%EA%B5%AC%EC%A1%B0-%EB%B0%8F-%EC%84%B1%EB%8A%A5%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC&quot;&gt;Nginx란? (내부 동작 구조 및 성능에 대하여)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://namu.wiki/w/NGINX&quot;&gt;wiki-nginx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Component - 컴포넌트 스캔의 필요성에 대해]]></title><description><![CDATA[시작에 앞서 본 포스팅은 저 혼자서 인강 을 듣고 나만의 예재로 새롭게 만들어 본 내용입니다. 틀린 내용이 많을 수 있으니, 감안해주세요! 본 포스팅은 제 지난 포스팅 시리즈 직접 만들어보며 이해하는 SOLID 원칙과 DI…]]></description><link>https://haon.site/haon/oop/component-scan/</link><guid isPermaLink="false">https://haon.site/haon/oop/component-scan/</guid><pubDate>Mon, 23 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;시작에-앞서&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%9E%91%EC%97%90-%EC%95%9E%EC%84%9C&quot; aria-label=&quot;시작에 앞서 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시작에 앞서&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;본 포스팅은 저 혼자서 &lt;a href=&quot;https://www.inflearn.com/roadmaps/373&quot;&gt;인강&lt;/a&gt; 을 듣고 나만의 예재로 새롭게 만들어 본 내용입니다. 틀린 내용이 많을 수 있으니, 감안해주세요!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;본 포스팅은 제 지난 포스팅 시리즈 &lt;a href=&quot;https://velog.io/@msung99/%EC%A7%81%EC%A0%91-%EA%B5%AC%ED%98%84%ED%95%98%EB%A9%B0-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-SOLID-%EC%9B%90%EC%B9%99%EA%B3%BC-DI-%EC%84%A4%EA%B3%84-Bean%EC%9D%84-%ED%86%B5%ED%95%B4-%EC%88%98%EB%8F%99%EC%9C%BC%EB%A1%9C-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%A3%BC%EC%9E%85%ED%95%B4%EB%B3%B4%EA%B8%B0&quot;&gt;직접 만들어보며 이해하는 SOLID 원칙과 DI 설계 : 수동으로 직접 의존관계 주입해보기&lt;/a&gt; 에 이어서 진행되는 내용입니다. 이전 포스팅을 읽고 오시는 것을 권장드립니다!&lt;/p&gt;
&lt;h4&gt;객체지향 시리즈 포스팅 진행현황&lt;/h4&gt;
&lt;p&gt;현재 진행중인 포스팅 내용은 아래와 같습니다. 그 중 5번째 포스팅을 지금 진행해볼까합니다!&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;객체지향을 아는척하지 말자 : 오해하고 있었던 객체지향의 정체&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;예제로 이해하는 SOLID 5원칙, 그리고 스프링 DI 컨테이너의 등장&lt;/li&gt;
&lt;li&gt;직접 만들어보며 이해하는 SOLID 원칙과 DI 설계 : 수동으로 직접 의존관계 주입해보기&lt;/li&gt;
&lt;li&gt;싱글톤(SingleTon) : 왜 스프링 컨테이너를 써야할까?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;컴포넌트 스캔과 @Autowired 의 메커니즘 : 필요성에 대해 (현재 포스팅)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;알면 도움될 컴포넌트 스캔의 다양한 대상들과 DI 에 대한 해결방법&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;의존관계를-수동으로-직접-주입해야할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84%EB%A5%BC-%EC%88%98%EB%8F%99%EC%9C%BC%EB%A1%9C-%EC%A7%81%EC%A0%91-%EC%A3%BC%EC%9E%85%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;의존관계를 수동으로 직접 주입해야할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;의존관계를 수동으로 직접 주입해야할까?&lt;/h2&gt;
&lt;p&gt;지난번 포스팅 시리즈에서 다룬 내용들은 모두 스프링 컨테이너에 빈을 직접 등록하고, 의존관계를 생성하는 것이었습니다.&lt;/p&gt;
&lt;p&gt;그런데, 이렇게 등록해야 할 스프링 Bean 이 수백개가 되면 일일이 등록하기도 귀찮아지고, 매핑관계가 햇갈려지지 않을까요? 이를 해결하도록 &lt;strong&gt;스프링에서는 설정정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 지원해줍니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;또한 의존관계도 자동으로 주입해주는 Autowired 도 지원해줍니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;컴포넌트-스캔&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%EC%BA%94&quot; aria-label=&quot;컴포넌트 스캔 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컴포넌트 스캔&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;컴포넌트 스켄은 이름 그대로 @Component 어노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;참고로 @Configuration 이 컴포넌트 스캔의 대상이 된 이유도 @Configuration 소스코드 원본을 열어보면 @Component 어노테이션이 붙어있기 때문입니다.&lt;/p&gt;
&lt;h3 id=&quot;컴포넌트-스캔-대상--component-어노테이션&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%EC%BA%94-%EB%8C%80%EC%83%81--component-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98&quot; aria-label=&quot;컴포넌트 스캔 대상  component 어노테이션 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컴포넌트 스캔 대상 : @Component 어노테이션&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/9dabb486-489f-4a74-b7ee-1cb89a90d726/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;컴포넌트 스캔은 @ComponentScan 어노테이션이 붙은 구현 클래스들을 기반으로 의존관계를 주입해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;지난 포스팅에서 진행했었던 도메인 설계내용을 다시 가져와봤는데, 기억나시나요?&lt;/p&gt;
&lt;p&gt;CarRepository 인터페이스에 대한 구현 클래스를 SonarTarRepository 를 선택하고 싶다고 해봅시다. 그러면 SonarTarRepository 만 @ComponentScan 어노테이션을 붙여줘서 컴포넌트 스캔 대상에 포함되도록 해주면 됩니다.&lt;/p&gt;
&lt;p&gt;아래와 같이 어노테이션을 추가해주시면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 맨 위에 어노테이션만 붙여주면 컴포넌트 스캔 대상에 포함된다!&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SonarTarRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CarRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Car&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; carList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; autoCarIdx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeCar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Car&lt;/span&gt; sonarTar&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        carList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;autoCarIdx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sonarTar&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        autoCarIdx&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Car&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getCarInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; carIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Car&lt;/span&gt; car &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; carList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;carIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; car&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;incrementSpeed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; carIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Car&lt;/span&gt; car &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; carList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;carIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        car&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;curSpeed &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;bean-등록-설정정보-클래스--componentscan-configuration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bean-%EB%93%B1%EB%A1%9D-%EC%84%A4%EC%A0%95%EC%A0%95%EB%B3%B4-%ED%81%B4%EB%9E%98%EC%8A%A4--componentscan-configuration&quot; aria-label=&quot;bean 등록 설정정보 클래스  componentscan configuration permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Bean 등록 설정정보 클래스 : @ComponentScan, @Configuration&lt;/h3&gt;
&lt;p&gt;그런데 @Component 어노테이션만 명시해준다고 해서 바로 스프링 빈으로 등록되는 것이 아닙니다. 지난번에 진행했던 외부 설정정보 클래스 BeanConfig 처럼, 설정정보를 담고있는 클래스를 생성해줘야 합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;컴포넌트 설정정보 클래스에는 @ComponentScan 어노테이션과, 지난번처럼 @Configuration 를 명시해줘야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@ComponentScan&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AutoBeanConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 명시해주면 끝입니다. 이전에는 @Bean 어노테이션을 일일이 등록해줘야 했는데, 정말 간단해졌죠? 설정정보 클래스 AutoBeanConfig 안에는 별내용이 들어있지도 않아서, 의존관계를 주입할때 햇갈릴일이 전혀 없어졌습니다.&lt;/p&gt;
&lt;h3 id=&quot;유의사항&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9C%A0%EC%9D%98%EC%82%AC%ED%95%AD&quot; aria-label=&quot;유의사항 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;유의사항&lt;/h3&gt;
&lt;p&gt;추가적으로 컴포넌트 스캔에서 제외하고 싶은 클래스 및 파일들은 아래와 같이 해주시면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@ComponentScan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        basePackages &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hello.core&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 디폴트는 AutoAppConfig 가 있는 패키지 (hello.core) 에 있는 것들을 모두 컴포넌트 스캔한다. =&gt; 권장방법 : 패키지 위치를 이렇게 지정하지 말고, 설정 정보 클래스의 위치를 프로젝트 최상단에 두는 것이다.&lt;/span&gt;
        excludeFilters &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@ComponentScan.Filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FilterType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ANNOTATION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; classes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 컴포넌트 스캔을 해서 자동으로 모든것을 스프링 빈으로 등록할 때, 그 중에서 뺄것을 지정해주는 것&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;        &lt;span class=&quot;token comment&quot;&gt;// 기존 클래스 BeanConfig 와 다르게 @Bean 으로 등록한 클래스가 하나도 없다!&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AutoAppConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;지난번에 만들었던 설정정보 클래스 BeanConfig 같은 클래스 및 파일들이 없어지지 않고 존재한다면, 위와같이 컴포넌트 스캔 대상에서 제외시켜줘야 합니다.
&lt;strong&gt;BeanConfig 클래스와 같이 @Configuration 어노테이션이 붙은 클래스들도컴포넌트 스캔의 대상에 포함된다는 것을 유의합시다!&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;컴포넌트-스캔시-자동-의존관계-주입--autowired&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%EC%BA%94%EC%8B%9C-%EC%9E%90%EB%8F%99-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%A3%BC%EC%9E%85--autowired&quot; aria-label=&quot;컴포넌트 스캔시 자동 의존관계 주입  autowired permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컴포넌트 스캔시 자동 의존관계 주입 : @Autowired&lt;/h2&gt;
&lt;p&gt;그런데 문제점이 하나 있습니다. 컴포넌트 스캔을 통해 스프링 빈을 등록하면, &lt;strong&gt;의존관계는 어떻게 주입시켜줄까요?&lt;/strong&gt; 의존관계를 주입시켜줄 방법이 없는데 말입니다. 이때 바로 @Autowired 어노테이션을 써야합니다.&lt;/p&gt;
&lt;p&gt;이전에 BeanConfig 에서는 @Bean 으로 직접 설정 정보를 작성했고, 의존관계도 직접 명시했습니다.&lt;/p&gt;
&lt;p&gt;이제는 이런 설정 정보 자체가 없기 떄문에, 의존관계 주입도 이 클래스 안에서 해결해야 한다. 이때 @Autowired 는 의존관계를 자동으로 주입해줘야하비다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;컴포넌트 스캔시 기존 @Bean 어노테이션을 사용하는 설정정보 클래스와 달리 의존관계를 주입할 방법이 없다. 이때 @Autowired 를 통해 의존관계를 자동으로 주입시켜주자!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;자동-의존관계를-주입하는-방법과-과정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EB%8F%99-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84%EB%A5%BC-%EC%A3%BC%EC%9E%85%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EA%B3%BC-%EA%B3%BC%EC%A0%95&quot; aria-label=&quot;자동 의존관계를 주입하는 방법과 과정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자동 의존관계를 주입하는 방법과 과정&lt;/h2&gt;
&lt;p&gt;결론부터 말씀드리자면 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;생성자에 @Autowired 를 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이어서 컴포넌트 스캔과 의존관계 주입의 전반적인 과정을 요약해보자면 다음과 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1.컴포넌트 스캔을 위해, 스캔 대상에 포함시킬 구현 클래스들에 @Component 어노테이션을 붙여준다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;2.컴포넌트 스캔을 위한 설정정보 클래스를 생성해준다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3.컴포넌트 스캔을 위해 @Component 을 붙여준 각 구현 클래스의 생성자에 @Autowired 를 붙여줘서 자동으로 의존관계가 주입되도록 해준다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;자동-의존관계가-주입되는-메커니즘&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EB%8F%99-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84%EA%B0%80-%EC%A3%BC%EC%9E%85%EB%90%98%EB%8A%94-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98&quot; aria-label=&quot;자동 의존관계가 주입되는 메커니즘 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자동 의존관계가 주입되는 메커니즘&lt;/h2&gt;
&lt;p&gt;게속해서 어떤 원리로 컴포넌트 스캔을 통한 Bean 등록 및 의존관계가 자동으로 주입되는 것인지 알아보겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;1-컴포넌트-스캔-대상을-선택해서-스프링-빈으로-등록하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%EC%BA%94-%EB%8C%80%EC%83%81%EC%9D%84-%EC%84%A0%ED%83%9D%ED%95%B4%EC%84%9C-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88%EC%9C%BC%EB%A1%9C-%EB%93%B1%EB%A1%9D%ED%95%98%EA%B8%B0&quot; aria-label=&quot;1 컴포넌트 스캔 대상을 선택해서 스프링 빈으로 등록하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 컴포넌트 스캔 대상을 선택해서 스프링 빈으로 등록하기&lt;/h3&gt;
&lt;p&gt;예를들어 계속 이번 포스팅 시리즈에서 살펴봤던 SonarTarRepository, StarRexRepository 중에서 컴포넌트 스캔 대상으로 SonarTarRepository 를 선택해서 스프링 빈으로 등록했다고 해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SonarTarRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CarRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;token comment&quot;&gt;// ... (세부 구현내용 생략)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그렇다면 아래처럼 스프링 컨테이너에 SonarTarRepository 에 대한 Bean 이 등록될겁니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ff5a73c4-8fd9-4099-9b2f-7cbf472bcfc6/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-컴포넌트-스캔을-위한-설정정보-클래스-생성해주기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%EC%BA%94%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%84%A4%EC%A0%95%EC%A0%95%EB%B3%B4-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%83%9D%EC%84%B1%ED%95%B4%EC%A3%BC%EA%B8%B0&quot; aria-label=&quot;2 컴포넌트 스캔을 위한 설정정보 클래스 생성해주기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 컴포넌트 스캔을 위한 설정정보 클래스 생성해주기&lt;/h3&gt;
&lt;p&gt;계속 앞서 언급드렸던 내용이죠? 자세한 설명은 생략하겠습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@ComponentScan&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AutoBeanConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;3-autowired-를-생성자에-명시하여-의존관계-주입해주기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-autowired-%EB%A5%BC-%EC%83%9D%EC%84%B1%EC%9E%90%EC%97%90-%EB%AA%85%EC%8B%9C%ED%95%98%EC%97%AC-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%A3%BC%EC%9E%85%ED%95%B4%EC%A3%BC%EA%B8%B0&quot; aria-label=&quot;3 autowired 를 생성자에 명시하여 의존관계 주입해주기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. @Autowired 를 생성자에 명시하여 의존관계 주입해주기&lt;/h3&gt;
&lt;p&gt;다음으로는 생성자에 @Autowired 를 명시하여 자동으로 의존관계를 주입하게 해주면 됩니다. 이때 어떻게 자동 의존관계가 주입되는가 묻는다면, &lt;strong&gt;앞서 스프링 빈에 등록된 스프링 빈들을 뒤져봐서 적합한 빈을 선택하고, 의존관계를 주입해주는 방식&lt;/strong&gt; 인겁니다.&lt;/p&gt;
&lt;h4&gt;기존 코드&lt;/h4&gt;
&lt;p&gt;기존코드가 아래와 같다고 해봅시다. 아래와 같은 코드로는 @Autowired 없어서 의존관계가 주입되지 않습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CarServiceImpl&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CarService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CarRepository&lt;/span&gt; carRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CarServiceImpl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CarRepository&lt;/span&gt; carRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;carRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; carRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeCar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Car&lt;/span&gt; car&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        carRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;makeCar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;car&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ... (이하 세부구현 내용 생략)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 아래와 같이 @Autowired 를 명시해주면, 앞서 스프링 컨테이너에 등록된 Bean 중에서 적합한 Bean 을 찾고 의존관계를 알아서 주입해주는 것입니다. 이번 예제에서는 SonarTarRepository 에 대한 Bean 이 선택되고 의존관계가 주입되겠죠?&lt;/p&gt;
&lt;h4&gt;리팩토링 코드&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CarServiceImpl&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CarService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CarRepository&lt;/span&gt; carRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CarServiceImpl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CarRepository&lt;/span&gt; carRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;carRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; carRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeCar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Car&lt;/span&gt; car&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        carRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;makeCar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;car&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ... (이하 세부구현 내용 생략)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;자동-의존관계-주입-메커니즘-세부분석&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EB%8F%99-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%A3%BC%EC%9E%85-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98-%EC%84%B8%EB%B6%80%EB%B6%84%EC%84%9D&quot; aria-label=&quot;자동 의존관계 주입 메커니즘 세부분석 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자동 의존관계 주입 메커니즘 세부분석&lt;/h2&gt;
&lt;p&gt;앞선 과정을 좀 더 세부적으로 설명드리자면 다음과 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;CarServiceImpl 에 대한 스프링 빈을 생성하면서, 스프링은 스프링 컨테이너에 있는 CarRepository 에 대한 스프링 빈이 있는지 뒤집니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;정확히는 뒤져볼때 &lt;strong&gt;타입을 기준으로 뒤져봅니다.&lt;/strong&gt; 즉 CarRepository 와 같은 타입이 스프링 빈으로 등록되어 있는지 보는 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;따라서 스프링 빈 중에서 SonarTarRepository 를 타입이 동일한것으로 찾게되고 의존관계를 주입해줍니다.
(=&gt; 어짜피 상속 관계에서 SonarTarRepository 는 CarRepository 의 자식 타입이니, 같은 타입으로 인식하는 것입니다! )&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이로써 기존에 스프링 설정정보를 수동으로 일일이 입력하던 귀찮은 작업없이, 컴포넌트 스캔과 @Autowired 를 통해 자동으로 의존관계를 주입하는 방법에 대해 메커니즘을 자세히 알아봤습니다.&lt;/p&gt;
&lt;p&gt;이번 포스팅을 마무리로 스프링의 객체지향 관련 시리즈 블로깅을 마무리 지으려고 했으나, Autowired, 컴포넌트 스캔과 관련해 추가적으로 알면 크게 도움될 내용들에 관해 하나 더 진행해볼까합니다.&lt;/p&gt;
&lt;p&gt;궁금한점이 있으시다면 댓글로 알려주세요! 도와드리겠습니다 😉&lt;/p&gt;</content:encoded></item><item><title><![CDATA[예재로 쉽게 이해하는 Ioc/DI]]></title><description><![CDATA[시작에 앞서]]></description><link>https://haon.site/haon/oop/loc-di/</link><guid isPermaLink="false">https://haon.site/haon/oop/loc-di/</guid><pubDate>Mon, 23 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;시작에-앞서&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%9E%91%EC%97%90-%EC%95%9E%EC%84%9C&quot; aria-label=&quot;시작에 앞서 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시작에 앞서&lt;/h2&gt;</content:encoded></item><item><title><![CDATA[객체지향을 아는척 하지 말자 - 오해하고 있었던 객체지향의 정체에 대해]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/oop/mistunderstanding/</link><guid isPermaLink="false">https://haon.site/haon/oop/mistunderstanding/</guid><pubDate>Sat, 21 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;시작에-앞서&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%9E%91%EC%97%90-%EC%95%9E%EC%84%9C&quot; aria-label=&quot;시작에 앞서 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시작에 앞서&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;혼자서 공부하며 작성한 것이므로,잘못 이해한 내용을 적었을 가능성이 충분히 있습니다 😆&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;객체지향이 왜 중요하고, 어떻게 활용된다는건지 아직도 전혀 모릅니다. 따라서 포스트 내용이 매우 추상적이고, 부실할 수 있습니다 😓&lt;/p&gt;
&lt;p&gt;구글링을 하다가 우연히 &lt;strong&gt;객체지향이 무엇인지 처음으로 이론을 얇게나마 접하게 되었습니다.&lt;/strong&gt; &lt;strong&gt;혼자서 인터넷에 떠돌아다니는 다른 블로그들을 참고하며 뭔지 간단히나마 이론만을 접한 것 이므로 설명이 많이 부실합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;프로젝트, 개발에 이 객체지향 이라는게 어떻게 활용되는지도 전혀 모릅니다.&lt;/strong&gt; 또한 인터넷의 자료를 읽다가 이해를 못한 내용들이 많습니다. 따라서 혹시 이 글을 읽으시는 분들이 있다면, 적은 내용이 틀릴 가능성이 충분히 있음을 감안하시는걸 미리 알려드립니다 🙏&lt;/p&gt;
&lt;h3 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h3&gt;
&lt;p&gt;학습을 시작한 계기가 부끄럽지만, 인터넷을 찾아보며 공부할때 &quot;객체지향&quot; 이라는 개념이 중요하다! 잘 활용해야한다! 라는 말들을 간혹 봤습니다. &lt;strong&gt;객체지향이 아직도 무엇인지 잘 모르겠지만, 일단 &quot;기초&quot; 개념을 공부하며 들었던 제 생각을 조금이라도 기록해보고자 합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;따라서 지금부터 포스트에 작성할 내용이 절대 정답이라고 할 수 없습니다. 하지만 제가 최근에 혼자 공부해봤던 개념에 대해 얇게라도 정리하며, 혹시 이 글을 읽는 분들에게도 제 생각을 공유하고자 합니다 😁&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1-객체지향은-현실세계를-일반화-한-것이-아니다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%80-%ED%98%84%EC%8B%A4%EC%84%B8%EA%B3%84%EB%A5%BC-%EC%9D%BC%EB%B0%98%ED%99%94-%ED%95%9C-%EA%B2%83%EC%9D%B4-%EC%95%84%EB%8B%88%EB%8B%A4&quot; aria-label=&quot;1 객체지향은 현실세계를 일반화 한 것이 아니다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 객체지향은 현실세계를 일반화 한 것이 아니다&lt;/h2&gt;
&lt;h3 id=&quot;우리는-현실세계에-존재하는-것을-일반화시키고-있었다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9A%B0%EB%A6%AC%EB%8A%94-%ED%98%84%EC%8B%A4%EC%84%B8%EA%B3%84%EC%97%90-%EC%A1%B4%EC%9E%AC%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%84-%EC%9D%BC%EB%B0%98%ED%99%94%EC%8B%9C%ED%82%A4%EA%B3%A0-%EC%9E%88%EC%97%88%EB%8B%A4&quot; aria-label=&quot;우리는 현실세계에 존재하는 것을 일반화시키고 있었다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;우리는 현실세계에 존재하는 것을 일반화시키고 있었다..!&lt;/h3&gt;
&lt;p&gt;이론적인 접근방식으로 설게한다면, 매번 잘못된, 편합된 사고를 가지고 이상한 접근방식을 가질 수 있게됩니다. 그 중 하나가 &lt;strong&gt;현실세계에 존재하는 물체를 일반화시켜서 프로그래밍에 적용하는 것&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;예를들어 기존의 저희는 아래와 같은 접근방식으로 일반화시켜서 프로그래밍을 진행해왔습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클래스는 와플기계이고, 와플은 객체(오브젝트)이다.&lt;/li&gt;
&lt;li&gt;Car 클래스의 멤버변수로는 speed, name 등이있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;심지어 위 내용들은 유명한 출판사 책들에서도 이론적으로 설명하는 내용들입니다. 저희는 이런식으로 접근하지 말자는 것이죠. 객체지향의 세계는 현실세계와 다른 방식으로 접근해야 합니다.&lt;/p&gt;
&lt;h3 id=&quot;객체끼리는-살아숨쉬며-상호간에-협력하는-사이이다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%9D%EC%B2%B4%EB%81%BC%EB%A6%AC%EB%8A%94-%EC%82%B4%EC%95%84%EC%88%A8%EC%89%AC%EB%A9%B0-%EC%83%81%ED%98%B8%EA%B0%84%EC%97%90-%ED%98%91%EB%A0%A5%ED%95%98%EB%8A%94-%EC%82%AC%EC%9D%B4%EC%9D%B4%EB%8B%A4&quot; aria-label=&quot;객체끼리는 살아숨쉬며 상호간에 협력하는 사이이다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;객체끼리는 살아숨쉬며, 상호간에 &quot;협력&quot;하는 사이이다.&lt;/h3&gt;
&lt;p&gt;어떻게 현실세계와 다른지 예시를 통해 알아봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/95b7225b-c22b-49ce-affa-319f15835b68/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;현실세계에서 본인이 자동차를 엑셀을 밟는 상황을 가정해보자. 현실세계에서는 그냥 엑셀을 밟으면 자동차가 속도가 증가해서 앞으로 나가는 상황으로 당연히 받아들여질겁니다. 사람인 내가 자동차에게 엑셀을 밟는 명령, 즉 행위를 시도하면 자동차는 그저 앞으로 나가는 물체에 불과하죠.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/653bace8-97e7-41c2-bc42-71d33e587966/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그러나 객체지향 세계에서는 아닙니다. &lt;strong&gt;자동차는 엄연히 &quot;살아숨쉬고 있는, 마치 인격이라도 가지고 있는것처럼 보이는 객체&quot; 로 바라보아야 합니다.&lt;/strong&gt; 객체지향의 세계에서는 본인(사람)도 객체이고, 자동차도 객체입니다. 따라서 사람은 엑셀을 밟아서 속도를 증가시키고 싶다면, 자동차에게 속도를 증가시키겠다는 요청을 보내야합니다.&lt;/p&gt;
&lt;p&gt;요청을 받은 자동차는 자율적으로 본인의 의사에따라 속도를 증가시킬지말지는 결정할 수 있는 주체입니다. 따라서 요청을 거부할 수도있고, 반대로 승인할 수도 있는것이에요!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;객체는 또 다른 객체를 엄연한 객체로 바라보고, 상호간에 요청과 응답을 주고받으면서 협력해야 하는 사이입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2-역할과-구현을-구분짓자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EC%97%AD%ED%95%A0%EA%B3%BC-%EA%B5%AC%ED%98%84%EC%9D%84-%EA%B5%AC%EB%B6%84%EC%A7%93%EC%9E%90&quot; aria-label=&quot;2 역할과 구현을 구분짓자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 역할과 구현을 구분짓자&lt;/h2&gt;
&lt;p&gt;또 &lt;strong&gt;객체간의 협력시 역할과 구현을 구분할 수 있어야합니다.&lt;/strong&gt; 책임이라는 키워드를 앞서 알게 되었는데, 역할과 구현은 또 도대체 뭘까요?&lt;/p&gt;
&lt;h3 id=&quot;역할과-구현은-어떻게-다른가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%AD%ED%95%A0%EA%B3%BC-%EA%B5%AC%ED%98%84%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8B%A4%EB%A5%B8%EA%B0%80&quot; aria-label=&quot;역할과 구현은 어떻게 다른가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;역할과 구현은 어떻게 다른가?&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/fcfc85f5-2d00-4868-913e-8da9d8892b3c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;앞선 예제에서, 사람과 자동차만 따로 생각해봅시다. 주유소는 따로 굳이 설명에 포함하지 않아도 충분히 설명가능하기 때문입니다.&lt;/p&gt;
&lt;p&gt;만일 사람과 자동차 객체와의 협력 관계에서, 자동차 객체는 엄밀히 따졌을때 다양한 차종으로 구분될 수 있겠죠? 당연한 말이지만 소나타, 스태랙스, 캠핑카와 같은 여러 차종들이 자동차에 해당하는 것입니다.&lt;/p&gt;
&lt;p&gt;그런데 여기서, 사람이 선택한 차종이 소나타였는데 스타랙스로 바뀐다고 한들, 전체적인 서비스 흐름에 영향을 끼칠까요? 그건 아닐겁니다. 앞선 예시에서 사람과 자동차에게 부여된 책임들을 다시 살펴본다면, 소나타가 스타랙스로 바뀐다고해서 엑셀을 밟았을때 속도가 증가하는것이 아닌, 엉뚱하게 반대로 속도가 감소해서 후진하는 경우는 없을겁니다.&lt;/p&gt;
&lt;p&gt;정리해보자면, &lt;strong&gt;여러 자동차들은 하나의 동일한 역할(=책임의 집합)을 수행하는 자동차로 구분될 것입니다.&lt;/strong&gt; 각각의 자동차들을 저희는 구현이라고 부루는 것이며, &quot;자동차&quot; 라는 것은 역할로 부르는 것입니다.&lt;/p&gt;
&lt;p&gt;조금 더 생각해보면, 아래와 같은 관점을 생각해낼 수 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;책임들은 모여 하나의 역할을 구성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;하나의 역할은 다양한 구현을 그 아래로 가질 수 있다.&lt;/li&gt;
&lt;li&gt;각 객체들은 구현이 아닌 역할을 중점으로 상호작용하는 것이 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;왜-구현이-아닌-역할을-중점으로-상호작용해야할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-%EA%B5%AC%ED%98%84%EC%9D%B4-%EC%95%84%EB%8B%8C-%EC%97%AD%ED%95%A0%EC%9D%84-%EC%A4%91%EC%A0%90%EC%9C%BC%EB%A1%9C-%EC%83%81%ED%98%B8%EC%9E%91%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;왜 구현이 아닌 역할을 중점으로 상호작용해야할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 구현이 아닌 역할을 중점으로 상호작용해야할까?&lt;/h3&gt;
&lt;p&gt;만일 역할이 아닌 구현을 중점으로 객체간에 상호작용한다고 해봅시다. 그러면 자동차가 아닌 소나타로 소통한다고 해보는것이 되겠죠?&lt;/p&gt;
&lt;p&gt;이러면 갑자기 서비스를 개발할떄, 다른 차종으로 급하게 바꿔서 개발을 진행해야한다고 해봅시다. 소타나에서 갑자기 스타랙스로 갈아끼워야 하느 상황인거죠. 그렇다면 저희는 자동차가 아닌 소나타라는 구현, 즉 구체화에 의존해있기 때문에 개발 코드를 일일이 새롭게 구현해주고 서비스 설계를 다시 진행해야 할겁니다.&lt;/p&gt;
&lt;p&gt;반대로 자동차라는 추상화, 즉 역할을 중점으로 개발했다면 스타랙스로 바뀌더라도 서비스 설계 자체에 거의 지장이 없을겁니다. 이 때문에 구현이 아닌, 역할을 중점으로 상호작용 해야한다고 합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;구현을 중점으로 객체간에 소통하면, 유연성이 떨어진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;역할을 중점으로 설계하면서, 언제든지 다른 구현 객체로 바뀌더라도 문제가 없도록 설계해야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3객체는-자율적인-존재이다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3%EA%B0%9D%EC%B2%B4%EB%8A%94-%EC%9E%90%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%A1%B4%EC%9E%AC%EC%9D%B4%EB%8B%A4&quot; aria-label=&quot;3객체는 자율적인 존재이다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.객체는 자율적인 존재이다&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/45c6d3c1-b674-4acf-9106-5c9d59378dbb/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;다음으로 저희는 &lt;strong&gt;객체를 자율적인 존재로 인식해야 한다&lt;/strong&gt;는 것입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;객체는 그 본인의 역할만 제대로 수행할 수 있다면 어떤식으로 구현돠어도 무관하다는 것입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;즉, 자동차라는 역할을 문제없이 수행하고, 타 객체와의 협력에서 문제가 없다면 소나타로 구현되던, 스타랙스로 구현되던 상관이없다는 것이죠. 그리고 소나타로 구현이 되었을때, 엑셀 기능만 잘 동작하도록 역할만 수행한다면 문제가 없으니까 소나타 내부 엔진이 어떻게 구체적으론 구현되던간에 상관이 없다는것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;4-행동이-상태를-결정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-%ED%96%89%EB%8F%99%EC%9D%B4-%EC%83%81%ED%83%9C%EB%A5%BC-%EA%B2%B0%EC%A0%95&quot; aria-label=&quot;4 행동이 상태를 결정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 행동이 상태를 결정&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/94ae3dd4-bbe8-4eb9-a8c1-019cfc47b2cd/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그리고 &lt;strong&gt;행동이 상태를 결정한다&lt;/strong&gt; 라는 것을 인지하고 접근하는 사고방식이 객체지향적인 접근입니다.&lt;/p&gt;
&lt;p&gt;객체지향적인 설계를 고려하지않은 경우를 가정해봅시다.
만일 Car 클래스를 설게할때 다른 객체들과의 협력관계를 생각하지 않았다면, 무의적으로 어떤 필드가 들어가겠지? 라는 추측을 가지고 설계할겁니다.
getter, setter 가 필요할것같고, 자동차이니까 speed, oil_gage 필드도 필요할 것같아서 마주잡이로 설계하는겁니다.&lt;/p&gt;
&lt;p&gt;이 방식은 잘못된, 객체지향을 무시하는 접근입니다. 이 자동차 클래스가 어떻게 서비스가 영향을 미칠줄 알고 설계를 마주잡이로 정의하는 것일까요?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;자동차 객체가 또 다른 어떤 객체와 어떤 상호작용을 할지가 정해지고, 역할이 정확히 정해진 후에 그것을 토대로, 상태, 필드, 속성등을 결정하는 것이 올바른 객체지향 접근방식이라고 하네요!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며---객체지향이-왜-중요하고-어떻게-활용하는건데&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0---%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%B4-%EC%99%9C-%EC%A4%91%EC%9A%94%ED%95%98%EA%B3%A0-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%99%9C%EC%9A%A9%ED%95%98%EB%8A%94%EA%B1%B4%EB%8D%B0&quot; aria-label=&quot;마치며   객체지향이 왜 중요하고 어떻게 활용하는건데 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며 - 객체지향이 왜 중요하고, 어떻게 활용하는건데?&lt;/h2&gt;
&lt;p&gt;객체지향이 왜 중요하냐구요? 언제 어떻게 활용하냐구요?
&lt;strong&gt;솔직히 말하자면, 모르겠습니다..! 🥹 (?ㅋㅋ)&lt;/strong&gt; 일단 구글링으로 혼자서 얇게나마 공부해보면서, 일단 최대한 간단한 이론이라도 이해해보는데 취지를 두고 글을 작성해봤습니다 😅&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;부끄럽지만 객제지향이라는게 어디서 어떻게 활용할 수 있는건지 모르고, 왜 중요하다는건지 모르겠습니다.&lt;/strong&gt; 이에관해 열심히 학습을 해봐야할 것 같습니다. (막막하네요 어떻게 학습할지..!)&lt;/p&gt;
&lt;p&gt;아무튼 간에!! 이렇게 포스트를 마쳐보겠습니다. 혹시 이 글을 읽으실분이 있을진 모르겠지만, 저처럼 객체지향이 무엇인지 궁금하여 기초 개념을 학습하는 모든 분들에게 이 포스팅이 도움이 되었으면 하는 바람입니다!! 😆&lt;/p&gt;</content:encoded></item><item><title><![CDATA[예재로 이해하는 SOLID 5원칙]]></title><description><![CDATA[…]]></description><link>https://haon.site/haon/oop/solid/</link><guid isPermaLink="false">https://haon.site/haon/oop/solid/</guid><pubDate>Sat, 21 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;학습배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%8A%B5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;학습배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학습배경&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;본 포스팅은 저 혼자서 &lt;a href=&quot;https://www.inflearn.com/roadmaps/373&quot;&gt;인강&lt;/a&gt; 을 듣고 나만의 예재로 새롭게 만들어 본 내용입니다. 틀린 내용이 많을 수 있으니, 감안해주세요!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;본 포스팅은 제 지난 포스팅 시리즈 &lt;a href=&quot;https://velog.io/@msung99/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%84-%EC%95%84%EB%8A%94%EC%B2%99%ED%95%98%EC%A7%80-%EB%A7%90%EC%9E%90-%EC%9A%B0%EB%A6%AC%EA%B0%80-%EC%98%A4%ED%95%B4%ED%95%98%EA%B3%A0-%EC%9E%88%EC%97%88%EB%8D%98-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%97%90-%EB%8C%80%ED%95%B4&quot;&gt;객체지향을 아는척하지말자 : 우리가 오해하고 있었던 객체지향에 대해&lt;/a&gt; 에 이어서 관련 학습 키워드를 인터넷에 열심히 찾아보니, SOLID 라는 패턴이 등장했습니다! 이번엔 SOLID 라는 디자인패턴이 무엇인지를 자세한 예제와 함께 만들어봤으니 열심히 읽어봐주세요 😆😆😆&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;객체지향의-역할과-구현-지난-포스팅-리마인딩&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%98-%EC%97%AD%ED%95%A0%EA%B3%BC-%EA%B5%AC%ED%98%84-%EC%A7%80%EB%82%9C-%ED%8F%AC%EC%8A%A4%ED%8C%85-%EB%A6%AC%EB%A7%88%EC%9D%B8%EB%94%A9&quot; aria-label=&quot;객체지향의 역할과 구현 지난 포스팅 리마인딩 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;객체지향의 역할과 구현 (지난 포스팅 리마인딩)&lt;/h2&gt;
&lt;p&gt;지난 포스팅에서 다루었던 객체지향의 내용중, 일부 내용은 간단히만 리마인딩하고 넘거가겠습니다. 활용했던 예제를 보고 다시 복습해보죠. 그만큼 중요한 내용이기 때문입니다!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/bb6931fe-a577-485b-a41a-abd784dc651f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;사람과 자동차 객체의 협력 관계에서, 자동차 객체는 다양한 차종으로 구분될 수 있을것이라고 했었습니다. 여러 자동차들은 하나의 동일한 역할(= 책임의 집합) 을 수행하는 &quot;자동차&quot; 로 구분된다고 했었습니다.&lt;/p&gt;
&lt;p&gt;즉 하나의 역할 아래에서 다양한 구현이 될 수 있으며, &lt;strong&gt;서비스 로직을 설계할때 각 객체들은 구현이 아닌 역할을 중점으로 상호작용해야한다고 했었습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;핵심만 정리해보자면 아래와 같았죠.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;구현을 중점으로 객체간에 소통하면 유연성이 떨어지니, 역할을 중점으로 설게해야한다. 언제든지 다른 구현 객체로 바뀌더라도 문제가 없도록 설계해야한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;저희는 이 객체지향의 특징에 대해 꼭 기억하고 있어야합니다! 그래야 SOLID 설계원칙은 무엇인지 이해가 가능하고, 설계가 가능하기 때문이죠.&lt;/p&gt;
&lt;p&gt;또 미리 중요한 내용을 미리 말씀드리자면 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;자바(JAVA) 에서 역할(=책임)은 인터페이스를 통해 구현해내고, 구현(=역할)은 클래스를 통헤 구현해낸다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;생각해보면 맞는 말인것 같습니다. 역할은 마치 추상적인이고, 구현은 추상적인것을 말 그대로 구현해내는 것인데 말입니다. 자바에서도 이를 가능케하는 것이 인터페이스와 클래스입니다. 추상화 되어있는 인퍼테이스로 각 인터페이스끼리 협력관계를 설게해놓고, 클래스를 통해 구체화시켜놓으면 되는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그러면 본격적으로 SOLID 에 대해 알아봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;solid&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solid&quot; aria-label=&quot;solid permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SOLID&lt;/h2&gt;
&lt;p&gt;SOLID 는 객체지향에 대한 5대 설계원칙을 줄여서 부르는것입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;S(Single Responsibility Principle) : 단일 책임원칙&lt;/li&gt;
&lt;li&gt;O(Open/Closed Principle) : 개방-폐쇄 원칙&lt;/li&gt;
&lt;li&gt;L(Liskov Substitution Principle) : 리스코프 치환 원칙&lt;/li&gt;
&lt;li&gt;I(Interfacwe Segregation Principle) : 인터페이스 분리 원칙&lt;/li&gt;
&lt;li&gt;D(Dependency Inversion Principle) : 의존관계 역전 원칙&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이들이 무엇인지 차근차근 이론적으로 먼저 알아보고, 추후 코드로 직접 구현도 해보면서 이해해봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;srp--단일-책임원칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#srp--%EB%8B%A8%EC%9D%BC-%EC%B1%85%EC%9E%84%EC%9B%90%EC%B9%99&quot; aria-label=&quot;srp  단일 책임원칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SRP : 단일 책임원칙&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;SRP (Single Responsibility Principle) 단일 책임원칙이란 &lt;strong&gt;한 클래스는 하나의 책임만 가져야한다&lt;/strong&gt;는 것입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;여기서 클래스에 대한 &quot;책임&quot;이란 무엇인지 저희가 알고있죠? 제가 계속 강조하며 다루었던 내용입니다. 객체간에 협력을 할때, 각 객체마다 책임(= 역할)을 지니고, 이를 중점으로 협력해야 한다고 했었습니다.&lt;/p&gt;
&lt;p&gt;이때 중요한 것은 바로 &quot;변경의 정도&quot; 입니다. &lt;strong&gt;변경이 있을때 해당 서비스에 미치는 파급효과가 적으면 단일책임원칙을 잘 따른것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ocp--개방-폐쇄-원칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ocp--%EA%B0%9C%EB%B0%A9-%ED%8F%90%EC%87%84-%EC%9B%90%EC%B9%99&quot; aria-label=&quot;ocp  개방 폐쇄 원칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OCP : 개방-폐쇄 원칙&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;OCP(Open/Closed Principle) 이란 확장에는 열려있으나, 변경에는 닫혀있어야한다는 것입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/b4584325-d540-405e-bfdd-55a94b8e3667/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;쉽게말해, &lt;strong&gt;자바로 코드를 짤때 코드 변경이 인터페이스에서 있어서는 안된다는 것입니다.&lt;/strong&gt; 인터페이스의 변경없이 언제든지, 문제없이 구현 클래스를 갈아끼울 수 있어야한다는 것이죠.&lt;/p&gt;
&lt;p&gt;위와 같이 자동차 인터페이스가 있고 그에 대한 구현 클래스가 소나타, 스타랙스, 캠핑카 관련 서비스의 구현 클래스가 있다고 해봅시다. 그 중 소나타 구현 클래스가 선택된 상황이라고 해보죠! 자바 코드로 나타내보면 아래와 같습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;CarService&lt;/span&gt; carservice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SonarTarService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/2a358cb8-7c5f-4cb4-be3e-8d6a84739de7/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그런데 현재 자동차 인터페이스를 구현한 구현 클래스가 소나타일때, 스타랙스로 구현을 바꾼다고 해봅시다. 이때 자바 코드로 구현할때 인터페이스에 영향을 주지 않고 갈아끼우는 것이 가능할까요?&lt;/p&gt;
&lt;p&gt;이는 다형성을 잘 활용하면 해결이 될것같지만, 순수 자바코드로는 불가능합니다. OCP 를 위반하는 상황이 발생하는 것이죠.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;핵심 요약&lt;/strong&gt;
구현 클래스 코드를 변경해도 인터페이스 코드에는 영향이 없습니다. 각 인터페이스끼리의 협력관계, 즉 역할에 영향을 미치지 않기 때문이죠.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;OCP 위반하는 상황 : 그러나 문제는 구현 클래스를 다른 클래스로 갈아끼울때 발생합니다. 인터페이스에서 구현 객체를 선택해야해서, 코드를 수정해야하기 때문입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;순수-자바코드에서-ocp-가-위반되는-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%9C%EC%88%98-%EC%9E%90%EB%B0%94%EC%BD%94%EB%93%9C%EC%97%90%EC%84%9C-ocp-%EA%B0%80-%EC%9C%84%EB%B0%98%EB%90%98%EB%8A%94-%EC%83%81%ED%99%A9&quot; aria-label=&quot;순수 자바코드에서 ocp 가 위반되는 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;순수 자바코드에서 OCP 가 위반되는 상황&lt;/h3&gt;
&lt;p&gt;위 상황을 좀 더 자세히 설명드리겠습니다.&lt;/p&gt;
&lt;p&gt;순수 자바 코드로 구현했을때는 OCP를 지키는 것에 한계가 있습니다. 자동차 인퍼페이스를 구현할때, 구현 클래스를 직접 선택해줘야 한다는 문제가 있습니다.&lt;/p&gt;
&lt;p&gt;만일 아래처럼 UserService 에서 CarService 인터페이스가 있고 그에대한 구현객체로 소나타 관련 구현 클래스(SonarTarService) 객체를 지정했다고 해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;CarService&lt;/span&gt; carservice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SonarTarService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 소나타가 아닌 스타랙스 서비스로 갈아끼워야하는 경우, 아래처럼 직접 UserService 에서 코드를 변경해줘야 합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;CarService&lt;/span&gt; carservice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StarRexService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 인터페이스에서 코드 수정이&lt;/span&gt;
       &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;                      &lt;span class=&quot;token comment&quot;&gt;// 일어났다! 구현 객체를 변경하면 DIP가 위반된다.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;변경에는 닫혀있어야 하는데(= 인터페이스에 코드 변경이 일어나서는 안되는데), 인터페이스 코드를 수정해야하므로 OCP 를 위반한 것이죠.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;ocp-를-어떻게-지키지--di-컨테이너&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ocp-%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%A7%80%ED%82%A4%EC%A7%80--di-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88&quot; aria-label=&quot;ocp 를 어떻게 지키지  di 컨테이너 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OCP 를 어떻게 지키지? : DI 컨테이너&lt;/h3&gt;
&lt;p&gt;이를 잘 생각해보면, 인터페이스 내부에서 직접 구현 클래스를 선택하는 방법이 아닌, 외부에서 구현 클래스를 선택하게 할 수 있다면 OCP 를 지킬 수 있지 않을까요?&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;스프링에서는 에서는 DIP 를 지키기 위해, 객페를 생성하고 연관관계를 맺어주는 별도의 조립, 설정자(DI, loc 컨테이너)를 제공해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;위 내용은 중요하니, 꼭 기억하고 넘어갑시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;lsp--리스코프-치환-원칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lsp--%EB%A6%AC%EC%8A%A4%EC%BD%94%ED%94%84-%EC%B9%98%ED%99%98-%EC%9B%90%EC%B9%99&quot; aria-label=&quot;lsp  리스코프 치환 원칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LSP : 리스코프 치환 원칙&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;LSP(Lisvov Substitution Principle) 란 프로그램 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야한다는 것입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/44614868-096e-442f-9002-be6957b1a35d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;쉽게말해, 자동차 인터페이스의 엑셀 기능은 전진하라는(= 규약)이 있을겁니다. 그런데 반대로 후진하게 구현해버려면, 컴파일 단계에서는 에러도 안터지고 문제없이 빌드에 성공하겠지만 프로그램의 정확성과 정해놓은 규약에 위반된 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;즉 LSP 를 위반한 것이고, 이 인터페이스의 구현 클래스를 엑셀을 밟았을때 앞으로 가도록 수정해서 해결해야겠죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;isp-인터페이스-분리-원칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#isp-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EB%B6%84%EB%A6%AC-%EC%9B%90%EC%B9%99&quot; aria-label=&quot;isp 인터페이스 분리 원칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ISP 인터페이스 분리 원칙&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;ISP(Interface segregation principle) 란 특정 클라이언트를 위한 인터페이스 여러개가 범용 인터페이스 하나보다 낫다는 것입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;쉽게말해, &lt;strong&gt;인터페이스를 자잘하게 쪼개면 좋다는 것입니다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;잘게 쪼개지 않는경우, 인퍼페이스 A의 주문 기능을 수정할때 상품조회와 같은 다른 기능도 포함되어 있는경우 어쩌면 영향을 미칠수도 있습니다. 상황이 곤란해질 수 있는것이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;dip-의존관계-역전-원칙&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dip-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%97%AD%EC%A0%84-%EC%9B%90%EC%B9%99&quot; aria-label=&quot;dip 의존관계 역전 원칙 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DIP 의존관계 역전 원칙&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;DIP(Dependency inversion principle) 란 프로그래머는 &quot;추상화에 의존해야지, 구체화에 의존하면 안된다&quot; 라는 것입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;쉽게말해, 클라이언트는 구현 클래스에 의존하지 말고, 인터페이스에 의존하게 설계해서 역할 중심의 설계가 되도록 만들라는 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;계속 말씀드렸듯이, 클라이언트가 역할에 의존해야 유연하게 구현체를 변경가능할겁니다!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;순수-자바코드에서-dip-를-위반하는-상황&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%88%9C%EC%88%98-%EC%9E%90%EB%B0%94%EC%BD%94%EB%93%9C%EC%97%90%EC%84%9C-dip-%EB%A5%BC-%EC%9C%84%EB%B0%98%ED%95%98%EB%8A%94-%EC%83%81%ED%99%A9&quot; aria-label=&quot;순수 자바코드에서 dip 를 위반하는 상황 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;순수 자바코드에서 DIP 를 위반하는 상황&lt;/h3&gt;
&lt;p&gt;그런데 이 DIP 원칙도 순수 자바코드에서는 지킬 수 없습니다. 아까 예제로 살펴봤던 UserService 를 다시 살펴봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;CarService&lt;/span&gt; carservice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SonarTarService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;아시듯이, UserService 클라이언트가 직접 구현 클래스를 선택하는 방식입니다. CarService 라는 추상화(인터페이스) 에도 의존하고 이지만, 동시에 SonarTarService 라는 구체화(구현 클래스) 에도 의존하고 있기 때문에 DIP 를 위반하는 것이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;dip-를-어떻게-지키지--di-컨테이너&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dip-%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%A7%80%ED%82%A4%EC%A7%80--di-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88&quot; aria-label=&quot;dip 를 어떻게 지키지  di 컨테이너 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DIP 를 어떻게 지키지? : DI 컨테이너&lt;/h3&gt;
&lt;p&gt;OCP 의 문제 발생상황가 마찬가지로, 스프링에서는 DIP 를 지킬 수 있도록 외부에서 인터페이스의 관계를 주입해주는 DI(Dependency Injection) 컨테이너 이라는 것을 제공해줍니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;정리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;객체지향의 핵심은 다형성이며, 결국 다형성 만으로는 쉽게 부품을 갈아 끼우듯이 개발할 수가 없습니다. 직접 일일이 수정해줘야하느라 OCP, DIP 를 위반하게 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금까지 객체지향의 특성을 살린 SOLID 5원칙에 대해 자세히 알아봤습니다. 처음부터 이해하기엔 어려울 수 있는 원칙이니 많이 시간을 투자하여 꼭 이해하셨으면 하는 바람입니다.&lt;/p&gt;
&lt;p&gt;이해가 안가시거나 햇갈리는 부분이 있다면 댓글로 알려주세요! 도와드리겠습니다 😉&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Oauth 소셜 로그인의 개념, 동작방식]]></title><description><![CDATA[잘못된 접근방식 : 서비스에게 우리 소셜 계정정보를 맡겨볼까?  보통 서비스에서 회원가입, 로그인, 로그아웃등을 JWT…]]></description><link>https://haon.site/haon/server/oauth/</link><guid isPermaLink="false">https://haon.site/haon/server/oauth/</guid><pubDate>Sun, 15 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;잘못된-접근방식--서비스에게-우리-소셜-계정정보를-맡겨볼까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%98%EB%AA%BB%EB%90%9C-%EC%A0%91%EA%B7%BC%EB%B0%A9%EC%8B%9D--%EC%84%9C%EB%B9%84%EC%8A%A4%EC%97%90%EA%B2%8C-%EC%9A%B0%EB%A6%AC-%EC%86%8C%EC%85%9C-%EA%B3%84%EC%A0%95%EC%A0%95%EB%B3%B4%EB%A5%BC-%EB%A7%A1%EA%B2%A8%EB%B3%BC%EA%B9%8C&quot; aria-label=&quot;잘못된 접근방식  서비스에게 우리 소셜 계정정보를 맡겨볼까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;잘못된 접근방식 : 서비스에게 우리 소셜 계정정보를 맡겨볼까?&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/58ebc0b2-53c7-422a-95f9-70df654e012b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;보통 서비스에서 회원가입, 로그인, 로그아웃등을 JWT 를 사용해서 구현합니다. 그런데 저희가 편리하게 사용중인 소셜로그인(카카오, 페이스북, 구글) 은 어떻게 구현할 수 있을까요?&lt;/p&gt;
&lt;p&gt;이 기능은 내가 사용하고싶은 서비스의 로그인을 중개해주는 카카오, 구글과 같은 소셜이 관여합니다. 그런데 우리의 서비스가 사용자를 대신해 페이스북에 댓글을 달거나, 유저 목록을 가져오는등의 기능을 만들 수 있지 않을까요?&lt;/p&gt;
&lt;p&gt;즉, 페이스북, 구글등의 소셜 계정정보를 사용자로부터 직접 제공받고 저희의 서비스에서 대신 댓글을 달아주거나, 소셜 팔로잉 정보를 조회해주는등의 기능을 개발할 수 있을것입니다.&lt;/p&gt;
&lt;p&gt;그러나 내 소셜 아이디 계정을 여러 서비스에서 돌려쓴다고요? 만약 해당 서비스가 해킹당해서 내 소셜 계정정보가 모두 유출되면 어떻게할까요? 이는 서비스 개발자, 사용자 모두에게 큰 부담인 방식일겁니다. &lt;strong&gt;즉, 서비스에서 유저의 소셜 계정 정보를 저장하고 있는 방식은 개발자와 유저 양측에게 큰 부담입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;만일 해킹 당한다면 해커는 해당 소셜 계정으로 페이스북, 구글등 모든 소셜에 접근하여 가능한 모든 악의적인 행위를 할수 있습니다. 또한 페이스북, 구글 입장에서도 본인들의 계정 정보가 모두 털리는 방식이라면 큰 불안감만 남을것입니다.&lt;/p&gt;
&lt;p&gt;이를 해결하기 위해 등장한 것이 바로 OAuth 입니다. 이를 이용하면 서비스와 로그인을 중개해주는 페이스북, 구글등의 소셜간의 서비스를 안전하게 상호작용하며 사용 가능합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;oauth가-그래서-뭘까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#oauth%EA%B0%80-%EA%B7%B8%EB%9E%98%EC%84%9C-%EB%AD%98%EA%B9%8C&quot; aria-label=&quot;oauth가 그래서 뭘까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OAuth가 그래서 뭘까?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;현재 사용중인 서비스가, 서비스를 이용하는 사용자의 타 소셜 정보에 접근하기위해 권한을 타 소셜로 부터 위임받는 것입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;구글, 페이스북, 카카오와 같은 다양한 소셜 플랫폼에 접근하도록 제 3자 클라이언트(우리의 서비스) 가 접근 권한을 위임받을 수 있는 표준 프로토콜입니다.&lt;/p&gt;
&lt;h3 id=&quot;타-플랫폼은-어떤-방식으로-사용자-소셜정보를-제공해주는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%83%80-%ED%94%8C%EB%9E%AB%ED%8F%BC%EC%9D%80-%EC%96%B4%EB%96%A4-%EB%B0%A9%EC%8B%9D%EC%9C%BC%EB%A1%9C-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%86%8C%EC%85%9C%EC%A0%95%EB%B3%B4%EB%A5%BC-%EC%A0%9C%EA%B3%B5%ED%95%B4%EC%A3%BC%EB%8A%94%EA%B0%80&quot; aria-label=&quot;타 플랫폼은 어떤 방식으로 사용자 소셜정보를 제공해주는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;타 플랫폼은 어떤 방식으로 사용자 소셜정보를 제공해주는가?&lt;/h3&gt;
&lt;p&gt;이때 페이스북, 구글등의 소셜이 서비스에게 &quot;내 아이디와 비밀번호 계정정보를 그대로 제공하는 것이 아니라, accessToken 의 형태로 발급해줍니다. 그대로 발급하면 당연히 보안상의 큰 문제가 발생하겠죠?&lt;/p&gt;
&lt;p&gt;본격적인 내용을 살펴보기전에, 핵심적인 내용을 먼저 요약해보자면 다음과 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;타 소셜 플랫폼(ex.구글)은 제 3자 클라이언트(서비스)에게 쌩판 동일한 아이디, 비밀번호를 제공해주지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;대신 서비스는 AccessToken을 발급받고 타 소셜 플랫폼의 일부 기능을 사용 가능해집니다.&lt;/li&gt;
&lt;li&gt;타 소셜은 플랫폼은 모든 기능을 제공해주지는 않습니다. 그 중 서비스에서 사용하고자 하는 필요한 일부기능만을 부분적으로 접근 허용할 수 있게 해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;다 이해되지 않으셔도 괜찮습니다. 지금부터 상세하게 설명드릴 예정이니까요!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;oauth-와-관련한-역할-및-용어&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#oauth-%EC%99%80-%EA%B4%80%EB%A0%A8%ED%95%9C-%EC%97%AD%ED%95%A0-%EB%B0%8F-%EC%9A%A9%EC%96%B4&quot; aria-label=&quot;oauth 와 관련한 역할 및 용어 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OAuth 와 관련한 역할 및 용어&lt;/h2&gt;
&lt;p&gt;본격적으로 소셜 로그인의 메커니즘을 살퍄보기전에, 우선 아래 용어들을 이해하셔야 설명이 가능해집니다.&lt;/p&gt;
&lt;h3 id=&quot;resource-owner&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#resource-owner&quot; aria-label=&quot;resource owner permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Resource Owner&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;내 서비스를 사용할 서비스의 사용자&lt;/strong&gt;입니다. 이 서비스 사용자들은 카카오, 구글등의 소셜 플랫폼에서의 리소스를 소유하고 있는 사용자입니다.&lt;/p&gt;
&lt;h3 id=&quot;resource-server&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#resource-server&quot; aria-label=&quot;resource server permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Resource Server&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;리소스 제공자(페이스북, 구글 등)&lt;/strong&gt; 으로, 데이터를 보유하고 있는 서버를 의미합니다.&lt;/p&gt;
&lt;h3 id=&quot;authorization-server&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#authorization-server&quot; aria-label=&quot;authorization server permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Authorization Server&lt;/h3&gt;
&lt;p&gt;Resource Owner를 인증하고, &lt;strong&gt;내 서비스(클라이언트)에게 토큰을 발급해주는 서버&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;h3 id=&quot;client-내-서비스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#client-%EB%82%B4-%EC%84%9C%EB%B9%84%EC%8A%A4&quot; aria-label=&quot;client 내 서비스 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Client(= 내 서비스)&lt;/h3&gt;
&lt;p&gt;내 서비스, 즉 내가 구현할 애플리케이션을 의미합니다.
Resource Server 의 리소스를 이용하고자 하는 서비스가 되겠죠? 저희가 개발하고자 하는 서비스를 클라이언트라고 말하는 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;oauth-프로토콜의-다양한-권한부여-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#oauth-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%9D%98-%EB%8B%A4%EC%96%91%ED%95%9C-%EA%B6%8C%ED%95%9C%EB%B6%80%EC%97%AC-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;oauth 프로토콜의 다양한 권한부여 방식 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OAuth 프로토콜의 다양한 권한부여 방식&lt;/h2&gt;
&lt;p&gt;OAuth 프로토콜은 다양한 종류로 다양한 클라이언트 환경에 적합한 권한 부여 방식을 제공하고 있습니다. 그 중 보편적이고 널리 쓰이는 한가지 방식에 대해서만 자세하게 다룰 예정이지만, 그래도 나머지 방식들도 간단히나마 알아보고 넘어가 보겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;1-authorization-code-grant&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-authorization-code-grant&quot; aria-label=&quot;1 authorization code grant permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Authorization Code Grant&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Authorization Code 를 발급받고 안전하게 AccessToken 을 발급받는 방식&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;권한부여 승인코드를 발급받고, 이를 활용해서 AccessToken을 발급받는 방식입니다. 가장 널리 쓰이는 방법으로, 아래에서 따로 자세히 설명드리겠습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;클라이언트(서비스)가 사용자를 대신해 특정 리소스에 접근을 요청할 때&lt;/strong&gt; 사용되는 방식입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;보통 타사의 클라이언트에게 보호된 자원을 제공하기 위한 인증에 사용됩니다. Refresh Token의 사용이 가능한 방식입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2-implicit-grant&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-implicit-grant&quot; aria-label=&quot;2 implicit grant permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Implicit Grant&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4fe41375-667d-4a53-a272-da063ee3940f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;AccessToken 을 URL에 담아서 바로 발급받는 방식. 탍취 위험성이 큽니다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;자격증명을 안전하게 저장하기 힘든 클라이언트&lt;/strong&gt;(ex: JavaScript등의 스크립트 언어를 사용한 브라우저)에게 최적화된 방식입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;권한 부여 승인 코드 없이 바로 Access Token이 발급 됩니다.&lt;/strong&gt; Access Token이 바로 전달되므로 만료기간을 짧게 설정함으로써 그토큰 탈취의 위험성을 줄여야합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Refresh Token 사용이 불가능한 방식이며, Access Token을 획득하기 위한 절차가 간소화되기에 효율성은 높아지지만 &lt;strong&gt;Access Token이 URL에 담겨서 전달된다는 취약점이 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;3-resource-owner-password-credentials-grant&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-resource-owner-password-credentials-grant&quot; aria-label=&quot;3 resource owner password credentials grant permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Resource Owner Password Credentials Grant&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1e117431-5015-4c0f-a659-a1afcfc09e71/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;간단하게 username, password로 Access Token을 받는 방식&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;자신의 서비스에서 제공하는 어플리케이션일 경우에만 사용되는 인증 방식입니다. Refresh Token의 사용도 가능합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;oauth-흐름을-이해하기전에-이건-알고가자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#oauth-%ED%9D%90%EB%A6%84%EC%9D%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0%EC%A0%84%EC%97%90-%EC%9D%B4%EA%B1%B4-%EC%95%8C%EA%B3%A0%EA%B0%80%EC%9E%90&quot; aria-label=&quot;oauth 흐름을 이해하기전에 이건 알고가자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OAuth 흐름을 이해하기전에 이건 알고가자!&lt;/h2&gt;
&lt;p&gt;또 아셔야할 용어를 정리해보면 다음와 같습니다. 모든 용어를 한번에 기억하실 필요는 없습니다. 메커니즘을 이해하시다보면 자연스럽게 각 용어가 무엇을 뜻하는것인지 이해가 되실겁니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Authorization Code&lt;/strong&gt; : AccessToken 을 발급하기 위한 임시 인증코드. 생명주기가 매우 짧다(10분이내)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Client ID&lt;/strong&gt; : 클라이언트(애플리케이션)의 고유한 ID
ex) goodapp-541106&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Client Secret&lt;/strong&gt; : 클라이언트(애플리케이션)를 위한 비밀키이며, 서비스 제공자에게 요청을 보낼 때 애플리케이션의 신원을 알려주는 값 입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Authorized Redirect URL(리다이렉션 엔드포인트)&lt;/strong&gt; : 서비스를 생성할 떄 등록한 Redirect URL.
즉, Authorization Server가 권한을 부여하는 과정에서 Authorized Code를 전달해줄 경로 (Authorized redirect URl로 Authorized Code 를 전달해줘!)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;scope&lt;/strong&gt; : 내 서비스(클라이언트)가 부여받은 리소스 접근 권한&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;인가 엔드포인트&lt;/strong&gt; : 클라이언트 애플리케이션이 인가 플로우를 시작할 때 사용하는 엔드포인트입니다. ex) 페이스북 로그인 페이지 URL&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;토큰 엔드포인트&lt;/strong&gt; : 클라이언트 애플리케이션이 토큰 플로우를 시작할 때 사용하는 엔드포인트입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;oauth-메커니즘--authorization-code-grant&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#oauth-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98--authorization-code-grant&quot; aria-label=&quot;oauth 메커니즘  authorization code grant permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OAuth 메커니즘 : Authorization Code Grant&lt;/h2&gt;
&lt;p&gt;이제 가장 보편적으로 사용되는 Authorization Code Grant 방식을 알아보겠습니다. 절차는 아래와 같습니다.
&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ec070ca4-2685-4d8a-83d0-5babc89ebb09/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;12-로그인-요청-및-authorizaion-code-요청&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#12-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%9A%94%EC%B2%AD-%EB%B0%8F-authorizaion-code-%EC%9A%94%EC%B2%AD&quot; aria-label=&quot;12 로그인 요청 및 authorizaion code 요청 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1~2. 로그인 요청 및 Authorizaion Code 요청&lt;/h3&gt;
&lt;p&gt;서비스 사용자가 &quot;페이스북 로그인 하기&quot; 버튼을 눌러서 로그인을 요청하는 경우입니다. Client(서비스)는 Authorization Code 를 요청할 수 있도록 사용자의 브라우저를 Authorization Server로 보내야합니다. ( Authorization URL을 통해 이동시키기!)&lt;/p&gt;
&lt;h4&gt;Authorization URL 요청에 포함되는 파라미터&lt;/h4&gt;
&lt;p&gt;위 그림에도 적어놓았듯이, 클라이언트는 Authorization Server 가 제공하는 Authorization URL 에 다음 파라미터들을 쿼리스트링으로 포함해서 보내야합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;redirect_uri, client_id, response_type, scope&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예시&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;https&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;google&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;authoirzation&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;rediect_uri&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;https&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;maestro&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;main
&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;client_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1298381293123873&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;response_type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;code
&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;create&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;read&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;이러한 인증 URL은 백엔드에서 생성하고, 프론트엔드는 백엔드로부터 URL 을 가져오는 것이 통상적입니다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;34-로그인-시도&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#34-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%8B%9C%EB%8F%84&quot; aria-label=&quot;34 로그인 시도 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3~4. 로그인 시도&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/806d6552-1e7b-4f03-aa60-c0030996ba6a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;사용자의 브라우저가 소셜 로그인 페이지(ex. 페이스북 로그인 브라우저 화면) 로 이동되었다면, 소셜 로그인을 시도하면 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;56-authorization-code-발급--redirect-url-로-리다이렉트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#56-authorization-code-%EB%B0%9C%EA%B8%89--redirect-url-%EB%A1%9C-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%ED%8A%B8&quot; aria-label=&quot;56 authorization code 발급  redirect url 로 리다이렉트 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5~6. Authorization Code 발급 + Redirect URl 로 리다이렉트&lt;/h3&gt;
&lt;p&gt;사용자가 앞서 올바른 소셜 계정을 입력하고 인증을 마쳤다면, Authorization Server 는 Authorization Code 를 발급해주고 지정한 Redirect URI 로 사용자를 리다이렉션 시켜줍니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;이때 Redirect URI 는 백엔드가 아닌, 프론트엔드의 URI 로 리다이렉션 시켜줘야합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;Authorization Code 는 어떻게 발급해줄까?&lt;/h4&gt;
&lt;p&gt;=&gt; &lt;strong&gt;Redirect URI에 Authorization Code 를 포함하여 사용자를 리다이렉션 시켜주는 방식입니다.&lt;/strong&gt; 구글의 경우 Authorization Code 를 QueryString에 포함하는 방식입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;78-authorization-code-로-accesstoken-발급후-저장하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#78-authorization-code-%EB%A1%9C-accesstoken-%EB%B0%9C%EA%B8%89%ED%9B%84-%EC%A0%80%EC%9E%A5%ED%95%98%EA%B8%B0&quot; aria-label=&quot;78 authorization code 로 accesstoken 발급후 저장하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7~8. Authorization Code 로 AccessToken 발급후 저장하기&lt;/h3&gt;
&lt;p&gt;발급받은 Authorization Code 는 Access Token 을 발급받기위한 임시 코드입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;클라이언트(Client) 는 Authorization Server 에 Authorization Code 를 전송하고, Access Token 을 발급받으면 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;클라이언트는 발급받은 Resource Owner(사용자)의 Access Token 을 DB 에도 저장해두고, 로컬 스토리지에도 저장해두는 처리가 필요합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이후 Resource Server(ex.구글, 페이스북) 에서 Resource Owner(사용자)의 리소스에 접근하기 위해서 Access Token 을 사용합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Authorization Code 와 Access Token 의 교환은 어디서 이루어질까? : OAuth 엔드포인트&lt;/h4&gt;
&lt;p&gt;이들의 교환은 OAuth 토큰 엔드포인트에서 이루어집니다.
더 자세한 내용은 &lt;a href=&quot;https://cloud.google.com/apigee/docs/api-platform/security/oauth/configuring-oauth-endpoints-and-policies?hl=ko&quot;&gt;Google Cloud Docs : OAuth 엔드포인트 이해&lt;/a&gt; 를 참고하시면 좋을듯합니다.&lt;/p&gt;
&lt;p&gt;아래 예시는 토큰 엔드포인트에서 Access Token 을 발급받기 위한 HTTP 요청의 예시입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;지정된 프론트앤드 URI 로 리다이렉트가 되었다면, 함께 전달된 Authorization Code 를 백엔드 API를 통해 백엔드로 전달해야합니다. Authrorization Code 를 전달받은 백엔드는 Authorization Code, Client ID, CLient Secret 등으로 Authorization Code 로 부터 Access Token 을 발급받으면 됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이때 grant_type은 항상 authorization_code 로 설정되어 있어야합니다. 또 code 에는 발급받은 Authorization Code 를 할당하시면 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;POST /auth/getSocialToken HTTP/1.1
Host:https://google.com

grant_type=authorization_code
&amp;amp;redirect_uri=https://maestro:com/main
&amp;amp;code=12123125123
&amp;amp;client_id=221399881
&amp;amp;client_secret=2131231235&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;911-인증이-필요한-api-요청시-access-token-을-활용해-요청하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#911-%EC%9D%B8%EC%A6%9D%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-api-%EC%9A%94%EC%B2%AD%EC%8B%9C-access-token-%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%B4-%EC%9A%94%EC%B2%AD%ED%95%98%EA%B8%B0&quot; aria-label=&quot;911 인증이 필요한 api 요청시 access token 을 활용해 요청하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;9~11. 인증이 필요한 API 요청시, Access Token 을 활용해 요청하기&lt;/h3&gt;
&lt;p&gt;AccessToken 을 발급받은 Resource Owner 는 이제 로그인에 성공한 것입니다.&lt;/p&gt;
&lt;p&gt;앞으로 클라이언트는 저장해둔 Resource Owner 의 Access Token 을 활용해 Resource Server 에 필요한 자원을 요청하면 됩니다. 그리고 Resource Owner 에게 서버스를 제공해주면 되겠죠?&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;authorization-code-을-왜-써야할까-필요성에-대해&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#authorization-code-%EC%9D%84-%EC%99%9C-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C-%ED%95%84%EC%9A%94%EC%84%B1%EC%97%90-%EB%8C%80%ED%95%B4&quot; aria-label=&quot;authorization code 을 왜 써야할까 필요성에 대해 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Authorization Code 을 왜 써야할까? 필요성에 대해..&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄요약 : URL에 직접 Access Token 을 전송받는 방식은 위험하니, 대신에 임시코드(Authorization Code) 를 발급받고 추후에 안전하게 발급받자!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;앞선 Authorization Code Grant 방식을 보며 느낀점은, 왜 Authorization Code 를 굳이 써야할까라는 의문이 들었습니다. 그냥 바로Access Token 을 발급해줘도 되지 않을까요?&lt;/p&gt;
&lt;p&gt;앞선 과정(5~6번)을 잘 떠올려봅시다. Redirect URI 를 통해서만 Authorization Code 를 발급받을 수 있다고했죠? 그런데 Access Token 을 발급받을때도 마찬가지로, &lt;strong&gt;Redriect URI 를 통해서만 URL 안에 데이터를 실어서 전달받는 방법밖에 존재하지 않습니다&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이렇게 민감한 정보는 URL에 실려서 오면 정말 위험하겠죠? 따라서 Authorization Code 를 통해 우선 덜 민감한 정보를 URL로 발급받고, 추후 안전한 방법으로 Access Token 을 발급받는 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://auth0.com/docs/&quot;&gt;auth0 docuement&lt;/a&gt;
&lt;a href=&quot;https://ko.wikipedia.org/wiki/OAuth&quot;&gt;위키피디아 OAuth&lt;/a&gt;
&lt;a href=&quot;https://minholee93.tistory.com/entry/OAuth20-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EB%93%B1%EB%A1%9D%ED%95%98%EA%B8%B0&quot;&gt;[OAuth2.0] 애플리케이션 등록하기 by 페이스북
&lt;/a&gt;&lt;a href=&quot;https://blog.naver.com/mds_datasecurity/222182943542&quot;&gt;https://blog.naver.com/mds_datasecurity/222182943542&lt;/a&gt;
&lt;a href=&quot;https://benohead.com/&quot;&gt;https://benohead.com/&lt;/a&gt;
&lt;a href=&quot;https://velog.io/@usreon/google-%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%82%BD%EC%A7%88-%EA%B3%BC%EC%A0%95&quot;&gt;0Auth - 구글 소셜로그인 기능 구현
&lt;/a&gt;&lt;a href=&quot;https://libertegrace.tistory.com/entry/40-Authentication-OAuth-20?category=869766&quot;&gt;Grace&apos;s Tech Blog : Authentication - OAuth2.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[웹소켓과 HTTP Polling 에 대해 알아보자!]]></title><description><![CDATA[웹소켓(Web Socket…]]></description><link>https://haon.site/haon/server/websocket/</link><guid isPermaLink="false">https://haon.site/haon/server/websocket/</guid><pubDate>Sat, 14 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;웹소켓web-socket-이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%B9%EC%86%8C%EC%BC%93web-socket-%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;웹소켓web socket 이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;웹소켓(Web Socket) 이란&lt;/h2&gt;
&lt;p&gt;웹소켓은 웹 어플리케이션을 위한 양방향 통신기법으로, 실시간성을 보장할 수 있는 방식입니다. 따라서 실시간성을 보장하는 서비스, 예를들어 게임, 체팅, 실시간 주석 차트, 가상화폐 거래소등에서 활용이됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;웹소켓 프로토콜은 웹 브라우저와 웹 서버간의 통신을 허용하는 프로토콜입니다.&lt;/li&gt;
&lt;li&gt;또 HTTP 통신과의 차이점 : HTTP 는 클라이언트가 서버에 요청하는 것이지만, 웹소켓은 서버와 클라이언트간에 &quot;양방향 요청&quot; 이 가능하다는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;http-polling-long-polling-streaming&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-polling-long-polling-streaming&quot; aria-label=&quot;http polling long polling streaming permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP Polling, Long Polling, Streaming&lt;/h2&gt;
&lt;p&gt;사실 HTTP 프로토콜에 대해 잘 파악하고 계신 분이라면 HTTP 방식으로도 실시간성을 보장하는 기법이 존재한다는 것을 알고 계실겁니다. HTTP 와 같은 단방향 프로토콜로, 제한된 웹 환경에서 웹 브라우저와 서버 사이의 양방향 실시간 통신을 위한 다양한 위회 기법들이 존재합니다. 그들은 비교.분석 해보겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;http-polling&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-polling&quot; aria-label=&quot;http polling permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP Polling&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0237d8d2-518d-493e-8d13-6b81f7947802/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Polling 은 웹 브라우저가 주기적으로 서버에게 이벤트가 발생했는지 확인하는 방식입니다. 만일 이벤트가 발생하지 않았다면 Response 에는 이벤트 정보가 포함되지 않고, 반대로 이벤트가 발생했다면 포함되는 방식입니다.&lt;/p&gt;
&lt;p&gt;다만 &lt;strong&gt;주기적으로 서버에게 이벤트 발생여부를 확인하는 방식이므로 실시간성이 떨어지는 단점이 있으며,&lt;/strong&gt; 주기적인 요청/응답이 오가기 때문에 계속 트래픽이 발생한다는 문제도 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;long-polling&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#long-polling&quot; aria-label=&quot;long polling permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Long Polling&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/f859f2ad-cad2-49ce-9331-9ad3cf321f82/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Long Polling 은 기존 Polling 의 실시간성을 보완한 기법입니다. 웹 브라우저가 요청을 전송할때 이벤트가 발생하기 전까지는 Response 를 보내지 않고, &lt;strong&gt;이벤트가 발생할때만 Response 를 이벤트 정보와 함께 담아서 보내는 방식입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;실시간성을 보완한 좋은 방식이 되었지만, 그러나 서버의 이벤트가 자주 발생하지 않는 환경에서 이용하는 것이 좋습니다. 서버의 이벤트가 자주 발생하면 비효율적인 트레픽이 발생할 수 있기 때문이죠.&lt;/p&gt;
&lt;h2 id=&quot;server-sent-events&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#server-sent-events&quot; aria-label=&quot;server sent events permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Server-sent Events&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/15320e10-a5c9-466b-82a8-1767303f6072/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;마지막으로 Server-sent Events 기법은 유추할 수 있듯이, 클라이언트의 요청은 없이 &lt;strong&gt;서버에서 클라이언트에게 단방향으로 이벤트를 전송하는 기법입니다.&lt;/strong&gt; 즉, 클라이언트에서 서버로는 이벤트를 전송할 수는 없는 방식입니다.
또 클라이언트는 서버에게 특정 이벤트를 구독하는(Subscribe) 요청을 전송합니다. 이후 서버는 해당 이벤트가 발생시 웹 브라우저에게 해당 이벤트를 전송합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;웹소켓-vs-http--실시간성에서-어떤기법이-더-유리한가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%B9%EC%86%8C%EC%BC%93-vs-http--%EC%8B%A4%EC%8B%9C%EA%B0%84%EC%84%B1%EC%97%90%EC%84%9C-%EC%96%B4%EB%96%A4%EA%B8%B0%EB%B2%95%EC%9D%B4-%EB%8D%94-%EC%9C%A0%EB%A6%AC%ED%95%9C%EA%B0%80&quot; aria-label=&quot;웹소켓 vs http  실시간성에서 어떤기법이 더 유리한가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;웹소켓 vs HTTP : 실시간성에서 어떤기법이 더 유리한가?&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/67bf9bde-a5c9-4a7f-be83-ccfd207626fc/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;HTTP 방식과의 차이점을 다시 간단히 짚고 넘어갈 필요가 있습니다. 정리해보자자면 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;HTTP&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;비 연결성(Connection less)&lt;/li&gt;
&lt;li&gt;매번 연결을 맺고 끊는 과정의 비용이 많이듭니다.&lt;/li&gt;
&lt;li&gt;Request - Response 의 구조를 지닙니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Web Socket&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;연결지향적인 특징을 지닙니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;한번 연결을 맺은 뒤 계속 유지되는 실시간성을 보장합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;양방향 통신이라는 특징을 지닙니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;HTTP는 클라이언트가 원하는 어떤 결과를 얻고자한다면 항상 서버에 요청을 보내야하지만, 웹소켓은 연결이 계속 유지되고 있는 상태이므로 &lt;strong&gt;클라이언트가 보낸 메시지를 서버는 그냥 듣고있기만 하면 됩니다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;HTTP 에 비해 웹소켓이 보내야하는 메시지, 데이터 양이 훨씬 적습니다.&lt;/strong&gt;
HTTP 요청을 보낼시 Request URL, 상태코드, Request&amp;#x26;Response 헤더와 바디, .. 등의 데이터 양이 매번 요청으로 만들어져서 서버로 보내진다면 실시간성을 요구하는 서비스에서는 꽤 큰 부담이 생길것입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;웹 소켓을 사용할때 처음 HandShake를 하는 과정은 HTTP 프로토콜을 통해 진행하겠지만, &lt;strong&gt;한번 연결이 수립된 후로는 간단한 메시지만 오가는 방식입니다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;따라서 앞서 살펴본 HTTP Polling 과 같은 방식으로 실시간성을 보장하는 서비스에 활용하는 것은 그리 좋은 방법은 아닐겁니다. 웹소켓이 아무리 보더라도 실시간 통신에 있어서 더 유리한 점이 많기 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;stomp는-왜-써야할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stomp%EB%8A%94-%EC%99%9C-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;stomp는 왜 써야할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;STOMP는 왜 써야할까?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;웹소켓은&lt;/strong&gt; 기본적으로 텍스트와 바이너라 타입의 메시지만을 양방향으로 주고받을 수 있는 프로토콜이라고 했습니다. 그러나, 그 메시지를 &lt;strong&gt;어떤식으로 주고받을지는 사실 따로 정해진 것이 없습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;그런데 프로젝트 투입 인원이 많을수록, 클라이언트와 서버가 어떤 형식으로 메시지를 주고받으며, 타입을 어떠하며, 그 메시지의 본문과 설정정보와 같은 데이터들을 어떻게 구분할지를 따로 정의해줘야합니다. 별도로 정하지 않는다면 꽤 혼동스러울 겁니다.&lt;/p&gt;
&lt;p&gt;사용할시의 장점을 정리해보자면 다음과 같습니다.&lt;/p&gt;
&lt;h3 id=&quot;stomp-사용시-장점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stomp-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%9E%A5%EC%A0%90&quot; aria-label=&quot;stomp 사용시 장점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;STOMP 사용시 장점&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;메시징 프로토콜과 메시징 형식을 개발할 필요가 없습니다. (즉, 하위 프로토콜과 메시지 컨벤션을 정의할 필요가 없는 것입니다!)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;연결 주소마다 새로운 핸들러를 구현하고 설정해줄 필요가 없습니다.&lt;/li&gt;
&lt;li&gt;메시지 브로커를 사용하면 구독을 관리하고 메시지를 Broadcast 하는데 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;외부 Messaging Queue 를 사용할 수 있습니다. (RabbitMQ, Kafka, ... )&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;STOMP 를 사용하면 형식을 고민하고, 파싱하는 코드를 구현할 필요도 없어진다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;stomp-frame&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stomp-frame&quot; aria-label=&quot;stomp frame permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;STOMP Frame&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;COMMAND
header1:value1
header2:value2

Body^@&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;STOMP 를 사용하면 고민할 필요가 없어진다고 했었죠? 위처럼 STOMP는 COMMAND, header, Body 라는 형식으로 골격을 정의해두었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/f753f68c-64a0-4200-a84f-29cdda950c4b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위 그림에서 왼쪽은 순수 웹소켓만 사용했을 때이고, 오른쪽이 STOMP를 사용했을 때입니다. 웹소캣만 사용했을 때는 서버에서 보는, 그냥 날 것의 메시지만 오고가는 반면에, STOMP 를 사용했을때는 커맨드, 헤더, 바디의 형태로 메시지가 오고가는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;stomp-기반의-통신흐름&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stomp-%EA%B8%B0%EB%B0%98%EC%9D%98-%ED%86%B5%EC%8B%A0%ED%9D%90%EB%A6%84&quot; aria-label=&quot;stomp 기반의 통신흐름 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;STOMP 기반의 통신흐름&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/92ffa347-037a-498a-abd8-c73b181af004/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;스프링이 STOMP를 사용하고 있을떄의 동작 흐름을 살펴봅시다. 왼쪽을 보면 메시지를 보내는 발신자와, 메시지를 받고자하는 구독자가 있습니다. 발신자는 이 구독자들에게 메시지를 보내고 싶어하고, 구독자들은 이 &quot;/topic&quot; 이라는 경로를 구독하고 있다고 가정해봅시다.&lt;/p&gt;
&lt;p&gt;이 상황에서 발신자는 바로 &quot;/topic&quot; 이라는 경로를 통해서 &quot;/topic&quot; 이라는 헤더를 destination 헤더로 넣어서 메시지를 송신할 수도 있겠지만, 서버 내에서의 어떤 처리, 혹은 가공이 필요하다면 이 &quot;/app&quot; 이라는 주소로 메시지를 송신하게 됩니다.&lt;/p&gt;
&lt;p&gt;그리고 서버가 이를 모두 마쳤다면 이 가공되거나 처리된 메시지를 &quot;/topic&quot; 이라는 경로에 담아서 다시 전송하게 되면 이 메시지가 메시지브로커에게 전달되게 되고, 그 다음에 이 메시지브로커는 전달받은 메시지를 &quot;/topic&quot; 을 구독하고 있는 구독자들에 최종적으로 전달이 됩니다.&lt;/p&gt;
&lt;h4&gt;정리&lt;/h4&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;구독자들은 &quot;/topic&quot; 경로를 구독하고 있는 상황&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;발신자는 서버를 통한 가공 및 처리를 위해 &quot;/app&quot; 주소로 메시지를 송신합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;가공을 마친 메시지를 &quot;/topic&quot; 이라는 경로에 담아서 전송하면 그 메시지를 메시지브로커가 전달받습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;이 메시지브로커는 그 메시지를 &quot;/topic&quot; 을 구독중인 구독자들에게 최종적으로 전달합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/WebSocket&quot;&gt;WebSocket Document&lt;/a&gt;
&lt;a href=&quot;https://techblog.woowahan.com/2547/&quot;&gt;우아한 형제들 기술블로그&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.daddyprogrammer.org/post/4691/spring-websocket-chatting-server-stomp-server/&quot;&gt;https://www.daddyprogrammer.org/post/4691/spring-websocket-chatting-server-stomp-server/&lt;/a&gt;
&lt;a href=&quot;https://www.joinc.co.kr/w/man/12/websocket&quot;&gt;https://www.joinc.co.kr/w/man/12/websocket&lt;/a&gt;
&lt;a href=&quot;https://www.youtube.com/watch?v=gQyRxPjssWg&amp;#x26;t=3085s&quot;&gt;https://www.youtube.com/watch?v=gQyRxPjssWg&amp;#x26;t=3085s&lt;/a&gt;
&lt;a href=&quot;https://ssup2.github.io/theory_analysis/Web_Polling_Long_Polling_Server-sent_Events_WebSocket/&quot;&gt;https://ssup2.github.io/theory_analysis/Web_Polling_Long_Polling_Server-sent_Events_WebSocket/&lt;/a&gt;
&lt;a href=&quot;https://www.youtube.com/watch?v=yXPCg5eupGM&amp;#x26;t=340s&quot;&gt;https://www.youtube.com/watch?v=yXPCg5eupGM&amp;#x26;t=340s&lt;/a&gt;
&lt;a href=&quot;https://www.youtube.com/watch?v=rvss-_t6gzg&amp;#x26;t=924s&quot;&gt;https://www.youtube.com/watch?v=rvss-_t6gzg&amp;#x26;t=924s&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Jenkins 와 Docker를 활용한 배포 자동화 구축하기]]></title><description><![CDATA[왜 Jenkins 를 사용하게 되었는가? 지난 포스팅 [Docker] SpringBoot 스냅샷 jar 파일을 도커를 이용해 EC2 서버에 배포하기 에서는 로컬에서 개발한 스프링부트 프로젝트를 "수동"으로 직접 Docker Image…]]></description><link>https://haon.site/haon/infra/ci-cd/jenkins-deployment-with-docker/</link><guid isPermaLink="false">https://haon.site/haon/infra/ci-cd/jenkins-deployment-with-docker/</guid><pubDate>Mon, 09 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;왜-jenkins-를-사용하게-되었는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-jenkins-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B2%8C-%EB%90%98%EC%97%88%EB%8A%94%EA%B0%80&quot; aria-label=&quot;왜 jenkins 를 사용하게 되었는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 Jenkins 를 사용하게 되었는가?&lt;/h2&gt;
&lt;p&gt;지난 포스팅 &lt;a href=&quot;https://velog.io/@msung99/Docker-%EB%8F%84%EC%BB%A4%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-SNAPSHOT-jar-%ED%8C%8C%EC%9D%BC-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot;&gt;[Docker] SpringBoot 스냅샷 jar 파일을 도커를 이용해 EC2 서버에 배포하기&lt;/a&gt; 에서는 로컬에서 개발한 스프링부트 프로젝트를 &quot;수동&quot;으로 직접 Docker Image 를 빌드하고, 도커허브에 올린후에 EC2 서버에서 도커 이미지를 직접 가져와서 컨테이너를 실행시키는 방식으로 배포를 진행했습니다. 현재 진행중인 사이드 프로젝트에서도 수정사항이 생길때마다 매번 이미지를 빌드하고 다시 배포하는 방식을 진행했었습니다.&lt;/p&gt;
&lt;p&gt;그러나 &lt;strong&gt;개발자가 매번 직접 도커 이미지를 빌드하고, 도커 허브에 올리고, 클라우드 서버에 땡겨와서 배포하는 방식은 정말 불편하다고 느껴졌습니다.&lt;/strong&gt; 따라서 배포 자동화에 관한 학습을 진행하다가, CI/CD 를 접하게 되었고 이렇게 학습을 시작하게 되었습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;어떻게-자동화-배포-서버를-구축할-것인가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%9E%90%EB%8F%99%ED%99%94-%EB%B0%B0%ED%8F%AC-%EC%84%9C%EB%B2%84%EB%A5%BC-%EA%B5%AC%EC%B6%95%ED%95%A0-%EA%B2%83%EC%9D%B8%EA%B0%80&quot; aria-label=&quot;어떻게 자동화 배포 서버를 구축할 것인가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어떻게 자동화 배포 서버를 구축할 것인가?&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/bee77b1d-0304-461f-9061-e1d8505dab38/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위와 같은 배포 자동화 시스템을 구축할 예정입니다. Jenkins 와 Docker를 사용해 어떻게 배포가 자동화 되는지를 먼저 간단히 나열해보자면 아래와 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;로컬에서 작업한 내용을 Jenkins와 연동된 깃허브 원격 레포지토리에 push 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Webhook : 새롭게 push된 내용을 기반으로 Jenkins 서버에서 Gradle 을 통해 build를 실행합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Gradlew build 를 통해 Jar 파일이 자동 생성되고, 해당 jar파일을 기반으로 도커 이미지가 자동으로 빌드됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;생성된 도커 이미지는 본인의 DockerHub 에 push됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SpringBoot 프로젝트를 배포할 EC2 서버에서 직전에 도커허브에 올라간 도커 이미지를 pull 받아옵니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;내려받은 도커 이미지를 기반으로 컨테이너에 감싸서 해당 프로젝트를 실행시켜줍니다.(docker run)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;dockerfile-작성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dockerfile-%EC%9E%91%EC%84%B1&quot; aria-label=&quot;dockerfile 작성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dockerfile 작성&lt;/h2&gt;
&lt;p&gt;우선 스프링부트 프로젝트에 도커파일을 작성했습니다. 이를 기반으로 빌드를 하고, 이미지를 실행해야겠죠?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ff6e8c11-ba2f-47af-8be0-6b8c3397d299/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위와 같이 루트 디렉토리에 도커파일을 위치시켰고, 또 아래아 같이 도커파일을 구성해줬습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;FROM openjdk:11-jdk
LABEL maintainer=&quot;email&quot;
ARG JAR_FILE=build/libs/core-0.0.1-SNAPSHOT.jar
ADD ${JAR_FILE} docker-springboot.jar
ENTRYPOINT [&quot;java&quot;,&quot;-Djava.security.egd=file:/dev/./urandom&quot;,&quot;-jar&quot;,&quot;/docker-springboot.jar&quot;]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;jenkins-서버-환경구축&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jenkins-%EC%84%9C%EB%B2%84-%ED%99%98%EA%B2%BD%EA%B5%AC%EC%B6%95&quot; aria-label=&quot;jenkins 서버 환경구축 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jenkins 서버 환경구축&lt;/h2&gt;
&lt;p&gt;우선 Jenkins 서버에 도커를 설치해주고, Jenkins 이미지를 내려받고 서버를 실행시켜줄 겁니다. 아래와 같이 따라해줍시다.&lt;/p&gt;
&lt;h4&gt;1. Docker 설치&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo apt update
sudo apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository &quot;deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable&quot;
sudo apt update
apt-cache policy docker-ce
sudo apt install docker-ce&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;2. Jenkins Image Pull + Run&lt;/h4&gt;
&lt;p&gt;다음으로 젠킨스 이미지를 도커허브로부터 내려받고, 해당 이미지를 컨테이너로 감싸서 실행시켜줍시다. 아래와 같이 해주세요!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker pull jenkins/jenkins:lts
docker run --privileged -d -p 8080:8080 -p 50000:50000 --name jenkins jenkins/jenkins:lts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h5&gt;2023.01.09 내용 추가&lt;/h5&gt;
&lt;p&gt;아래 방법은 사용하지 마세요..! 계속 제가 포스팅한 내용은 따라하시다보면 알겠지만, 나중에 권한문제로 에러가 계속 발생합니다! 위의 내용으로 docker run 을 해주세요!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;docker pull jenkins&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;jenkins&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;lts
docker run &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;d &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token number&quot;&gt;50000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;50000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;name jenkins jenkins&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;jenkins&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;lts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;이때 젠킨스 서버는 기본적으로 8080포트를 기반으로 접속 가능하기 때문에, &lt;strong&gt;컨테이너 포트를 8080 으로 실행&lt;/strong&gt;시켜주시켜야 한다는 점을 유의해주세요!&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3. Jenkins 초기 관리자 비밀번호&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/3436814c-7719-42aa-b81c-2fa66bb9aa71/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;jenkins의 초기 관리자 비밀번호를 요구하는 화면입니다.&lt;/p&gt;
&lt;p&gt;해당 경로(/var/jenkins_home/secrets/initialAdminPassword)에 있는 파일의 내용을 읽어서 Administrator password 란에 입력해주면됩니다.&lt;/p&gt;
&lt;p&gt;이 파일은 docker volume을 사용하지 않았다면, docker 내부에만 존재하는 파일이므로 아래와 같은 명령어를 입력하여 jenkins container에 접근해야합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;docker &lt;span class=&quot;token keyword&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;it jenkins &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;bash&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;docker container 내부 쉘에서&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;cat &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;var&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;jenkins_home&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;secrets&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;initialAdminPassword&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;을 입력하면 초기 관리자 비밀번호가 출력되게 됩니다.&lt;/p&gt;
&lt;h4&gt;4. Jenkins 관련 플러그인 설치&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6be7b760-5623-4d20-81e0-37692cd6a832/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;앞서 인증에 성공했다면, 위와 같은 화면이 나올텐데 어떤 방식으로 플러그인을 설치할 것인지 선택하는 란이 나오게 되는 것입니다. 저희는 Install suggested plugin 을 선택하여 필수적인 플러그인들을 모두 설치받도록 해줍시다.&lt;/p&gt;
&lt;h4&gt;5. 관리자 계정 생성&lt;/h4&gt;
&lt;p&gt;플러그인 설치가 완료되면 어드민 계정을 생성하는 페이지가 나오게됩니다. 생성을 하시면 Jenkins 메인 페이지가 나오게 됩니다! 여기까지하면 기본 셋팅은 완료된 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ci-구축--github-와-jenkins-연동&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ci-%EA%B5%AC%EC%B6%95--github-%EC%99%80-jenkins-%EC%97%B0%EB%8F%99&quot; aria-label=&quot;ci 구축  github 와 jenkins 연동 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CI 구축 : GitHub 와 Jenkins 연동&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;젠킨스 메인페이지에서 새로운 Item -&gt; FreeStyle Project 를 생성해줍시다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/3cfda1e5-010a-4234-8c63-8c5339fbaf30/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;가장 먼저 GitHub Project 를 선택해주고, 본인이 연동하고자 할 깃허브 레포지토리 URL 를 넣어줍니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e154d233-19a8-49b3-8a29-94c1736a3f54/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;다음으로 &quot;소스 코드 관리&quot;로 넘어가서, Git 을 선택해주고 마찬가지로 깃허브 레포지토리 URL 를 넣어줍시다. 그러고 Credentials 라는 란이 나오게 되는데, 이는 Jenkins 와 깃허브간에 데이터를 주고받을 때의 인증 방식을 의미하는 것입니다.
보통 현업에서는 SSH-key 인증 방식을 사용하지만, 저희는 임의로 간단한 테스트 프로젝트를 생성하고 연동하는 것이 목표이기 때문에 깃허브 계정 로그인 인증방식을 사용하겠습니다.&lt;/p&gt;
&lt;p&gt;맨 처음에는 Credential란에 None 이 뜰텐데, 해당 None 을 클릭후 add 를 클릭하여 본인의 깃허브 계정의 아이디와 비밀번호를 입력해주시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/83ea20ea-8ada-46fb-9c42-d44c91d0b430/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;다음으로 깃허브에 push가 될때 build 가 실행될 브랜치를 선택할 수 있습니다. 저는 임의로 생성한 테스트 레포지토리에서 메인 브랜치를 master가 아닌 main 브렌치로 설정했기 때문에, main 브랜치로 바꿔줬습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6d407050-217b-4e8c-8287-dfb65e20fa44/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그 다음으로 빌드을 어떤 방식으로 진행할지 지정할 수 있는데(build trigger), 저희는 위와 같은 방법을 택하도록 하겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;execute-shell-빌드-실행내용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#execute-shell-%EB%B9%8C%EB%93%9C-%EC%8B%A4%ED%96%89%EB%82%B4%EC%9A%A9&quot; aria-label=&quot;execute shell 빌드 실행내용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Execute shell 빌드 실행내용&lt;/h3&gt;
&lt;p&gt;다음으로 build steps 에서 빌드할 내용을 지정해주시면 됩니다. 아래와 같이 작성해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;chmod &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;x gradlew
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;gradlew clean build&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7ebefe89-3bd7-4f70-bf47-6be6b8f17a74/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;추가적으로 docker 에 관한 실행 명령이 있는데, 이는 gradlew 파일을 기반으로 빌드하는 것에 대한 주제와 벗어나는 내용이므로 추후에 설명드리도록 하겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;webhook-연동--github--jenkins&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webhook-%EC%97%B0%EB%8F%99--github--jenkins&quot; aria-label=&quot;webhook 연동  github  jenkins permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;WebHook 연동 : Github + Jenkins&lt;/h2&gt;
&lt;p&gt;다음으로는 연동하고자 했던 해당 깃허브 레포지토리에 접속해서 Webhook을 설정해주셔야 합니다. 레포지토리의 Settings -&gt; Webhooks 란으로 들어가줍시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/3c58ad5d-32d6-4ec8-800f-1dd680f59f93/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그리고 Webhook 을 추가해줍시다. 그리고 아래와 같이 구성해주셔야 합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Payload URL : 젠킨스 IP주소:호스트 포트번호/github-webhook/&lt;/li&gt;
&lt;li&gt;Content type : application/json&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;Payload URL 포맷 예시
http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123.456&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.78&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;github&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;webhook&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; 맨 마지막에 github&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;webhook&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; 을 꼭 붙여주셔야합니다!&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/3a42895c-f28c-4f12-a1fd-b2daa47b61f2/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;만일 앞서 Jenkins 에서 설정시 Credentials 를 로그인 인증방식이 아닌, ssh-key 인증방식을 택하셨다면 github Deploy 란에서도 따로 ssh-key 를 등록해주셔야 연동이 가능해집니다! 다만, 저는 이번 테스트에서 앞서 봤듯이 User-password 방식을 택했으므로 ssh-key 를 등록하지 않았습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;jenkins-와-springboot-배포서버-연동&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jenkins-%EC%99%80-springboot-%EB%B0%B0%ED%8F%AC%EC%84%9C%EB%B2%84-%EC%97%B0%EB%8F%99&quot; aria-label=&quot;jenkins 와 springboot 배포서버 연동 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jenkins 와 SpringBoot 배포서버 연동&lt;/h2&gt;
&lt;p&gt;다음으로 스프링부트 프로젝트가 올라가는 EC2 서버와 연동을 시켜보는 방법을 알아봅시다. 이때 알고 가셔야 할점은 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Jenkins 서버에서 jar파일이 빌드되고 생성된 도커 이미지가 도커 허브에 push 될텐데, 이 도커 이미지를 스프링부트 서버에서 pull 받고 실행시켜야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;이때 젠킨스 서버와 SpringBoot 서버가 서로 연동이 되어있어야만 도커 허브를 통해 도커 이미지를 옮기는 것이 가능할겁니다.&lt;/strong&gt; 이를 가능하게 해주는 작업을 하는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;ssh-key 인증방식을 통한 연동을 진행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;1-ssh-key-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-ssh-key-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;1 ssh key 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. ssh key 생성&lt;/h3&gt;
&lt;p&gt;배포서버에 접근하기 위해, PEM 형식의 key 를 생성해야합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;ssh&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;keygen &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;t rsa &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;C &lt;span class=&quot;token string&quot;&gt;&quot;키명칭&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;m PEM &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;P &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;root&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ssh&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;키명칭&quot;&lt;/span&gt;

ex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ssh&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;keygen &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;t rsa &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;C &lt;span class=&quot;token string&quot;&gt;&quot;mykey&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;m PEM &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;P &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;root&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ssh&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mykey&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6e697e3a-4ea1-413b-8d9a-67b9ff38c9d5/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;저와 똑같이 따라하셨다면 위와 같이 ssh key 가 public, private 2개가 생성된 것을 볼 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-스프링부트-배포서버-public-key-등록&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EB%B0%B0%ED%8F%AC%EC%84%9C%EB%B2%84-public-key-%EB%93%B1%EB%A1%9D&quot; aria-label=&quot;2 스프링부트 배포서버 public key 등록 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 스프링부트 배포서버 public key 등록&lt;/h3&gt;
&lt;p&gt;앞서 생성한 key 중에서 public key를 배포서버의 .ssh/authorized_keys 파일에 추가해주셔야 합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;//&lt;/span&gt; jenkins서버
sudo cat &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;root&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ssh&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;키명칭&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pub

ex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; sudo cat &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;root&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ssh&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mykey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pub

&lt;span class=&quot;token operator&quot;&gt;//&lt;/span&gt; 배포서버
vi &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ssh&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;authorized_keys&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;vi 편집기로 authorized_keys 에 접속했다면 앞서 jenkins서버에서 생성한 public key 를 복붙해주시면 됩니다!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;또한, 만일 authorized_keys 에 Read Only File 이라는 에러 메시지가 뜨면서 저장에 계속 실패한다면 쓰기 권한이 없어서 그런것입니다. authorized_keys 는 보안과 관련한 정말 중요한 파일이므로, 함부로 쓰고 저장하는 것이 안되기 때문이죠.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;저장에 실패하시는 분들은 아래 명령을 통해 권한을 풀어주시기 바랍니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo vi .ssh/authorized_keys

// =&gt; sudo 관리자 권한으로 authorized_keys 파일 편집하기&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/86d258cf-65ba-40ec-8f76-671ec0ae2189/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-publish-over-ssh-플러그인-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-publish-over-ssh-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;3 publish over ssh 플러그인 설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Publish Over SSH 플러그인 설정&lt;/h3&gt;
&lt;p&gt;이제 Jenkins의 플러그인을 통해 젠킨스 서버와 스프링부트 서버를 제대로 연동해줄 차례입니다. 앞선 과정은 ssh key 를 등록함으로써 데이터를 주고받을 때 인증하기 위한 과정이였다면, 이 과정은 실질적인 연동과정입니다.&lt;/p&gt;
&lt;h4&gt;플러그인 설치&lt;/h4&gt;
&lt;p&gt;Jenkins 관리 &gt; 플러그인 관리 &gt; 설치가능 탭 에서 Publish Over SSH 플러그인을 검색하여 설치하고 젠킨스를 재실행하여 플러그인이 적용 될 수 있도록 해주시면 됩니다!&lt;/p&gt;
&lt;h4&gt;플러그인 연동&lt;/h4&gt;
&lt;p&gt;다음으로 Jenkins 관리 &gt; 시스템 설정 &gt; Publish over SSH 로 접속해줍시다. 여기서 스프링부트 서버와 연동을 시도할 수 있는겁니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ebfcd44b-ea93-4d6e-956c-2e9f3f2f6427/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;우선 Passphrase 부분을 채워줍니다. &lt;strong&gt;접속하려는 서버(스프링부트 서버)의 비밀번호&lt;/strong&gt;를 입력해주시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/45b35b4f-06c7-4900-be2a-b56e90525c2d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;또한, 아래 SSH Servers 에서 &quot;추가&quot; 를 클릭해줍니다. 채워야할 란은 다음과 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Name : 임의의 서버 이름&lt;/li&gt;
&lt;li&gt;HostName : 접속하려는 서버의 IP 주소&lt;/li&gt;
&lt;li&gt;Username : 접속하려는 서버의 로그인 아이디&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e0856e15-1c2f-4771-a37f-fd47c2560271/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;까지 입력하신후, Test Configuration 을 통해 연동을 테스트해봅시다. 위와 같이 Success 가 뜬다면 연동에 성공한 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;dind--jenkins-컨테이너안에-도커-설치-및-권한설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dind--jenkins-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%95%88%EC%97%90-%EB%8F%84%EC%BB%A4-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EA%B6%8C%ED%95%9C%EC%84%A4%EC%A0%95&quot; aria-label=&quot;dind  jenkins 컨테이너안에 도커 설치 및 권한설정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DinD : Jenkins 컨테이너안에 도커 설치 및 권한설정&lt;/h2&gt;
&lt;p&gt;다음으로 Jenkins 컨테이너 안에 도커를 설치(DinD)하고, 몇가지 권한 설정을 해줄겁니다.&lt;/p&gt;
&lt;h3 id=&quot;권한-문제로-인한-도커-컨테이너-재시작&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B6%8C%ED%95%9C-%EB%AC%B8%EC%A0%9C%EB%A1%9C-%EC%9D%B8%ED%95%9C-%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%9E%AC%EC%8B%9C%EC%9E%91&quot; aria-label=&quot;권한 문제로 인한 도커 컨테이너 재시작 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;권한 문제로 인한 도커 컨테이너 재시작&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;본격적인 시작에 앞서, jenkins 컨테이너 안에서 도커 설치를 하는데 권한 관련 문제가 꽤 많더라구요. 혹시 저와 비슷한 관련 문제를 걲을 분들은 아래와 같은 명령을 통해서 도커 컨테이너를 새롭게 생성하시길 바랍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2023.01.09 내용추가&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;만일 앞서 &quot;--privileged&quot; 옵션을 통해 권한 부여를 이미 해줬다면, 현재 내용은 건너뛰셔도 됩니다!&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;docker stop &lt;span class=&quot;token string&quot;&gt;&apos;컨테이너 id&apos;&lt;/span&gt;    &lt;span class=&quot;token operator&quot;&gt;//&lt;/span&gt; 해당 컨테이너 실행을 멈추고
docker commit jenkins newjenkinsimages  &lt;span class=&quot;token operator&quot;&gt;//&lt;/span&gt; 해당 컨테이너에 대한 복사본 커밋 도커 이미지를 생성
docker run &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;privileged &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;d &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token number&quot;&gt;8081&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token number&quot;&gt;50001&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;50000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;name newjenkins newjenkinsimagemages   &lt;span class=&quot;token operator&quot;&gt;//&lt;/span&gt; 새로운 컨테이너에서 작업 내용을 그대로 가져와서 재시작&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/77233c24-5a97-4bc8-b28a-1909642ad5b3/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;정상적으로 실행되었다면, 아래와 같이 jenkins 컨테이너에 접속해주세요!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;docker &lt;span class=&quot;token keyword&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;itu &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;jenkins 컨테이너 id&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;bash

ex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; docker &lt;span class=&quot;token keyword&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;itu &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; 81fba6bc7732 &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;bash&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;jenkins-컨테이너에-도커-설치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jenkins-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%97%90-%EB%8F%84%EC%BB%A4-%EC%84%A4%EC%B9%98&quot; aria-label=&quot;jenkins 컨테이너에 도커 설치 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;jenkins 컨테이너에 도커 설치&lt;/h3&gt;
&lt;p&gt;이제 jenkins 컨테이너에다 도커를 설치해야 합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;# Docker 설치
## - Old Version Remove
apt-get remove docker docker-engine docker.io containerd runc
## - Setup Repo
apt-get update
apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
  &quot;deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
  $(lsb_release -cs) stable&quot; | tee /etc/apt/sources.list.d/docker.list &gt; /dev/null
## - Install Docker Engine
apt-get update
apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.opendocs.co.kr/?p=704&quot;&gt;참고: jenkins 컨테이너 내부에서 도커 실행&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;다음으로 아래와 같은 명령어를 통해 도커데몬을 실행해주시면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;service docker start
systemctl start docker&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://help.iwinv.kr/manual/read.html?idx=583&quot;&gt;참고 : Docker 수동으로 데몬 시작하기&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;jenkins--docker-그룹-추가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jenkins--docker-%EA%B7%B8%EB%A3%B9-%EC%B6%94%EA%B0%80&quot; aria-label=&quot;jenkins  docker 그룹 추가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jenkins &amp;#x26; Docker 그룹 추가&lt;/h2&gt;
&lt;h4&gt;1. Docker 그룹에 root 계정 추가&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;usermod -aG docker root
su - root&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;2. 그룹에 잘 추가되었는지 확인하기&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;id -nG    // &quot;root docker&quot; 가 뜨면 정상&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;3. docker.sock 권한 변경&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;chmod 666 /var/run/docker.sock&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;4. root 에서 도커 로그인&lt;/h4&gt;
&lt;p&gt;젠킨스에서 도커허브에 빌드 된 이미지를 푸쉬할 수 있도록 root 유저로 들어가서 도커 로그인을 해주도록 합니다. 여기서 로그인시 입력하는 아이디와 암호는 위에서 가입한 도커허브의 아이디와 암호를 입력하면 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;su - root
docker login&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만일 root 에서 도커 로그인 시도시에 권한문제로 &quot;su:Authentication Failure&quot; 를 마주했다면, 아래 블로깅을 참고하세요!
(사실 sudo 를 현재 젠킨스 컨테이너 안에 설치해야하는데, 설치 과정을 생략하느라 문제가 발생할 수 있습니다..!)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;#x26;blogId=cthu3801&amp;#x26;logNo=220710165047&quot;&gt;참고 - su : Authentication Failure&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;docker를-활용해-jenkins-서버에서-스프링부트-서버로-배포-자동화하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#docker%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%B4-jenkins-%EC%84%9C%EB%B2%84%EC%97%90%EC%84%9C-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%84%9C%EB%B2%84%EB%A1%9C-%EB%B0%B0%ED%8F%AC-%EC%9E%90%EB%8F%99%ED%99%94%ED%95%98%EA%B8%B0&quot; aria-label=&quot;docker를 활용해 jenkins 서버에서 스프링부트 서버로 배포 자동화하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Docker를 활용해 Jenkins 서버에서 스프링부트 서버로 배포 자동화하기&lt;/h2&gt;
&lt;p&gt;이제 거의 다 왔습니다. 아까 생성했던 젠킨스 Item (FreeStyle Project) 가 있죠? 거기에 다시 들어가서 docker 와 관련한 명령어들을 작성해야 합니다.&lt;/p&gt;
&lt;h4&gt;1. docker 이미지 생성 및 도커허브에 push&lt;/h4&gt;
&lt;p&gt;Build Steps 에서 Add build steps 을 클릭하여 Execute shell 을 추가해줍니다. 그리고 아래와 같이 작성해줍시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1a6d4f68-56ed-449e-9d77-fd12a1f83194/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker login -u &apos;도커허브아아디&apos; -p &apos;도커허브비번&apos; docker.io
docker build -t [dockerHub UserName]/[dockerHub Repository]:[version]
docker push [dockerHub UserName]/[dockerHub Repository]:[version]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;2. 스프링부트 서버에서 docker pull 및 이미지 실행시키기&lt;/h4&gt;
&lt;p&gt;아래와 같이 빌드환경란으로 가서, Send files of execute commands over SSH after the build runs 를 선택해줍니다. 그리고 아래와 같이 스프링부트 서버에서 어떤 동작을 수행할지 정의해주면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/dac6f2d4-a7d2-4294-81c2-3e7e9ab560ea/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;docker login &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;u &lt;span class=&quot;token string&quot;&gt;&apos;도커허브아아디&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token string&quot;&gt;&apos;도커허브비번&apos;&lt;/span&gt; docker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io
docker pull &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;dockerHub UserName&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;dockerHub Repository&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;version&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
docker ps &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;q &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;filter&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;containerName&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; grep &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;q &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; docker rm &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f $&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;docker ps &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;aq &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;filter&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;containerName&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
docker run &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;d &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;name &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;containerName&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token number&quot;&gt;9001&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9001&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;dockerHub UserName&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;dockerHub Repository&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;version&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 도커 명령어 각 줄을 해석해보자면 다음과 같습니다.&lt;/p&gt;
&lt;h4&gt;1. docker pull&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;이전에 젠킨스 서버에서 올려놓은 도커이미지를 스프링부트 서버에서 pull 하는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;docker pull &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;dockerHub UserName&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;dockerHub Repository&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;version&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;2. docker ps -1 --filter ~~~&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;배포서버에서 도커컨테이너 이름으로 검색하여 있으면 해당 컨테이너를 정지하고 삭제하는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;docker ps &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;q &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;filter&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;containerName&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; grep &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;q &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; docker rm &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f $&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;docker ps &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;aq &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;filter&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;containerName&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;3. docker run&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;새롭게 받아온 도커 이미지를 스프링부트 서버에서 실행시켜주는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;docker run &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;d &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;name &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;containerName&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token number&quot;&gt;9001&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9001&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;dockerHub UserName&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;dockerHub Repository&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;version&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;build-및-배포-테스트-결과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#build-%EB%B0%8F-%EB%B0%B0%ED%8F%AC-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B2%B0%EA%B3%BC&quot; aria-label=&quot;build 및 배포 테스트 결과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Build 및 배포 테스트 결과&lt;/h2&gt;
&lt;p&gt;gradle 파일을 기반으로 성공적으로 빌드가 되는 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/81ff1bfb-7f9b-4981-a04e-9a1f0621a9e2/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;최종적으로 SUCCESS 라는 결과를 확인할 수 있게 되었습니다!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6f962028-1538-4e55-868d-ddeb63628de8/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;트러블-슈팅-및-해결과정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-%EB%B0%8F-%ED%95%B4%EA%B2%B0%EA%B3%BC%EC%A0%95&quot; aria-label=&quot;트러블 슈팅 및 해결과정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트러블 슈팅 및 해결과정&lt;/h2&gt;
&lt;h4&gt;1.docker.sock 에 대한 권한을 설정안해주고, docker login 도 안해줬더니 발생한 에러&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;위에 제가적은 부제목 &quot;Jenkins &amp;#x26; Docker 그룹 추가&quot; 의 내용을 똑같이 실행했더니, 문제가 해결되었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;참고자료&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://technote.kr/369&quot;&gt;Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post &quot;http://%2Fvar%2Frun%2Fdocker.sock/v1.24/auth&quot;: dial unix /var/run/docker.sock: connect: permission denied&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;2. Docker COPY failed: no source files were specified 에러&lt;/h4&gt;
&lt;p&gt;도커파일 안의 내용이 문제였습니다. 이전에 제가 프로젝트를 할때 /build/libs 에서 jar 파일과 같은 디렉토리 상에 도커파일이 위치하도록 하고, 그에 따른 도커파일 내용을 작성했었는데, 이 도커파일 내용을 그대로 이번 Jenkins 자동화 실험에서 그대로 인용하니 에러가 발생했습니다. 해결방법은 이번 포스팅의 도커파일과 같이 도커파일의 내용을 바꿔줬습니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@appti/%EB%8F%84%EC%BB%A4%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8&quot;&gt;도커파일 작성 내용 참고&lt;/a&gt;
&lt;a href=&quot;https://bgpark.tistory.com/132&quot;&gt;Docker COPY failed: no source files were specified 에러
&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;service docker start
systemctl start docker&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금까지 도커와 젠킨스를 활용해 CI/CD 를 구축하는 방법에 대해 알아봤습니다. 저도 타 블로깅을 참고하며 이렇게 구축을 하는데 성공했지만, 계속 에러가 발생하더라구요. 또 애매한 이론 및 명령어 설명으로인해 꽤 애먹었던 것 같네요. 그래서 많은 분들이 최대한 제 현재 포스팅을 보시고, 고생안하시고 도움이 되셨으면 하는 바람에서 나름 deep하게 적은것 같네요!&lt;/p&gt;
&lt;p&gt;궁금한 내용이 있으시다면 댓글 꼭 넘겨주세요!! 제가 아는선에서 도와드리겠습니다 😉&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@haeny01/AWS-Jenkins%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-Docker-x-SpringBoot-CICD-%EA%B5%AC%EC%B6%95#-item-%EC%83%9D%EC%84%B1&quot;&gt;[AWS] Jenkins를 활용한 Docker x SpringBoot CI/CD 구축
&lt;/a&gt;&lt;a href=&quot;https://junghwanta.tistory.com/45&quot;&gt;Docker/Jenkins 를 활용한 웹서버 자동 배포 &amp;#x26; Image 자동 배포&lt;/a&gt;
&lt;a href=&quot;https://velog.io/@hind_sight/Docker-Jenkins-%EB%8F%84%EC%BB%A4%EC%99%80-%EC%A0%A0%ED%82%A8%EC%8A%A4%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-Spring-Boot-CICD#%EC%9E%91%EC%84%B1-%EB%8F%99%EA%B8%B0&quot;&gt;[Docker &amp;#x26; Jenkins] 도커와 젠킨스를 활용한 Spring Boot CI/CD🥸
&lt;/a&gt;
&lt;a href=&quot;https://velog.io/@dion/Jenkins-with-Docker-and-GitHub#jenkins%EC%99%80-github-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0&quot;&gt;Jenkins with Docker and GitHub
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://league-cat.tistory.com/347&quot;&gt;- 도커 설치 후 도커 명령어 실행 에러 Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://help.iwinv.kr/manual/read.html?idx=583&quot;&gt;Docker 데몬(Daemon)으로 수동 시작&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://hitomis.tistory.com/95&quot;&gt;systemctl 실행문제 : privileged 권한으로 다시 이미지를 실행하기&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.opendocs.co.kr/?p=704&quot;&gt;jenkins 설치 내부에서 docker 실행&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;#x26;blogId=cthu3801&amp;#x26;logNo=220710165047&quot;&gt;su:Authentication failure&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://iamrealizer.tistory.com/47&quot;&gt;E212: Can&apos;t open file for writing 에러 해결 하기&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://hyunmin1906.tistory.com/282&quot;&gt;Jenkins에서 sudo 권한 사용&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://starseeker711.tistory.com/176&quot;&gt;리눅스에서 sudo 권한이 없을때&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://domdom.tistory.com/374&quot;&gt;[Ubuntu] 리눅스에서 ... is not in the sudoers file. This incident will be reported. 문제 해결 방법&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://kit2013.tistory.com/187&quot;&gt;useradd와 adduser의 차이&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[도커를 이용해 EC2 서버에 jar 배포하기]]></title><description><![CDATA[EC2 서버에 Docker 이미지가 올라가는 과정  본격적인 시작전에, 어떻게 Docker…]]></description><link>https://haon.site/haon/server/docker-ec2-deployment/</link><guid isPermaLink="false">https://haon.site/haon/server/docker-ec2-deployment/</guid><pubDate>Thu, 05 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;ec2-서버에-docker-이미지가-올라가는-과정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ec2-%EC%84%9C%EB%B2%84%EC%97%90-docker-%EC%9D%B4%EB%AF%B8%EC%A7%80%EA%B0%80-%EC%98%AC%EB%9D%BC%EA%B0%80%EB%8A%94-%EA%B3%BC%EC%A0%95&quot; aria-label=&quot;ec2 서버에 docker 이미지가 올라가는 과정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EC2 서버에 Docker 이미지가 올라가는 과정&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/801226c6-c63b-4a8f-a16b-147f8a19888b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;본격적인 시작전에, 어떻게 Docker 로 생성한 이미지가 컨테이너에 감싸져서 배포가 이루어지는지 알아봅시다.&lt;/p&gt;
&lt;p&gt;우선 본인의 로컬에서 진행한 프로젝트 내용이 있을겁니다. 해당 프로젝트의 내용을 하나의 jar 파일로 압축시킨 후, 이를 Dockerfile 를 통해 이미지화 시킨 후 DockerHub 에 push 하는 겁니다.&lt;/p&gt;
&lt;p&gt;그 후 클라우드 EC2 서버에서 직전에 push 했던 도커 이미지를 내려받은 후(pull), Docker run을 통해 해당 이미지를 감싸고 있는 도커 컨테이너를 실행시켜서 서버를 배포시키는 방식입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;jar-파일을-빌드하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jar-%ED%8C%8C%EC%9D%BC%EC%9D%84-%EB%B9%8C%EB%93%9C%ED%95%98%EA%B8%B0&quot; aria-label=&quot;jar 파일을 빌드하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;jar 파일을 빌드하기&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a9f2fb8e-a3b9-4e13-900e-f689cbd182bc/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;gradle 의 bootJar 라는 것이 있습니다. 이를 활용해 jar 파일을 빌드해주셔야 합니다.&lt;/p&gt;
&lt;h3 id=&quot;jar-파일이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jar-%ED%8C%8C%EC%9D%BC%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;jar 파일이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;jar 파일이란?&lt;/h3&gt;
&lt;p&gt;jar 파일을 여러개의 클래스 파일들과 관련 리소스 및 메타 데이터를 하나의 파일로 모아서 배포를 위해 만들어진 SW 패키지 파일 포맷입니다. 또 jar는 zip 으로 이루어진 압축 파일입니다.&lt;/p&gt;
&lt;p&gt;빌드에 성공하셨다면 아래와 같이 build/libs 디렉토리에 jar 파일이 성공적으로 빌드된 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a9daed4d-8648-4c32-a7fb-62da4c54b168/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;dockerfile-생성하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dockerfile-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0&quot; aria-label=&quot;dockerfile 생성하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DockerFile 생성하기&lt;/h2&gt;
&lt;p&gt;다음으로는 DockerFile 을 생성할 차례입니다. 도커 파일이란 &lt;strong&gt;도커 이미지를 생성하기 위한 DSL으로서 몇가지 명령어들 커스텀 이미지를 만들 수 있게 해줍니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/17e9dbf2-938c-4e5d-a91c-5815e6d88aec/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Dockerfile 은 직전에 생성한 jar 파일이 위치한 곳, 즉 build/libs 파일에서 생성되어야 합니다. 따라서 이 경로로 이동하여 Docker 파일을 작성해주도록 합시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;cd /build/libs
ls&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;도커파일의 내용은 아래와 같습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;FROM openjdk:11
ARG JAR_FILE=*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT [&quot;java&quot;,&quot;-jar&quot;,&quot;/app.jar&quot;]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;FROM : 기반이 되는 이미지를 의미하고, jdk 11 버전을 사용함을 명시했습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ARG : 컨테이너 내에서 사용할 수 있는 변수를 지정할 수 있습니다. 즉, 여기서는 확장자가 &quot; .jar &quot; 인 변수(파일)을 Docker 컨테이너에서 사용하겠음을 명시한 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;COPY : ARG의 JAR_FILE 변수를 컨테이너의 app.jar에 복사한다는 뜻입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ENTRYPOINT : 컨테이너가 시작됐을 떄 실행할 스크립트를 명시합니다. 직전에 COPY 를 통해 app.jar 에 카피한 jar 파일을 도커 컨테이너가 시작됐을 때 실행하기 위해 명시한 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;docker-image-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#docker-image-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;docker image 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Docker Image 생성&lt;/h2&gt;
&lt;p&gt;다음으로 도커 이미지를 생성할 차례입니다. Dockerfile을 빌드해줌으로써 도커 이미지가 생성되는데, 터미널에 Dockerfile이 있는 위치에서 다음 명령어를 써주면 Dockerfile이 빌드가 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker build -t &apos;도커에 가입한 본인의 계정&apos;/&apos;프로젝트명&apos;:&apos;버전&apos; &apos;경로&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/2c85a779-a95a-477b-b80b-9a46b5d73d7d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker build `옵션` `도커에가입한 계정`/`프로젝트명`:`버전` `경로`
&gt;&gt;&gt; docker build -t msung99/maestroproject:0.1.0 .&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이때 맥북 M1 칩을 사용하시는 분들을 주의하셔야 할 사항이 있습니다. 현재 본인이 사용중인 맥북이 Apple M1칩이라면 도커가 해당 이미지를 빌드할때 생성된 빌드 플랫폼이 클라우드 서버(linux) 와 m1 맥북간의 호환성이 안맞는 문제가 발생합니다.&lt;/p&gt;
&lt;p&gt;맥북 m1 의 경우 아래의 명령을 통해 호환성을 맞춰주도록 사전에 제대로 이미지를 빌드해주도록 합시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker build -platform linux/amd64 -t `도커에가입한 계정`/`프로젝트명`:`버전` `경로`&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이미지가 정상적으로 생성되었는지는 아래와 같이 확인할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker images    // 모든 도커 이미지 조회&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7933c85c-9ceb-4656-b45c-e0a16b904e50/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;dockerhub-레포지토리에-push&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dockerhub-%EB%A0%88%ED%8F%AC%EC%A7%80%ED%86%A0%EB%A6%AC%EC%97%90-push&quot; aria-label=&quot;dockerhub 레포지토리에 push permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DockerHub 레포지토리에 push&lt;/h2&gt;
&lt;p&gt;이제 도커허브에 이미지를 올릴 차례입니다. 도커허브의 레포지토리에 방금 생성한 이미지를 push 하고, 추후 클라우드 서버에서 해당 레포지토리로 부터 도커 이미지를 가져오는 방식입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&gt;&gt;&gt; docker push &apos;이미지명&apos;

docker push msung99/maestro:0.1.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;도커허브에서 해당 레포지토리에 접속하시면 정상적으로 이미지가 push 된 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/88e37c2b-26ff-4eb9-ae34-a0673352ceec/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ec2-클라우드-서버에-docker-설치하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ec2-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EC%84%9C%EB%B2%84%EC%97%90-docker-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0&quot; aria-label=&quot;ec2 클라우드 서버에 docker 설치하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EC2 클라우드 서버에 Docker 설치하기&lt;/h2&gt;
&lt;p&gt;이제 EC2 서버에 도커를 설치하고, 아까 도커허브에 올렸던 도커 이미지를 pull 받은 후 이 서버에서 스프링부트 프로젝트를 실행시키면 됩니다. 아래와 같이 도커를 위한 환경셋팅을 진행해줍시다.&lt;/p&gt;
&lt;p&gt;참고로 저는 Ubuntu18.04 LTS 버전을 기반으로 클라우드 서버를 구축했습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EC2 서버 구축하는 방법은 이번 주제에 너무 벗어나는 내용이니, 설명은 제외하도록 하겠습니다.&lt;/li&gt;
&lt;li&gt;만일 sudo 를 매번 타이밍하는 것이 번거롭다면 아래 설치 과정 이전에 &quot;sudo su&quot; 를 명령하여 sudo 를 매번 타이밍하는 일이 없도록 합시다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo apt update
sudo apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository &quot;deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable&quot;
sudo apt update
apt-cache policy docker-ce
sudo apt install docker-ce&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;docker-image-pull&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#docker-image-pull&quot; aria-label=&quot;docker image pull permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Docker Image pull&lt;/h2&gt;
&lt;p&gt;직전에 설명드렸듯이 이제 도커 이미지를 레포지토리로부터 서버에 내려받으면 됩니다. 아래와 같이 내려받도록 해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo docker pull &apos;레포지토리명&apos;

sudo docker pull msung99/maestro:0.1.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;아래와 같이 pull 을 정상적으로 실행했다면 성공입니다!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/f8ff4fd8-9bdd-434f-bc0c-276b3c4c1880/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;도커-컨테이너-실행시키기-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%8B%A4%ED%96%89%EC%8B%9C%ED%82%A4%EA%B8%B0-1&quot; aria-label=&quot;도커 컨테이너 실행시키기 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도커 컨테이너 실행시키기 (1)&lt;/h2&gt;
&lt;p&gt;마지막 단계입니다. 내려받은 도커 이미지를 컨테이너에 감싸서 실행시켜주면 끝입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0f15a3d1-2631-4d95-b6c3-970f506944b2/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo docker run --name &apos;컨테이너명&apos; -d -p &apos;호스트 포트&apos;:&apos;컨테이너 포트&apos; &apos;레포지토리명&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo docker run --name &apos;container&apos; -d -p 8080:8080 msung99/maestro:0.1.0

docker build --platform linux/amd64 -t msung99/maestro:0.1.0 .
=&gt; 두번째는 맥북 m1 칩의 경우일때 실행하면 됩니다. 아까 말씀드렸던 호환성 문제 때문입니다.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;도커-컨테이너-실행시키기-2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%8B%A4%ED%96%89%EC%8B%9C%ED%82%A4%EA%B8%B0-2&quot; aria-label=&quot;도커 컨테이너 실행시키기 2 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도커 컨테이너 실행시키기 (2)&lt;/h2&gt;
&lt;p&gt;또는 아래와 같이 실행시킬 수도 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker run -p 호스트 포트&apos;:&apos;컨테이너 포트&apos; &apos;레포지토리명&apos;

docker run -p 9001:9001 msung99/maestro:0.1.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5fe821a4-3540-49b2-8b48-227b6d09f6b5/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;docker-컨테이너-status-조회&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#docker-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-status-%EC%A1%B0%ED%9A%8C&quot; aria-label=&quot;docker 컨테이너 status 조회 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Docker 컨테이너 STATUS 조회&lt;/h2&gt;
&lt;p&gt;이렇게까지 잘 따라해주셨다면 도커 이미지가 컨테이너에 감싸서 정상적으로 실행되고 있을겁니다. 혹시 모르니 정상적으로 실행되고 있는지를 확인해보고 싶다면, 아래와 같은 명령를 통해 컨테이너의 상태를 조회해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker ps -a&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/00d1595f-6c95-482f-b9c8-003c929a023c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;만일 STATUS 가 EXITED 라면 정상 실행되지 않은 것으로, 어떤 문제가 발생했는지 다시 확인해봐야 합니다. 방금 생성한 도커 컨테이를 삭제시켜주고, 각자 발생한 문제를 해결하고 와야합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;삭제하려는 도커 컨테이너가 만일 실행중인 경우 stop 을 해주고 삭제 시켜줘야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker stop container
docker rm container&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;정상-실행-조회&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EC%83%81-%EC%8B%A4%ED%96%89-%EC%A1%B0%ED%9A%8C&quot; aria-label=&quot;정상 실행 조회 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정상 실행 조회&lt;/h2&gt;
&lt;p&gt;아래와 같이 생성한 IP주소를 9001번 포트로 접속해보면 정상적으로 서버가 구축되었음을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/fd3e4698-7945-4980-9410-1ef6c26b99d9/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며--더-공부해볼-내용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0--%EB%8D%94-%EA%B3%B5%EB%B6%80%ED%95%B4%EB%B3%BC-%EB%82%B4%EC%9A%A9&quot; aria-label=&quot;마치며  더 공부해볼 내용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며 &amp;#x26; 더 공부해볼 내용&lt;/h2&gt;
&lt;p&gt;이렇게 로컬에서 작업했던 스프링부트 프로젝트를 도커를 통해 EC2서버에 배포하는 과정까지 다루어보았습니다. 다만 현 포스팅에서 내용을 담지못해 아쉬웠던 점은 다음과 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;도커란 무엇인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;호스트 포트와 컨테이너 포트란 무엇이며, 둘의 차이는 무엇인가?&lt;/li&gt;
&lt;li&gt;호환성 : 맥북 m1에 기반한 서버는 왜 linux amd64 서버와 호환이 안되는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이번에 도커를 학습하며 함께 학습했던 내용이나, 위 내용들을 모두 다루기엔 포스팅 내용이 너무 길어진다 판단하여 추후에 다루기로 결정했습니다.&lt;/p&gt;
&lt;p&gt;궁금하신 점이나 도움을 받고 싶은 내용이 있다면 댓글로 남겨주시면 도와드리겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://sas-study.tistory.com/425&quot;&gt;[Docker](이미지 플랫폼 관련 에러 The requested image&apos;s platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) ...&lt;/a&gt;
&lt;a href=&quot;https://velog.io/@wind1992/Django-Docker%EB%A1%9C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0#5-%EB%8F%84%EC%BB%A4%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A5%BC-%EB%8F%84%EC%BB%A4%ED%97%88%EB%B8%8C%EC%97%90-%ED%91%B8%EC%8B%9C%ED%95%98%EA%B8%B0git-push%EB%9E%91-%EB%B9%84%EC%8A%B7&quot;&gt;Django-Docker로-배포하기#5-도커이미지를-도커허브에-푸시하기git-push랑-비슷&lt;/a&gt;
&lt;a href=&quot;https://so-es-immer.tistory.com/entry/%EB%A7%A5-mac%EC%97%90%EC%84%9C-%EB%8F%84%EC%BB%A4-%EC%97%90%EB%9F%AC-Cannot-connect-to-the-Docker-daemon-%EC%96%B4%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%ED%82%A4%EC%9E%90&quot;&gt;[Docker] 도커에서 Container 포트와 Host 포트의 개념&lt;/a&gt;
&lt;a href=&quot;https://rkdals213.tistory.com/32&quot;&gt;Docker를 이용한 Spring 프로젝트 배포&lt;/a&gt;
&lt;a href=&quot;https://galid1.tistory.com/428&quot;&gt;Spring jar 배포&lt;/a&gt;
&lt;a href=&quot;&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[도커를 이용해 EC2 서버에 배포하기]]></title><description><![CDATA[EC2 서버에 Docker 이미지가 올라가는 과정  본격적인 시작전에, 어떻게 Docker…]]></description><link>https://haon.site/haon/infra/deploy/docker-basic/</link><guid isPermaLink="false">https://haon.site/haon/infra/deploy/docker-basic/</guid><pubDate>Thu, 05 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;ec2-서버에-docker-이미지가-올라가는-과정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ec2-%EC%84%9C%EB%B2%84%EC%97%90-docker-%EC%9D%B4%EB%AF%B8%EC%A7%80%EA%B0%80-%EC%98%AC%EB%9D%BC%EA%B0%80%EB%8A%94-%EA%B3%BC%EC%A0%95&quot; aria-label=&quot;ec2 서버에 docker 이미지가 올라가는 과정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EC2 서버에 Docker 이미지가 올라가는 과정&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/801226c6-c63b-4a8f-a16b-147f8a19888b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;본격적인 시작전에, 어떻게 Docker 로 생성한 이미지가 컨테이너에 감싸져서 배포가 이루어지는지 알아봅시다.&lt;/p&gt;
&lt;p&gt;우선 본인의 로컬에서 진행한 프로젝트 내용이 있을겁니다. 해당 프로젝트의 내용을 하나의 jar 파일로 압축시킨 후, 이를 Dockerfile 를 통해 이미지화 시킨 후 DockerHub 에 push 하는 겁니다.&lt;/p&gt;
&lt;p&gt;그 후 클라우드 EC2 서버에서 직전에 push 했던 도커 이미지를 내려받은 후(pull), Docker run을 통해 해당 이미지를 감싸고 있는 도커 컨테이너를 실행시켜서 서버를 배포시키는 방식입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;jar-파일을-빌드하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jar-%ED%8C%8C%EC%9D%BC%EC%9D%84-%EB%B9%8C%EB%93%9C%ED%95%98%EA%B8%B0&quot; aria-label=&quot;jar 파일을 빌드하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;jar 파일을 빌드하기&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a9f2fb8e-a3b9-4e13-900e-f689cbd182bc/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;gradle 의 bootJar 라는 것이 있습니다. 이를 활용해 jar 파일을 빌드해주셔야 합니다.&lt;/p&gt;
&lt;h3 id=&quot;jar-파일이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jar-%ED%8C%8C%EC%9D%BC%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;jar 파일이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;jar 파일이란?&lt;/h3&gt;
&lt;p&gt;jar 파일을 여러개의 클래스 파일들과 관련 리소스 및 메타 데이터를 하나의 파일로 모아서 배포를 위해 만들어진 SW 패키지 파일 포맷입니다. 또 jar는 zip 으로 이루어진 압축 파일입니다.&lt;/p&gt;
&lt;p&gt;빌드에 성공하셨다면 아래와 같이 build/libs 디렉토리에 jar 파일이 성공적으로 빌드된 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a9daed4d-8648-4c32-a7fb-62da4c54b168/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;dockerfile-생성하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dockerfile-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0&quot; aria-label=&quot;dockerfile 생성하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DockerFile 생성하기&lt;/h2&gt;
&lt;p&gt;다음으로는 DockerFile 을 생성할 차례입니다. 도커 파일이란 &lt;strong&gt;도커 이미지를 생성하기 위한 DSL으로서 몇가지 명령어들 커스텀 이미지를 만들 수 있게 해줍니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/17e9dbf2-938c-4e5d-a91c-5815e6d88aec/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Dockerfile 은 직전에 생성한 jar 파일이 위치한 곳, 즉 build/libs 파일에서 생성되어야 합니다. 따라서 이 경로로 이동하여 Docker 파일을 작성해주도록 합시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;cd /build/libs
ls&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;도커파일의 내용은 아래와 같습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;FROM openjdk:11
ARG JAR_FILE=*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT [&quot;java&quot;,&quot;-jar&quot;,&quot;/app.jar&quot;]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;FROM : 기반이 되는 이미지를 의미하고, jdk 11 버전을 사용함을 명시했습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ARG : 컨테이너 내에서 사용할 수 있는 변수를 지정할 수 있습니다. 즉, 여기서는 확장자가 &quot; .jar &quot; 인 변수(파일)을 Docker 컨테이너에서 사용하겠음을 명시한 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;COPY : ARG의 JAR_FILE 변수를 컨테이너의 app.jar에 복사한다는 뜻입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ENTRYPOINT : 컨테이너가 시작됐을 떄 실행할 스크립트를 명시합니다. 직전에 COPY 를 통해 app.jar 에 카피한 jar 파일을 도커 컨테이너가 시작됐을 때 실행하기 위해 명시한 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;docker-image-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#docker-image-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;docker image 생성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Docker Image 생성&lt;/h2&gt;
&lt;p&gt;다음으로 도커 이미지를 생성할 차례입니다. Dockerfile을 빌드해줌으로써 도커 이미지가 생성되는데, 터미널에 Dockerfile이 있는 위치에서 다음 명령어를 써주면 Dockerfile이 빌드가 됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker build -t &apos;도커에 가입한 본인의 계정&apos;/&apos;프로젝트명&apos;:&apos;버전&apos; &apos;경로&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/2c85a779-a95a-477b-b80b-9a46b5d73d7d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker build `옵션` `도커에가입한 계정`/`프로젝트명`:`버전` `경로`
&gt;&gt;&gt; docker build -t msung99/maestroproject:0.1.0 .&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이때 맥북 M1 칩을 사용하시는 분들을 주의하셔야 할 사항이 있습니다. 현재 본인이 사용중인 맥북이 Apple M1칩이라면 도커가 해당 이미지를 빌드할때 생성된 빌드 플랫폼이 클라우드 서버(linux) 와 m1 맥북간의 호환성이 안맞는 문제가 발생합니다.&lt;/p&gt;
&lt;p&gt;맥북 m1 의 경우 아래의 명령을 통해 호환성을 맞춰주도록 사전에 제대로 이미지를 빌드해주도록 합시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker build -platform linux/amd64 -t `도커에가입한 계정`/`프로젝트명`:`버전` `경로`&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이미지가 정상적으로 생성되었는지는 아래와 같이 확인할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker images    // 모든 도커 이미지 조회&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7933c85c-9ceb-4656-b45c-e0a16b904e50/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;dockerhub-레포지토리에-push&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dockerhub-%EB%A0%88%ED%8F%AC%EC%A7%80%ED%86%A0%EB%A6%AC%EC%97%90-push&quot; aria-label=&quot;dockerhub 레포지토리에 push permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DockerHub 레포지토리에 push&lt;/h2&gt;
&lt;p&gt;이제 도커허브에 이미지를 올릴 차례입니다. 도커허브의 레포지토리에 방금 생성한 이미지를 push 하고, 추후 클라우드 서버에서 해당 레포지토리로 부터 도커 이미지를 가져오는 방식입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&gt;&gt;&gt; docker push &apos;이미지명&apos;

docker push msung99/maestro:0.1.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;도커허브에서 해당 레포지토리에 접속하시면 정상적으로 이미지가 push 된 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/88e37c2b-26ff-4eb9-ae34-a0673352ceec/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ec2-클라우드-서버에-docker-설치하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ec2-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EC%84%9C%EB%B2%84%EC%97%90-docker-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0&quot; aria-label=&quot;ec2 클라우드 서버에 docker 설치하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EC2 클라우드 서버에 Docker 설치하기&lt;/h2&gt;
&lt;p&gt;이제 EC2 서버에 도커를 설치하고, 아까 도커허브에 올렸던 도커 이미지를 pull 받은 후 이 서버에서 스프링부트 프로젝트를 실행시키면 됩니다. 아래와 같이 도커를 위한 환경셋팅을 진행해줍시다.&lt;/p&gt;
&lt;p&gt;참고로 저는 Ubuntu18.04 LTS 버전을 기반으로 클라우드 서버를 구축했습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EC2 서버 구축하는 방법은 이번 주제에 너무 벗어나는 내용이니, 설명은 제외하도록 하겠습니다.&lt;/li&gt;
&lt;li&gt;만일 sudo 를 매번 타이밍하는 것이 번거롭다면 아래 설치 과정 이전에 &quot;sudo su&quot; 를 명령하여 sudo 를 매번 타이밍하는 일이 없도록 합시다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo apt update
sudo apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository &quot;deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable&quot;
sudo apt update
apt-cache policy docker-ce
sudo apt install docker-ce&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;docker-image-pull&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#docker-image-pull&quot; aria-label=&quot;docker image pull permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Docker Image pull&lt;/h2&gt;
&lt;p&gt;직전에 설명드렸듯이 이제 도커 이미지를 레포지토리로부터 서버에 내려받으면 됩니다. 아래와 같이 내려받도록 해줍시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo docker pull &apos;레포지토리명&apos;

sudo docker pull msung99/maestro:0.1.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;아래와 같이 pull 을 정상적으로 실행했다면 성공입니다!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/f8ff4fd8-9bdd-434f-bc0c-276b3c4c1880/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;도커-컨테이너-실행시키기-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%8B%A4%ED%96%89%EC%8B%9C%ED%82%A4%EA%B8%B0-1&quot; aria-label=&quot;도커 컨테이너 실행시키기 1 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도커 컨테이너 실행시키기 (1)&lt;/h2&gt;
&lt;p&gt;마지막 단계입니다. 내려받은 도커 이미지를 컨테이너에 감싸서 실행시켜주면 끝입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0f15a3d1-2631-4d95-b6c3-970f506944b2/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo docker run --name &apos;컨테이너명&apos; -d -p &apos;호스트 포트&apos;:&apos;컨테이너 포트&apos; &apos;레포지토리명&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo docker run --name &apos;container&apos; -d -p 8080:8080 msung99/maestro:0.1.0

docker build --platform linux/amd64 -t msung99/maestro:0.1.0 .
=&gt; 두번째는 맥북 m1 칩의 경우일때 실행하면 됩니다. 아까 말씀드렸던 호환성 문제 때문입니다.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;도커-컨테이너-실행시키기-2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%8B%A4%ED%96%89%EC%8B%9C%ED%82%A4%EA%B8%B0-2&quot; aria-label=&quot;도커 컨테이너 실행시키기 2 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도커 컨테이너 실행시키기 (2)&lt;/h2&gt;
&lt;p&gt;또는 아래와 같이 실행시킬 수도 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker run -p 호스트 포트&apos;:&apos;컨테이너 포트&apos; &apos;레포지토리명&apos;

docker run -p 9001:9001 msung99/maestro:0.1.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5fe821a4-3540-49b2-8b48-227b6d09f6b5/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;docker-컨테이너-status-조회&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#docker-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-status-%EC%A1%B0%ED%9A%8C&quot; aria-label=&quot;docker 컨테이너 status 조회 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Docker 컨테이너 STATUS 조회&lt;/h2&gt;
&lt;p&gt;이렇게까지 잘 따라해주셨다면 도커 이미지가 컨테이너에 감싸서 정상적으로 실행되고 있을겁니다. 혹시 모르니 정상적으로 실행되고 있는지를 확인해보고 싶다면, 아래와 같은 명령를 통해 컨테이너의 상태를 조회해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker ps -a&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/00d1595f-6c95-482f-b9c8-003c929a023c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;만일 STATUS 가 EXITED 라면 정상 실행되지 않은 것으로, 어떤 문제가 발생했는지 다시 확인해봐야 합니다. 방금 생성한 도커 컨테이를 삭제시켜주고, 각자 발생한 문제를 해결하고 와야합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;삭제하려는 도커 컨테이너가 만일 실행중인 경우 stop 을 해주고 삭제 시켜줘야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker stop container
docker rm container&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;정상-실행-조회&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EC%83%81-%EC%8B%A4%ED%96%89-%EC%A1%B0%ED%9A%8C&quot; aria-label=&quot;정상 실행 조회 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정상 실행 조회&lt;/h2&gt;
&lt;p&gt;아래와 같이 생성한 IP주소를 9001번 포트로 접속해보면 정상적으로 서버가 구축되었음을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/fd3e4698-7945-4980-9410-1ef6c26b99d9/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며--더-공부해볼-내용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0--%EB%8D%94-%EA%B3%B5%EB%B6%80%ED%95%B4%EB%B3%BC-%EB%82%B4%EC%9A%A9&quot; aria-label=&quot;마치며  더 공부해볼 내용 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며 &amp;#x26; 더 공부해볼 내용&lt;/h2&gt;
&lt;p&gt;이렇게 로컬에서 작업했던 스프링부트 프로젝트를 도커를 통해 EC2서버에 배포하는 과정까지 다루어보았습니다. 다만 현 포스팅에서 내용을 담지못해 아쉬웠던 점은 다음과 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;도커란 무엇인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;호스트 포트와 컨테이너 포트란 무엇이며, 둘의 차이는 무엇인가?&lt;/li&gt;
&lt;li&gt;호환성 : 맥북 m1에 기반한 서버는 왜 linux amd64 서버와 호환이 안되는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이번에 도커를 학습하며 함께 학습했던 내용이나, 위 내용들을 모두 다루기엔 포스팅 내용이 너무 길어진다 판단하여 추후에 다루기로 결정했습니다.&lt;/p&gt;
&lt;p&gt;궁금하신 점이나 도움을 받고 싶은 내용이 있다면 댓글로 남겨주시면 도와드리겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://sas-study.tistory.com/425&quot;&gt;[Docker](이미지 플랫폼 관련 에러 The requested image&apos;s platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) ...&lt;/a&gt;
&lt;a href=&quot;https://velog.io/@wind1992/Django-Docker%EB%A1%9C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0#5-%EB%8F%84%EC%BB%A4%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A5%BC-%EB%8F%84%EC%BB%A4%ED%97%88%EB%B8%8C%EC%97%90-%ED%91%B8%EC%8B%9C%ED%95%98%EA%B8%B0git-push%EB%9E%91-%EB%B9%84%EC%8A%B7&quot;&gt;Django-Docker로-배포하기#5-도커이미지를-도커허브에-푸시하기git-push랑-비슷&lt;/a&gt;
&lt;a href=&quot;https://so-es-immer.tistory.com/entry/%EB%A7%A5-mac%EC%97%90%EC%84%9C-%EB%8F%84%EC%BB%A4-%EC%97%90%EB%9F%AC-Cannot-connect-to-the-Docker-daemon-%EC%96%B4%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%ED%82%A4%EC%9E%90&quot;&gt;[Docker] 도커에서 Container 포트와 Host 포트의 개념&lt;/a&gt;
&lt;a href=&quot;https://rkdals213.tistory.com/32&quot;&gt;Docker를 이용한 Spring 프로젝트 배포&lt;/a&gt;
&lt;a href=&quot;https://galid1.tistory.com/428&quot;&gt;Spring jar 배포&lt;/a&gt;
&lt;a href=&quot;&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[블로깅을 통한 설명하기의 학습효과와 능동적인 공부법]]></title><description><![CDATA[…]]></description><link>https://haon.site/회고/study/</link><guid isPermaLink="false">https://haon.site/회고/study/</guid><pubDate>Sat, 31 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;시작에-앞서&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%9E%91%EC%97%90-%EC%95%9E%EC%84%9C&quot; aria-label=&quot;시작에 앞서 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시작에 앞서&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;올해를 마무리지으며, 지금껏 제가 선택하고 큰 성장을 이루게 해주었던 제 블로깅 학습방법을 공유하고자 글을 작성하게 되었습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;일반적인-블로깅-방법--학습효과가-두각되지-못하는-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%BC%EB%B0%98%EC%A0%81%EC%9D%B8-%EB%B8%94%EB%A1%9C%EA%B9%85-%EB%B0%A9%EB%B2%95--%ED%95%99%EC%8A%B5%ED%9A%A8%EA%B3%BC%EA%B0%80-%EB%91%90%EA%B0%81%EB%90%98%EC%A7%80-%EB%AA%BB%ED%95%98%EB%8A%94-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;일반적인 블로깅 방법  학습효과가 두각되지 못하는 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;일반적인 블로깅 방법 : 학습효과가 두각되지 못하는 경우&lt;/h2&gt;
&lt;p&gt;지금껏 정말 많은 사람들이 블로깅 내용을 참고했고, 도움을 많이 받았던 경험이 있습니다. 제 주변에서도 많은 지인들이 공부했던 내용을 블로깅을 하면서 기록을 해둡니다.&lt;/p&gt;
&lt;p&gt;그런데 다들 학습을 하면서 공통적으로 큰 아쉬움을 보이는 면이 많았습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;분명 공부는 했는데, 기억이 잘 안난다. 블로그에 정리까지 해두었는데!&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;충분히 이해했다고 생각하지만, 막상 응용해서 할 수 있는게 없다..&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;남들 따라서 본인도 블로깅을 한다고 하지만, 정작 동일한 시간이 부여됐는데도 불구하고 블로깅 방법에 따라서 큰 학습효과가 차이가 납니다. 저는 &lt;strong&gt;왜 블로깅을 하는가?&lt;/strong&gt; 에 대해서 다시 생각해 보셨으면 합니다. &lt;strong&gt;단순히 본인만 알아보도록 정리한 포스팅이라면, 본인 로컬 컴퓨터에 메모장으로 정리한 것과 다를바가 없는 것이죠.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;효과적인-학습방법이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%9A%A8%EA%B3%BC%EC%A0%81%EC%9D%B8-%ED%95%99%EC%8A%B5%EB%B0%A9%EB%B2%95%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;효과적인 학습방법이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;효과적인 학습방법이란?&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/040e9cfb-7b04-48f1-98e4-bf0e2d0a13bf/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위 파라미드는 다들 한번씩은 보셨을법합니다. 어떤 방식으로 학습을 하는가에 따라 큰 효율성의 차이를 보여주는 구조로, &lt;strong&gt;외부에서 학습한 내용이 본인 두뇌에 기억되는 비율&lt;/strong&gt; 을 나타내는 것입니다.&lt;/p&gt;
&lt;p&gt;단순하게 수업듣고, 읽는것은 가장 낮은 학습효과를 보이고 있습니다. 반면 &lt;strong&gt;타인에게 가르치는 학습방식은 무려 90%라는 기억률을 보여주는 큰 효과가 있습니다.&lt;/strong&gt; 이 방식은 저희가 평소에 들었을법한 방식이지만, &lt;strong&gt;다들 이 학습법을 블로깅할 때도 똑같이 학습방법을 적용할 수 있다는 사실을 간과하고 있습니다.&lt;/strong&gt; 제 주변에서도 그런분들이 정말 많이 계시구요.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;쉽게-얻은-것은-쉽게-잃는다-easy-come-easy-go-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%89%BD%EA%B2%8C-%EC%96%BB%EC%9D%80-%EA%B2%83%EC%9D%80-%EC%89%BD%EA%B2%8C-%EC%9E%83%EB%8A%94%EB%8B%A4-easy-come-easy-go-&quot; aria-label=&quot;쉽게 얻은 것은 쉽게 잃는다 easy come easy go  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쉽게 얻은 것은 쉽게 잃는다 (Easy come, Easy Go) 🧐&lt;/h2&gt;
&lt;p&gt;누군가에게 설명하기 위해서는 본인 스스로가 내용을 제대로 이해하고 있어야만 온전한 설명이 가능하며, 상대방이 어떤 질문이나 댓글을 남기더라도 답변이 가능합니다.&lt;/p&gt;
&lt;p&gt;개인적인 일화로, 고등학생 때 영어 심화반에서 공부를하며 성적도 1등급을 계속 얻어갔던 경험이 있었습니다. 단순 암기식으로 높은 점수를 받아서 심화반에도 배정되고, 영어 내신 성적도 잘 나왔었죠. 그러나 친구들에게 내신 공부가 아닌 수능 및 어학과 관련한 질문은 받으면 어버버했던 기억이 생생하네요😅 &lt;strong&gt;과거 시험에만 최적화된 방식으로 수동적인 학습했던 방식은 정작 사회에 나와보니 한계가 많다는 것을 경험했던 것 같습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;쉽게 얻은 것은 쉽게 잃는다(Easy come, easy go)는 유명한 말 있잖아요. 쉬운 길을 택할수록 쉽게 사라지는 경험을 아마 모두 해 봤을 거라고 생각합니다. 혼자 카페에 가서 코딩 강의를 시청하고, 프로그래밍 문법 책 하나를 사서 처음부터 끝까지 계속 따라쳐보는 것을 그대로 블로그에 정리하는 것은 정말 쉬운 일입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;책임감있는-열정적인-블로깅을-해보자-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B1%85%EC%9E%84%EA%B0%90%EC%9E%88%EB%8A%94-%EC%97%B4%EC%A0%95%EC%A0%81%EC%9D%B8-%EB%B8%94%EB%A1%9C%EA%B9%85%EC%9D%84-%ED%95%B4%EB%B3%B4%EC%9E%90-&quot; aria-label=&quot;책임감있는 열정적인 블로깅을 해보자  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;책임감있는, 열정적인 블로깅을 해보자 🔥&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0010a465-4cbc-4f09-9a09-3e43fa3b837c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;그런데 마치 본인이 강사가 된 것처럼 시뮬레이션을 해보고, 남을 가르치는 방식을 블로깅에서도 적용해보는것은 어떨까요?&lt;/strong&gt; 이 방식은 분명 처음에는 정말 어렵고 시간도 오래걸려서 하기가 싫어질겁니다. 저도 과거에는 싫어하는 방식이였구요.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;지금의 저처럼 구어체를 사용해서 설명해보고, 남에게 오차없이 지식을 알려주기 위해 알려주는 방식은 어렵고 도전적이라 생각이 들어요.&lt;/strong&gt; 하지만 기존의 잘못된 방식을 사용했다간 오점이 있는 정보를 다른 사람들에게 공유하고, 본인도 잘못된 정보를 학습할 수도 있는것입니다.&lt;/p&gt;
&lt;p&gt;떄문에 책임감있게 시간도 오래걸려서 공식 도큐먼트와 공신력있는 웹사이트를 참고할 수 있겠죠. 또 포스팅 내용을 보는 다른 사람들과 QA를 꾸준히 주고받으며 잘못된 내용을 지적받거나, 제가 미흡하게 알고있던 지식도 질문받으면서 다시 찾아보고 빈틈을 효과적으로 채울 수 있을겁니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;다방향으로 부딪히며 배우고 성장하는 것이 최고의 공부인 셈이고, 이런 경험들은 일방향적인 학습의 빈틈을 효과적으로 채워 줄 거라 생각합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;본인만-알아볼-수-있는-포스팅-내용은-어떨가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B3%B8%EC%9D%B8%EB%A7%8C-%EC%95%8C%EC%95%84%EB%B3%BC-%EC%88%98-%EC%9E%88%EB%8A%94-%ED%8F%AC%EC%8A%A4%ED%8C%85-%EB%82%B4%EC%9A%A9%EC%9D%80-%EC%96%B4%EB%96%A8%EA%B0%80&quot; aria-label=&quot;본인만 알아볼 수 있는 포스팅 내용은 어떨가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;본인만 알아볼 수 있는 포스팅 내용은 어떨가?🤨&lt;/h2&gt;
&lt;p&gt;안타깝게도 혼자만 이해하고 학습한 내용을 정리할 수 있는 블로깅 내용은 다른 사람들이 잘 보지 않습니다. 저도 마찬가지로 이해하기 힘든 포스팅은 바로 보지도 않고 다른 포스팅 내용들을 찾아나섭니다. 본인만 알아볼 수 있는 포스팅은 아래와 같은 단점밖에 보이지 않습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;다른 사람들로부터 피드백을 받을 수 없다. 조회수 자체가 낮기 때문에 소통이 불가능하죠.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;빠른 시간내에 포스팅은 가능하지만, 미흡한 학습효과로 인해 금방 잊어버리게 됩나다. 결국 며칠만 지나더라도 학습을 전혀 안한 상태와 똑같죠.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;책임감없는 블로깅이므로, 적당히 구글링을 해보다 학습을 완료해서 잘못된 개념을 이해해버릴 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;심한 경우, 본인도 추후에 다시 해당 포스팅을 읽어봤을 떄 이해하지 못할 수 있습니다. (경험담)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;몰론 굳이 장점을 뽑자면, 빠른 시간안에 여러 이론을 훑어보고 포스팅을 진행할 수는 있겠죠...? 하지만 빠르게 공부했다고 해서 좋은것이 절대 아니라는 것은 앞서 계속 말씀드렸던 내용입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;제-포스팅-내용을-공유합니다--과거-블로깅-vs-현재-블로깅-차이&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%9C-%ED%8F%AC%EC%8A%A4%ED%8C%85-%EB%82%B4%EC%9A%A9%EC%9D%84-%EA%B3%B5%EC%9C%A0%ED%95%A9%EB%8B%88%EB%8B%A4--%EA%B3%BC%EA%B1%B0-%EB%B8%94%EB%A1%9C%EA%B9%85-vs-%ED%98%84%EC%9E%AC-%EB%B8%94%EB%A1%9C%EA%B9%85-%EC%B0%A8%EC%9D%B4&quot; aria-label=&quot;제 포스팅 내용을 공유합니다  과거 블로깅 vs 현재 블로깅 차이 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제 포스팅 내용을 공유합니다! : 과거 블로깅 vs 현재 블로깅 차이&lt;/h2&gt;
&lt;p&gt;제가 경험했던 과거 포스팅과 최근 포스팅 내용의 차이를 한번 보셨으면 좋겠습니다.
분명 제가 적은것인데도 불구하고, 과거 포스팅은 저 조차도 몇달째 보이지 않을 뿐더러 이해가 안됩니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%9D%80%EB%8B%89&quot;&gt;과거 포스팅 : 자바 데이터 은닉&lt;/a&gt;
&lt;a href=&quot;https://velog.io/@msung99/SQL-Injection-%EB%B3%B4%EC%95%88%EC%9D%98-%ED%97%88%EC%A0%90%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%95%85%EC%9D%98%EC%A0%81%EC%9D%B8-SQL-%EA%B3%B5%EA%B2%A9%EA%B8%B0%EB%B2%95&quot;&gt;가장 최근 포스팅 : SQL Injection : 보안의 허점을 이용한 악의적인 SQL 공격기법&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;스스로에게-물어봅시다-오늘-공부한게-본인의-것이-되었는지요&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%EC%8A%A4%EB%A1%9C%EC%97%90%EA%B2%8C-%EB%AC%BC%EC%96%B4%EB%B4%85%EC%8B%9C%EB%8B%A4-%EC%98%A4%EB%8A%98-%EA%B3%B5%EB%B6%80%ED%95%9C%EA%B2%8C-%EB%B3%B8%EC%9D%B8%EC%9D%98-%EA%B2%83%EC%9D%B4-%EB%90%98%EC%97%88%EB%8A%94%EC%A7%80%EC%9A%94&quot; aria-label=&quot;스스로에게 물어봅시다 오늘 공부한게 본인의 것이 되었는지요 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스스로에게 물어봅시다. 오늘 공부한게 본인의 것이 되었는지요!&lt;/h2&gt;
&lt;p&gt;저는 항상 이 마음가짐으로 꾸준히 학습합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;오늘 한 공부가 정말 내 것이 되었니?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;라는 물음을 끊임없이 던지며, 스스로 학습하고, 오늘 학습한 내용을 정확히 인지하고, 알고 모르는지 확실히 인정하는 학습법을 실천중입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이 포스팅을 보시는 분들도 모두 블로깅에 더 책임감을 가지고 열심히 학습하셨으면 하는 바람입니다. 책임감있는 블로깅일수록 더 능동적으로 찾아보고, 응용하고, 재밌게 학습할 수 있는 방법이 되기 떄문이죠! 꼭 도움이 되셨으면 합니다 🙂&lt;/p&gt;</content:encoded></item><item><title><![CDATA[SQL Injection 공격기법과 유형에 대해 알아보자!]]></title><description><![CDATA[시작에 앞서 웹 해킹의 다양한 공략법 및 구조가 존재하며, 보안이 취약한 웹 페이지들은 악의적인 해커들로 인해 정보가 유출될 수 있습니다. 다양한 공격방법 중에 가장 쉬운 공격이면서, 취약점으로 손꼽힐 수 있는 SQL Injection…]]></description><link>https://haon.site/haon/server/sql-injection/</link><guid isPermaLink="false">https://haon.site/haon/server/sql-injection/</guid><pubDate>Thu, 29 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;시작에-앞서&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%9E%91%EC%97%90-%EC%95%9E%EC%84%9C&quot; aria-label=&quot;시작에 앞서 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시작에 앞서&lt;/h2&gt;
&lt;p&gt;웹 해킹의 다양한 공략법 및 구조가 존재하며, 보안이 취약한 웹 페이지들은 악의적인 해커들로 인해 정보가 유출될 수 있습니다. 다양한 공격방법 중에 가장 쉬운 공격이면서, 취약점으로 손꼽힐 수 있는 SQL Injection 에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;원리부터 공격기법의 다양한 종류까지 자세히 살펴볼테니 꼼꼼히 읽어보시고 본인의 것으로 만드시면 좋겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;sql-은-왜-사용할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sql-%EC%9D%80-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;sql 은 왜 사용할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SQL 은 왜 사용할까?&lt;/h2&gt;
&lt;p&gt;우선 기초적인 SQL의 특징에 대해 간단히 알아보겠습니다. 왜 개발할 때 SQL 을 사용해야 할까요?&lt;/p&gt;
&lt;p&gt;웹 사이트를 운영하는데 있어 대용량의 데이터에 대한 관리 및 처리가 요구될 겁니다.
이런 데이터들을 단순히 텍스트 파일로 저장하거나, 파일로 간단하게만 관리한다면 양이 어마어마 하기 때문에 원하는 데이터에 대한 조회,삭제,수정등의 처리가 곤란해지기 때문에 사용하겠죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;sql-injection&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sql-injection&quot; aria-label=&quot;sql injection permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SQL Injection&lt;/h2&gt;
&lt;p&gt;그럼 본격적으로 SQL Injection 에 대해 알아봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/fefb8d99-3535-4c66-ba50-9477849884a4/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;우선 Injection 의 뜻은 &quot;삽입한다&quot; 는 뜻입니다. 즉, &lt;strong&gt;개발자가 만들어놓은 SQL 쿼리 문에 서비스 사용자의 데이터 입력값이 삽입된 후 악의적으로 활용되는 기법입니다.&lt;/strong&gt; 이때 서비스 사용자란 정상적인 사용자가 아닌, 해커들이 악의적으로 넣는 입력 데이터입니다.&lt;/p&gt;
&lt;p&gt;서비스 사용자의 입력값이 서버측에서 코드로 입력되어 실행되는 &lt;strong&gt;코드 인젝션 공격 기법&lt;/strong&gt; 중 하나입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;sql injection 은 말 그대로 웹 사이트의 보안상 허점을 이용해 특정 sql 쿼리문을 전송하여 공격자(해커)가 원하는 데이터베이스의 중요한 정보를 가져오는 해킹 기법을 말합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;sql-injection-의-원리와-동작과정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sql-injection-%EC%9D%98-%EC%9B%90%EB%A6%AC%EC%99%80-%EB%8F%99%EC%9E%91%EA%B3%BC%EC%A0%95&quot; aria-label=&quot;sql injection 의 원리와 동작과정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SQL Injection 의 원리와 동작과정&lt;/h2&gt;
&lt;p&gt;자, 예를들어 아래와 같이 관리자 페이지가 따로 있다고 해보죠. 이 관리자 페이지에 접속하기 이전에, 현재 유저가 관리자 권한이 있는지를 검증해야 하는 로직이 필요할겁니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/12134a04-97f3-41b5-b078-54a27876fc56/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;검증을 위한 로직은 아래와 같이 select 문을 작성할 수 있을겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; auth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;admin&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;msung&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;더 정확히 말하자면, 위 SQL 구문은 권한(auth 필드)이 관리자(admin) 이면서 아이디(id 필드) 가 msung 인 유저를 추출해내는 것입니다.
이때 외부로 부터 입력받은 데이터는 &apos;admin&apos; 과 &apos;msung&apos; 입니다.&lt;/p&gt;
&lt;p&gt;그런데 만일 외부 입력으로 &quot;im not admin&quot; 과 &quot; &apos;OR &apos;1&apos; = &apos;1 &quot; 을 입력받으면 어떻게 될까요? 즉 아래와 같은 SQL 문이 실행되는 상황을 가정해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; auth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;im not admin&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos; &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;1&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;1&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;아이디가 스페이스 이거나(id = &apos; &apos;), 또는 1이 1과 같은 경우 (&apos;1&apos; = &apos;1&apos;) 라고 WHERE 조건을 걸어버린다면 무조건 참이 될겁니다.&lt;/p&gt;
&lt;p&gt;즉 외부 입력으로 그 어떠한 auth 를 입력받고, id 입력을 받아오는지와 상관없이 &apos;1&apos; = &apos;1&apos; 라는 구문에 의해서 해커는 관리자 계정의 User 테이블의 모든 필드 정보를 얻을 수 있습니다.&lt;/p&gt;
&lt;p&gt;cf) AND 연산자의 우선순위가 OR 연산자보다 높다는 것에 유념하세요.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;해커의-악의적인-활용-사례&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B4%EC%BB%A4%EC%9D%98-%EC%95%85%EC%9D%98%EC%A0%81%EC%9D%B8-%ED%99%9C%EC%9A%A9-%EC%82%AC%EB%A1%80&quot; aria-label=&quot;해커의 악의적인 활용 사례 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;해커의 악의적인 활용 사례&lt;/h2&gt;
&lt;p&gt;또한 최악의 경우, 아래와 같은 SQL Injection 도 활용해서 모든 유저의 정보를 DB 에서 삭제시켜 버릴 수도 있습니다.&lt;/p&gt;
&lt;p&gt;외부 입력으로 &quot;admin&quot; 과 &quot; &apos;; DROP TABLE users;--&apos; &quot; 를 받아오는 경우입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; auth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;admin&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos; &apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DROP&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; Users&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;--&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;조금 더 보시기 편하게 아래처럼 2줄로 나눠봤습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; auth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;admin&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos; &apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;DROP&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;--&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같은 문자열을 파라미터로 해커를 통해 받는다면, 서비스에 존재하는 모든 유저에 대한 데이터가 날라가 버릴 수 있습니다.&lt;/p&gt;
&lt;p&gt;첫번째 세미콜론을 통해 SQL문을 종료해버리고, 새로운 DROP 구문을 통해 삭제 요청을 보내서 삭제시킨 후에 뒤에 - 2개로 기존의 SQL 문을 주석처리 해버리는 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;sql-injection-의-다양한-공격기법-종류&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sql-injection-%EC%9D%98-%EB%8B%A4%EC%96%91%ED%95%9C-%EA%B3%B5%EA%B2%A9%EA%B8%B0%EB%B2%95-%EC%A2%85%EB%A5%98&quot; aria-label=&quot;sql injection 의 다양한 공격기법 종류 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SQL Injection 의 다양한 공격기법 종류&lt;/h2&gt;
&lt;p&gt;이렇게 단순하면서도 취약한 SQL Injection 의 공격기법은 다양합니다. 그들에 대해 자세히 파해쳐보죠!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1-classic-sql-injection&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-classic-sql-injection&quot; aria-label=&quot;1 classic sql injection permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Classic SQL Injection&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;말 그대로 가장 근본적이며, 기초적인 SQL Injection 공격 기법을 의미합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;앞서 살펴본 내용들이 모두 이 공격기법에 해당합니다. SQL Injection 중에서 의 가장 일반적인 공격 방법이며, 공격자가 동일한 통신구간(ex. 브라우저) 에서 공격을 시도하고 결과를 얻을 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;참고-추가적인-공격방법-예시&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0-%EC%B6%94%EA%B0%80%EC%A0%81%EC%9D%B8-%EA%B3%B5%EA%B2%A9%EB%B0%A9%EB%B2%95-%EC%98%88%EC%8B%9C&quot; aria-label=&quot;참고 추가적인 공격방법 예시 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고) 추가적인 공격방법 예시&lt;/h3&gt;
&lt;p&gt;추가적으로 이 공격기법에 대한 다양한 SQL Injection 구문을 아래와 같이 적어놓겠습니다. 왜 공격에 취약한 구문인지 깊게 생각해보시고, 이해가 안가시다면 댓글로 물어보신다면 알려드리겠습니다!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;msung&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;1 or &apos;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos; = 1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;아래 예시는 외부 입력으로 &quot;random_input OR 1=1 --&quot; 과 &quot;random_password&quot; 를 입력받은 경우입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;구문에서 &quot;--&quot; 를 사용한 것은 주석입니다. -- 뒤에 오는 쿼리문의 문자열들 내용들을 모두 주석처리 시킨것이죠!&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;random_input&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;-- &apos;AND password = &apos;random_password&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2-union-based-sql-injection&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-union-based-sql-injection&quot; aria-label=&quot;2 union based sql injection permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. UNION based SQL Injection&lt;/h2&gt;
&lt;p&gt;SQL 을 공부하셨다면, UNION 명령어에 대해서도 잘 아실것이라 생각합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;UNION 이란? : 여러개의 SQL 문을 합쳐 한번에 실행될 수 있는 SQL 문으로 만들어주는 것&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;즉, UNION 은 2개 또는 그 이상의 SELECT 문을 합쳐서 하나의 결과로 출력합니다. 이 UNION 의 특징을 활용하변 해커는 외부입력으로 UNION 과 추가적인 SELECT 문을 삽입해서 원하는 정보를 쏙 빼놓을 수 있게됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a4cfa609-6489-4617-9fa0-bca9de58a1f5/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4&gt;SQL 쿼리문에 보내기&lt;/h4&gt;
&lt;p&gt;아래 예시는 외부 입력으로 &quot;im not admin UNION select 1,1 --&quot; 과 &quot;hacker&quot; 를 보낸 경우입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; auth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;im not admin&apos;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;UNION&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;-- and id = &apos;hacker&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그 어떤 id를 입력받더라도 주석처리가 되므로 검증 처리 대상에서 무시됩닉다.
또한 select 1,1 을 통해 항상 참이 되는 경우를 만들었기 때문에, 모든 유저의 정보를 탈취할 수 있는 SQL 구문이 된 것입니다.&lt;/p&gt;
&lt;h4&gt;URL 의 PathVariable 에 UNION 과 SQL 문을 보내는 경우&lt;/h4&gt;
&lt;p&gt;또 아래와 같이 특정 URL 에 GET 요청을 보내는 경우를 생각해봅시다.
외부 입력으로 &quot;1 UNION SELECT 1 FROM User&quot; 를 보낸 경우입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;http://mytest.com/getProduct/userIdx=1 UNION SELECT 1 FROM User&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3-error-based-sql-injection&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-error-based-sql-injection&quot; aria-label=&quot;3 error based sql injection permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Error-based SQL Injection&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;해커가 의도적으로 잘못된 SQL 쿼리문을 DB에 요청해서 서버로부터 리턴받은 에러 메시지를 통해 DB의 정보를 파악하는 공격기법&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/730eff2b-aa52-4c32-af1e-5bc26ad3481c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;출처 : &lt;a href=&quot;https://www.bugbountyclub.com/pentestgym/view/53&quot;&gt;펜테스트짐&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;만일 문법적으로 허용되지 않는 오류가 있는 SQL 쿼리문을 DB 에 요청한다면, &lt;strong&gt;DB 는 실행하지 못하고 왜 틀렸는지 알려주는 오류 구문를 리턴해줍니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;개발자를 이 오류를 방치한다면 오류 메시지를 일반 사용자도 쉽게 확인 가능하며, 이 메시지를 해커가 악용해서 공격에 필요한 정보로 활용할 수도 있습니다.&lt;/p&gt;
&lt;p&gt;때로는 오류 메시지가 웹브라우저에는 출력이 안되더라도 HTML 소스코드의 주석에서 나타날 수도 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;blind-injectionboolean-based-blind-sql-injection&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#blind-injectionboolean-based-blind-sql-injection&quot; aria-label=&quot;blind injectionboolean based blind sql injection permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Blind Injection(Boolean Based Blind SQL Injection)&lt;/h2&gt;
&lt;p&gt;앞서 살펴본 SQL Injection 으로 공격당하는 상황 이외에, 외부 입력 쿼리에 대한 참, 거짓으로만 서버의 데이터를 일부 추출해내는 공격 기법입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;외부 입력으로 쿼리르 삽입하고, 해당 쿼리에 대한 참과 거짓 여부를 서버로 부터 리턴받고 DB의 내용을 추측해내는 공격기법&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;앞서 살펴본 내용은 원하는 데이터를 한방에 얻어내는 기법이였다면, 현 기법은 참과 거짓에 대한 서버의 반응을 살펴보고 정보를 추측해내는 기법이라고 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;이 기법은 &lt;strong&gt;수많은 질의(QA)를 서버에 전송하고 그들을 토대로 결과를 조합하여 원하는 정보들을 얻어내면서 공격&lt;/strong&gt;을 하는 방식입니다. 이 방식은 수많은 시도를 외부입력으로 보내면서 DB의 정보를 추측해내야 하기 때문에, 해커들은 공격을 시도할때 &lt;strong&gt;자동화된 툴을 사용&lt;/strong&gt;하여 공격을 계속 시도합니다.&lt;/p&gt;
&lt;h4&gt;로그인 시도시 Blind SQL Injection 이 적용되는 사례&lt;/h4&gt;
&lt;p&gt;예를들어 아래와 같은 로그인 창에서 SQL 문을 입력하는 상황을 가정해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0bd918fc-d405-48cc-a845-a69dc261483d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; users &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; id&lt;span class=&quot;token string&quot;&gt;&apos;1&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; users &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; id&lt;span class=&quot;token string&quot;&gt;&apos;1&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;왜 1=1 을 넣었을때는 유저가 존재하고, 1=2을 넣었을때는 유저가 존재하지 않는다고 리턴될까요?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1=2을 넣은 경우
=&gt; 이 경우는 1과 2가 다르기 때문에 무조건 결과값이 거짓이 나옵니다. 따라서 해당 유저는 없다는 응답값을 리턴받게 되는 것이죠.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1=1을 넣은 경우
=&gt; 이 경우는 1과 1이 같기 때문에 AND의 두 조건중 하나는 무조건 참인 것입니다. 나머지 조건인 id 값이 1이 존재하는지 여부를 검증하는 로직만 참이 성립한다면, 해커는 id 값이 1인 유저가 존재한다고 판단할 수 있게 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;이렇게 직접적으로 DB의 정보를 알수없더라도 참 거짓 응답값을 리턴받고 정보를 계속 추측해나가는 방식을 Blind SQL Injection 이라고 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금까지 SQL Injection 이란 무엇인지 알아봤습니다. 저는 최근에 이 내용을 학습할 때 꽤 어려웠던 내용같았는데, 이 포스팅을 참고하시는 분들은 꼭 자세히 읽어보시고 도움이 되셨으면합니다.&lt;/p&gt;
&lt;p&gt;또 각 공격기법에 대한 해결&amp;#x26;대응방안이 있는데, 이는 조만간 새로운 포스팅과 함께 내용을 구성해보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/sql/relational-databases/security/sql-injection?view=sql-server-ver16&quot;&gt;MicroSoft SQL Injection : Documentation&lt;/a&gt;
&lt;a href=&quot;https://www.bugbountyclub.com/pentestgym/view/52&quot;&gt;https://www.bugbountyclub.com/pentestgym/view/52&lt;/a&gt;
&lt;a href=&quot;https://portswigger.net/web-security/sql-injection&quot;&gt;https://portswigger.net/web-security/sql-injection&lt;/a&gt;
&lt;a href=&quot;https://m.blog.naver.com/lstarrlodyl/221837243294&quot;&gt;https://m.blog.naver.com/lstarrlodyl/221837243294&lt;/a&gt;
&lt;a href=&quot;https://gomguk.tistory.com/118&quot;&gt;https://gomguk.tistory.com/118&lt;/a&gt;
&lt;a href=&quot;https://hyjykelly.tistory.com/5&quot;&gt;https://hyjykelly.tistory.com/5&lt;/a&gt;
&lt;a href=&quot;https://dpdpwl.tistory.com/98&quot;&gt;https://dpdpwl.tistory.com/98&lt;/a&gt;
&lt;a href=&quot;https://hyotwo.tistory.com/70&quot;&gt;https://hyotwo.tistory.com/70&lt;/a&gt;
&lt;a href=&quot;https://www.youtube.com/watch?v=OUGrSB0CAxs&quot;&gt;https://www.youtube.com/watch?v=OUGrSB0CAxs&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Cors, Sop 에 대해 알아보자!]]></title><description><![CDATA[웹 개발을 하다보면 cors 라는 빨간 에러메시지를 접하게 되는 경우가 많습니다.
이는 보통 클라이언트와 서버 프로젝트가 분리되어 개발되고 서로 RestAPI…]]></description><link>https://haon.site/haon/server/cors/</link><guid isPermaLink="false">https://haon.site/haon/server/cors/</guid><pubDate>Mon, 26 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;웹 개발을 하다보면 cors 라는 빨간 에러메시지를 접하게 되는 경우가 많습니다.
이는 보통 클라이언트와 서버 프로젝트가 분리되어 개발되고 서로 RestAPI 를 통해 통신할 때 자주 접하는 에러로 많은 분들이 혼동을 겪게 되는 부분입니다.&lt;/p&gt;
&lt;p&gt;그러나, 이렇게 우리에게 고통을 주는 CORS 는 알고보면 오히려 클라이언트와 서버가 원활하게 통신할 수 있도록 돕고 있다는 반전이 있습니다.&lt;/p&gt;
&lt;p&gt;이번 포스팅에서는 CORS, POS 란 무엇인지 알아보며, 이들에 대한 접근제어 시나리오(흐름 순서)에 대해 자세하게 알아보겠습니다. 또한 CORS 에 대한 에러를 처리하기 위한 방법들도 자세히 파해쳐봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;cors-에러를-언제-어떻게-접하게-될까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cors-%EC%97%90%EB%9F%AC%EB%A5%BC-%EC%96%B8%EC%A0%9C-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%A0%91%ED%95%98%EA%B2%8C-%EB%90%A0%EA%B9%8C&quot; aria-label=&quot;cors 에러를 언제 어떻게 접하게 될까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CORS 에러를 언제, 어떻게 접하게 될까?&lt;/h2&gt;
&lt;p&gt;이 에러는 &lt;strong&gt;한 사이트에서 주소가 다른 서버로 요청을 보낼 때&lt;/strong&gt; 자주 접하게되는 오류입니다.&lt;/p&gt;
&lt;p&gt;주소가 &quot;naver.com&quot; 이라는 웹 사이트에서 &quot;google_search.com&quot; 인 서비스에 API 정보를 받아오기 위해 프론트에서 HTTP 요청을 보냈을 때, 미리 어떤 설정을 해주지 않는다면 아래와 같은 CORS 에러 메시지를 접하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/b54f7f6e-2096-497b-a158-0303b948d602/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;우리는 이 에러를 정확히 이해하고 해결하는 방법을 알기 위해선, 우선 출처(Origin) 란 무엇이고, SOP 라는것이 무언인지를 먼저 알아야합니다. 곧 바로 무엇인지 알아보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;출처origin-이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B6%9C%EC%B2%98origin-%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;출처origin 이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;출처(Origin) 이란?&lt;/h2&gt;
&lt;p&gt;우리가 어떤 사이트를 접속할 때 인터넷 주소창에다 URL 이라는 문자열을 입력하고, 접근하게 됩니다.
이떄 URL 의 구성성분에 대해 더 자세히 알아보겠습니다.
출처(origin) 이라는 것은 이 URL 이라는 단위에 포함되어 있는 더 작은 구성성분 이기 때문입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7bd48930-788e-46b4-9a6b-b8f2fc8e9693/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;URL은 크게 6가지의 구성단위로 구분 및 구성된다고 볼 수 있습니다. 이때 핵심적인 부분은 바로 프로토콜, 호스트, 포트인데 이 3가지로 구성된 것이 바로 출처(origin) 입니다.&lt;/p&gt;
&lt;p&gt;정리해보자면 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;출처(Origin) : Protocol + Host + Port&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;즉 출처란 프로토콜, 호스트, 그리고 포트 3가지를 합친 URL 을 의미한다!&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;sopsame-origin-policy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sopsame-origin-policy&quot; aria-label=&quot;sopsame origin policy permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SOP(Same-Origin Policy)&lt;/h2&gt;
&lt;p&gt;출처(origin) 라는 개념을 이해하셨다면 바로 SOP 가 무엇인지를 알아봅시다.
SOP란 &lt;strong&gt;다른 출처의 리소스를 사용하는 것을 제한하는 보안 정책&lt;/strong&gt;을 의미합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;동일한 출처에서만 리소스를 공유할 수 있도록하는 법률 정책&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;앞서 살펴봤던 origin(출처)의 구성성분인 프로토콜, 호스트, 포트를 통해 같은 출처인지 다른 출처인지를 판단할 수 있습니다. 즉 이 3가지 중에 하나라도 다르다면 다른 출처라고 판단하는 것이며, 반대로 이 3가지가 모두 같아야지만 같은 출처(Same Origin) 이라고 말하는 것입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;동일 출처(same origin) 서버에 있는 리소스는 자유롭게 가져올 수 있지만, 다른 출처(Cross origin) 서버에 있는 이미지나 데이터 같은 리소스는 상호작용이 불가능하다는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;sop-는-왜-필요한가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sop-%EB%8A%94-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%9C%EA%B0%80&quot; aria-label=&quot;sop 는 왜 필요한가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SOP 는 왜 필요한가?&lt;/h2&gt;
&lt;p&gt;예를들어 페이스북을 사용하는 유저(클라이언트)가 있다고 해봅시다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;요약&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;어떤 요청에 대한 출처(origin) 가 동일한 출처(same-origin)로 부터 왔다면 SOP 정책을 지켰다고 판단하고 해당 요청을 수락한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;해커가 심은 홈페이지 url 과 같은 다른 출처(cross-origin) 으로 부터 왔다면, SOP 정책에 위반한 것이라 판단하고 해당 요청을 거부하고 에러 메시지를 터뜨린다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;이 클라이언트가 페이스북 사이트에 원하는 리소스를 제공받기 위해 요청을 보낼겁니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a61d93a9-0b65-4c00-8aae-fad89c8cc1a0/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;
&lt;p&gt;이떄 유저는 페이스북 서비스에 로그인을 시도하고, 페이스북에서 인증(Authentication) 토큰을 받아옵니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;사용자는 발급받은 인증 토큰을 페이스북에 매번 요청을 보낼때마다 실어서 보낼겁니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그러던 중, 해커는 해당 사용자가 혼동할만한, 진짜 흥미롭게 생긴 내용이 실린 링크를 사용자에게 악의를 품고 메일을 통해서 보냅니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;만일 사용자가 해커가 보낸 해당 링크에 유혹되버려서 클릭을 해버린다면, 정상적인 페이스북 주소 &lt;a href=&quot;http://www.facebook.com&quot;&gt;www.facebook.com&lt;/a&gt; 가 아닌 해커가 만든 악의적인 주소 &lt;a href=&quot;http://www.hacker.com&quot;&gt;www.hacker.com&lt;/a&gt; 으로 이동해버리게 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;즉, 해커가 만든 사이트의 HTML, CSS, JS 가 사용자의 브라우저가 다운로드가 되고 실행되면서, 사용자는 의도치않게 해커가 만든 주소에다 토큰 정보를 요청을 보내버릴 수도 있으며, 의도하지 않게 금융정보나 개인정보가 털리게 될 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;이렇게 될때, 여기서 &lt;strong&gt;SOP 가 의도치 않은 악의적인 URL 로 사용자의 원하는 정보를 보내지 않도록 브라우저에서 막아버릴 수 있게 합니다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;7&quot;&gt;
&lt;li&gt;
&lt;p&gt;그렇다면 페이스북 입장에서는, 이 요청이 어디로부터 온가지? 라고 &lt;strong&gt;요청이 들어온 출처(origin)을 확인&lt;/strong&gt;을 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이 요청을 확인해보니 &lt;a href=&quot;http://www.hacker.com&quot;&gt;www.hacker.com&lt;/a&gt; 으로 부터 온 것입니다. &lt;strong&gt;그러면 페이스북 입장에서는 자기 출처와 다른 것이니 요청을 받은 Origin (출처)는 다른 것이라! 라고 인식합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;즉, &lt;strong&gt;Cross Origin 으로 판단하기 때문에 SOP 에 위반된다라고 판단하고 해당 요청을 거부합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;cors--다른-출처에-대한-리소스가-필요한-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cors--%EB%8B%A4%EB%A5%B8-%EC%B6%9C%EC%B2%98%EC%97%90-%EB%8C%80%ED%95%9C-%EB%A6%AC%EC%86%8C%EC%8A%A4%EA%B0%80-%ED%95%84%EC%9A%94%ED%95%9C-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;cors  다른 출처에 대한 리소스가 필요한 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CORS : 다른 출처에 대한 리소스가 필요한 경우&lt;/h2&gt;
&lt;p&gt;그렇다면 다른 출처에서 원하는 리소스에 접근하는 행위는 SOP 에 위반되는 것인데, 다른 출처에서 접근할 수 있는 방법은 없을까요?&lt;/p&gt;
&lt;p&gt;백엔드에서 개발한 API 와 리소스들을 프론트에서 접근할때 다른 출처(cross-origin) 라고해서 접근이 아예 안된다면, 이건 답이 없을겁니다.&lt;/p&gt;
&lt;p&gt;이에 대한 정답은 CORS 에 있습니다. 초반부에 설명드렸듯이 CORS 는 오히려 우리를 돕고 있다는 사실이 있다고 했었죠?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;CORS 란? : 다른 출처의 자원을 공유하는 방식&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;저희가 다른 출처(cross-origin) 에서 원하는 리소스에 접근하는 것은 원칙적으로는 SOP 에 위반되지만, 일부적으로 특정 출처에 대해서만 개방을 해서 리소스에 접근 가능하도록 하는 방식입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By MOZILLA 인용&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS) 는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;시빨간-cors-에러-메시지는-무슨-의미였는가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EB%B9%A8%EA%B0%84-cors-%EC%97%90%EB%9F%AC-%EB%A9%94%EC%8B%9C%EC%A7%80%EB%8A%94-%EB%AC%B4%EC%8A%A8-%EC%9D%98%EB%AF%B8%EC%98%80%EB%8A%94%EA%B0%80&quot; aria-label=&quot;시빨간 cors 에러 메시지는 무슨 의미였는가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시빨간 CORS 에러 메시지는 무슨 의미였는가?&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7ed57e48-49b5-4ece-af22-44805a37c285/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;CORS 에 위반되는 출처이니까 이러한 에러 메시지가 뜨는 것이겠죠?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;즉, 이 메시지는 CORS 방식에 따라 허용된 다른 출처(cross-origin) 들이 있을텐데, 이렇게 &lt;strong&gt;허용된 다른 출처(cross-origin)들 중에 해당 출처가 해당되지 않아서 SOP 를 위반했다고 뜨는 에러 메시지입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;cors-접근제어-시나리오&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cors-%EC%A0%91%EA%B7%BC%EC%A0%9C%EC%96%B4-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4&quot; aria-label=&quot;cors 접근제어 시나리오 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CORS 접근제어 시나리오&lt;/h2&gt;
&lt;p&gt;이제 CORS 에 대해 더 자세히 파해쳐봅시다. CORS 접근제어 시나리오 에는 총 3가지가 존재합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;단순요청(Simple Request)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;프리플라이트 요청(Preflight Request)&lt;/li&gt;
&lt;li&gt;인증정보 포함 요청(Credentialed Request)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;preflight-request&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#preflight-request&quot; aria-label=&quot;preflight request permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Preflight Request&lt;/h2&gt;
&lt;p&gt;쉽게 말해, 사전확인 작업입니다.&lt;/p&gt;
&lt;p&gt;예를들어 친구집에 놀러가능 경우를 생각해봅시다. 친구에게 너희집 놀러가도 되니? 물어보는 것이고, 친구는 응답으로 어 놀러와! 또는 놀러오지마! 라고 해주는 것입니다.
그 친구의 응답에 따라서 본인은 놀러갈지 안놀러갈지 정하는 방식입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Preflight 는 본격적인 본 요청을 보내기전에 일단 서버한테 물어보는 것입니다.&lt;/strong&gt; 서버는 요청에 대해 요청 보내도 된다~ 또는 안된다 라고 응답해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;preflight-request-과정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#preflight-request-%EA%B3%BC%EC%A0%95&quot; aria-label=&quot;preflight request 과정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Preflight Request 과정&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1f00b5e1-2b9b-4620-b9ec-6699731c1878/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;클라이언트에서 OPTIONS 메소드를 통해 다른 도메인의 리소스에 요청이 가능한지 확인 작업을 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;요청이 가능하다면 클라이언트는 실제 요청(Actual Request) 을 보냅니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;앞서 친구집 놀러가는 예시와 동일한 원리입니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Preflight Request : 본 요청(실제 요청. actual request) 를 보내기전에, 일단 서버에게 OPTIONS 메소드를 통해 물어보는 것입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;또한 이렇게 사전질문을 하는 것을 OPTIONS 를 통해서 물어볼 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Preflight Response : 사전질문에 대해 서버에서 응답을 해줍니다. 즉, 서버는 그 요청을 받아들일 수 있다고 클라이언트에게 의사를 전달하는 것이죠.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;만일 클라이언트가 보낸 요청이 거부된다면 클라이언트에서 Actual Request 는 실제로 보내지는 않습니다.&lt;/strong&gt; 그런데 보내도 된다고 이야기하면 실제로 보내지는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Actual Request + Actual Response : 본 요청과 응답입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;preflight-request-포맷&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#preflight-request-%ED%8F%AC%EB%A7%B7&quot; aria-label=&quot;preflight request 포맷 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Preflight Request 포맷&lt;/h2&gt;
&lt;p&gt;사전 요청(Preflight Request) 를 할때 물어볼 것들이 있습니다. 그리고 그것에 맞는 포맷이 있다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/8e4e7803-7c61-43fa-9b49-1f7e6391b1ce/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Origin : Header 에는 Origin 이 들어있어야한다. 즉, 이 요청은 어디로부터 날라온 것이야! 라고 출처를 표현을 해주는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Access-Control-Request-Method : 실제 요청의 메소드, 즉 나는 이 메소드를 보낼건데, 이 메소드를 보내도 되겠니? 라고 물어볼 정보를 담는 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Access-Control-Request-Headers : 실제 요청의 추가 헤더로 어떤 것들을 더 보낼 수 있는지라고 물어보는 요청입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;preflight-response-포맷&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#preflight-response-%ED%8F%AC%EB%A7%B7&quot; aria-label=&quot;preflight response 포맷 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Preflight Response 포맷&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/252139da-7930-46be-a426-56975d8fa489/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Access-Control-Allow-Origin : 서버측에서는 이 Origin 은 허가가 되어있어! 라고 응답해주는 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Access-Control-Allow-Methods : 서버 측에서는 이러한 메소드들이 허가가 되어있어! 라는 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Access-Control-Allow-Headers : 서버 측에서는 이러한 헤더들이 허가가 되어있어! 라는 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Access-Control-Max-Age : Prelflight 응답 캐시 기간을 의미합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;캐시-기간이란-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BA%90%EC%8B%9C-%EA%B8%B0%EA%B0%84%EC%9D%B4%EB%9E%80-&quot; aria-label=&quot;캐시 기간이란  permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;캐시 기간이란? 🧐&lt;/h3&gt;
&lt;p&gt;Preflight 요청을 보내게되면 실질적으로 2번 요청(사전 요청, 실제 요청)이 보내질텐데, 이렇게 되면 매번 하나의 요청을 보낼때마다 2번의 요청이 왔다갔다 한다는 것이겠죠?&lt;/p&gt;
&lt;p&gt;이러면 굉장히 리소스 측면에서 좋지는 않을겁니다. 그렇기 떄문에 이 Preflight 응답에 대해서 브라우저는 캐싱을 해두고, 다음 똑같은 요청을 보낼때 Preflight 캐싱된것을 확인하고 Preflight 사전 요청을 미리 보내지 않고, 바로 본 요청을 보내게 되는 것입니다.&lt;/p&gt;
&lt;p&gt;그래서 Access-Control-Max-Age 를 설정하면 위 예제의 경우 86400초를 캐싱(저장)을 해두는 명령이 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;simple-request-란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#simple-request-%EB%9E%80&quot; aria-label=&quot;simple request 란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Simple Request 란?&lt;/h2&gt;
&lt;p&gt;앞서 알아본 Preflight Request 에서 사전 요청&amp;#x26;응답 과정을 제거하고 &lt;strong&gt;본 요청&amp;#x26;응답 (Actual Request + Actual Response) 만 남긴것&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Prelight 과 다르게, Prelight 요청(사전요청) 없이 바로 요청을 날리는 절차&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;simple-request-조건&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#simple-request-%EC%A1%B0%EA%B1%B4&quot; aria-label=&quot;simple request 조건 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Simple Request 조건&lt;/h2&gt;
&lt;p&gt;Simple Request 를 날리려면 다음 조건들을 만족해야 합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;GET, POST, HEAD 메소드 중에 하나여야한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Content-Type 은 다음 3가지중에 하나여야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;application/x-www-formp-urlencoded&lt;/li&gt;
&lt;li&gt;multipart/form-data&lt;/li&gt;
&lt;li&gt;text/plain&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;헤더는 Accept, Accept-Language, Content-Language, Content-Type 만 허용된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;simple-request-과정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#simple-request-%EA%B3%BC%EC%A0%95&quot; aria-label=&quot;simple request 과정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Simple Request 과정&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/46e52211-4a31-404d-9203-89d508a1a7e0/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;클라이언트가 Server-b.com 라는 출처(origin) 에서 보내는 것이라고 보냅니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;서버에서는 전달받은 Origin 을 확인합니다. 그런데 위 예제의 경우 허용된 출처가 와일드카드(별표 *) 로 명시되어 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;와일드카드는 모든 출처에 대해 허용하겠다는 의미입니다. 따라서 전달받은 해당 출처에 대해 리소스를 제공하도록 허용해줍니다. (정상적인 응답을 해줍니다)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그런데 만일 Access-Control-Allow-Origin 이 별표 * 가 아니라 foo.hi 만 명시되어 있는 경우, Origin 이 foo.example 인 곳에서 요청을 보내면 Cross-Origin 에러가 터질겁니다.&lt;/p&gt;
&lt;p&gt;이 점을 기억하고 계시길 바라며, 이에 대한 내용은 바로 아래에서 계속 이어서 설명하겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;preflight-request-는-왜-필요할까-언제쓰이지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#preflight-request-%EB%8A%94-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%A0%EA%B9%8C-%EC%96%B8%EC%A0%9C%EC%93%B0%EC%9D%B4%EC%A7%80&quot; aria-label=&quot;preflight request 는 왜 필요할까 언제쓰이지 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Preflight Request 는 왜 필요할까? 언제쓰이지?&lt;/h2&gt;
&lt;p&gt;위와 같은 의문이 당연히 생길 수 있습니다.
Simple Request 를 보내면 그냥 딱 한번 요청을 보내고 끝나는 간단한 방식일텐데, 왜 Preflight 로 굳이 2번 왔다갔다를 해야할까요?&lt;/p&gt;
&lt;p&gt;이 정답은 &lt;strong&gt;CORS를 모르는 출처를 위해서입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/74b7d737-6d01-4902-8793-6097dc2f8707/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위 그림은 서버가 CORS 를 모르는 서버, 즉 CORS 에 관해 아무런 설정도 없는 서버입니다.&lt;/p&gt;
&lt;p&gt;클라이언트에서 Actual Request, 즉 Preflight 요청 없이 바로 본 요청을 보낼 때 어떤 오류가 생기는 것인지 보는 것입니다.&lt;/p&gt;
&lt;h3 id=&quot;단순한-get-post-요청을-보냈다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%88%9C%ED%95%9C-get-post-%EC%9A%94%EC%B2%AD%EC%9D%84-%EB%B3%B4%EB%83%88%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;단순한 get post 요청을 보냈다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단순한 GET, POST 요청을 보냈다면?&lt;/h3&gt;
&lt;p&gt;만일 단순한 GET 요청을 클라이언트가 보낸경우, 서버는 브라우저를 통해 받은 Origin 을 확인하는데, 해당 서버는 별도의 CORS 를 설정해주지 않았기 때문에 클라이언트에게 CORS 에러메시지를 던져주면 끝입니다.&lt;/p&gt;
&lt;h3 id=&quot;중요한-요청--put-delete-요청을-보냈다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A4%91%EC%9A%94%ED%95%9C-%EC%9A%94%EC%B2%AD--put-delete-%EC%9A%94%EC%B2%AD%EC%9D%84-%EB%B3%B4%EB%83%88%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;중요한 요청  put delete 요청을 보냈다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;중요한 요청 : PUT, DELETE 요청을 보냈다면?&lt;/h3&gt;
&lt;p&gt;그러나 DELETE 요청을 보낸다면, &lt;strong&gt;서버는 DB에 있는 소중한 데이터를 삭제하고 나서 CORS 에러 메시지를 리턴하는 방식입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이러면 CORS 에러 메시지를 내뱉어서 겉보기에는 DB에 데이터가 삭제되는 현상이 방지된것 처럼 보여도, &lt;strong&gt;실제로는 DB의 데이터가 삭제되고 CORS 메시지가 터진 것이라서 문제가 발생한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/f7a356a5-ee3c-4fc7-a05c-e31e371583b4/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이 떄문에 Preflight 가 필요한것입니다. Prelight 요청을 통해 사전에 요청을 보내고 받은 Response 로 CORS 에러가 터졌다고 메시지를 받으면 클라이언트는 Actual Request 를 보내지 않습니다. 이에따라 서버는 DB 에 있는 데이터들을 안전하게 보관할 수 있는 것이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;즉, Prelight 요청은 CORS 를 모르는 서버를 위한 필요한 작업이라고 보면됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;이러한 이유로 GET, POST 요청의 경우 Simple Request 가 수행되며,
PUT, DELETE 의 경우 Preflight Request 가 수행됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;credentialed-request&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#credentialed-request&quot; aria-label=&quot;credentialed request permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Credentialed Request&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;인증 관련 헤더를 포함할 떄 사용하는 요청입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;쿠키, JWT 등을 클라이언트에서 자동으로 담아서 보내고 싶을때, 클라이언트에서 credentials 를 include 하게되면 서버측에 전달이 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;서버측-설정-및-셋팅-방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B2%84%EC%B8%A1-%EC%84%A4%EC%A0%95-%EB%B0%8F-%EC%85%8B%ED%8C%85-%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;서버측 설정 및 셋팅 방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서버측 설정 및 셋팅 방법&lt;/h3&gt;
&lt;p&gt;중요한것은 서버측에서 설정을 해줘야합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Access-Control-Allow-Credentials : true 로 설정해줘야합니다. 그래야지만 클라이언트 측에서 보낸것을 받을 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Access-Control-Allow-Credentials 를 true 로 두는 순간부터, Access-Control-Allow-Origin 을 별표 * 로 설정해두면 안됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(모든 출처를 허용하면 안된다)
만일 허용하는 순간 에러가 터집니다. 따라서 정확한 출처(Origin) 을 줘야한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;cors-해결하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cors-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0&quot; aria-label=&quot;cors 해결하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CORS 해결하기!&lt;/h2&gt;
&lt;h4&gt;1. 프론트 프록시 서버 설정(개발 환경)&lt;/h4&gt;
&lt;p&gt;프론트 프록시 서버 설정을 조금 바꿔주는 방식입니다. 그러나 서버측에서 충분히 설정이 가능하기 때문에, 굳이 프론트 영역에서 이를 처리해야 하는 경우는 드뭅니다. 중요한 내용은 아니므로 자세한 설명은 생략하겠습니다.&lt;/p&gt;
&lt;h4&gt;2. 직접 헤더에 설정해주기&lt;/h4&gt;
&lt;p&gt;HTTP Header 의 정보를 직접 건들여서 설정해주는 방식이나, 서버 프로그래머 입장에서는 이 방식보다는 스프링부트와 같은 프레임워크를 활용해서 처리하는 방식이 더 트랜디합니다. 이 또한 자세한 설명은 생략하겠습니다.&lt;/p&gt;
&lt;h4&gt;3. 스프링 부트를 이용하기&lt;/h4&gt;
&lt;p&gt;Controller 에 @CrossOrigin 어노테이션을 붙여주면 됩니다.&lt;/p&gt;
&lt;p&gt;origin 속성에다 출처(origin) 을 설정해주면 된다. 만약 이 origin 을 별도로 설정안해주면 디폴트값으로 별표 * 가 설정되어서 모든 출처를 다 받아버리게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ef5a0da1-bec5-4161-ba1c-77092721fe84/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그런데 위 방법은 모든 Controller 에 대해서 @CrossOrigin 어노테이션을 붙어야하는 방식이다. 이를 전역적으로 한방에 처리하는 방식은 아래와 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/c873b234-d35a-4586-ac0b-b5aab4e24ad8/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위처럼 Configuration 클래스를 새롭게 따로 하나 만들고, 해당 클래스가 WebMvcConfigurer 인터페이스를 implements 하도록 합니다. 그리고 CORS 설정을 여기서 따로 해주면 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;지금까지 저희를 그 동안 괴롭혔던 CORS 에 정체에 대해 자세히 알아봤습니다. 다소 긴 포스팅 내용이였으나, 꼼꼼히 다시 읽어보시길 바라며 이해가 잘 안가시는 분이 있다면 댓글로 남겨주세요!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/HTTP/CORS&quot;&gt;https://developer.mozilla.org/ko/docs/Web/HTTP/CORS&lt;/a&gt;
&lt;a href=&quot;https://studyandwrite.tistory.com/374&quot;&gt;https://studyandwrite.tistory.com/374&lt;/a&gt;
&lt;a href=&quot;https://lovon.tistory.com/156&quot;&gt;https://lovon.tistory.com/156&lt;/a&gt;
&lt;a href=&quot;https://www.youtube.com/watch?v=-2TgkKYmJt4&amp;#x26;t=513s&quot;&gt;https://www.youtube.com/watch?v=-2TgkKYmJt4&amp;#x26;t=513s&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Refresh token, Access token 이란?]]></title><description><![CDATA[JWT(Json Web Token) 을 통한 인증,인가 방식을 학습했다면, 바로 이어서 등장하는 이론이 바로 Refresh Token 과 Access Token…]]></description><link>https://haon.site/haon/server/jwt-token/</link><guid isPermaLink="false">https://haon.site/haon/server/jwt-token/</guid><pubDate>Wed, 14 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;JWT(Json Web Token) 을 통한 인증,인가 방식을 학습했다면, 바로 이어서 등장하는 이론이 바로 Refresh Token 과 Access Token 입니다. 이들은 무엇이며, 어떤 방식으로 클라이언트와 서버간에 전송이 이루어지며, 코드 구현은 어떻게 되는지 자세히 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;지난 포스팅에서 JWT 에 대해 알아봤는데, 참고하실 분들은 아래 링크를 따라서 JWT가 무엇인지에 대해서 학습하시는 것을 권장드립니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Spring-Security-JWT&quot;&gt;https://velog.io/@msung99/Spring-Security-JWT&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;기존-access-token-jwt-의-한계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EC%A1%B4-access-token-jwt-%EC%9D%98-%ED%95%9C%EA%B3%84&quot; aria-label=&quot;기존 access token jwt 의 한계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기존 Access Token (JWT) 의 한계&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/bf417743-408f-45b6-9b66-c2dc46b7feac/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;1-payload-의-값을-탈취해가기-쉽다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-payload-%EC%9D%98-%EA%B0%92%EC%9D%84-%ED%83%88%EC%B7%A8%ED%95%B4%EA%B0%80%EA%B8%B0-%EC%89%BD%EB%8B%A4&quot; aria-label=&quot;1 payload 의 값을 탈취해가기 쉽다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Payload 의 값을 탈취해가기 쉽다.&lt;/h3&gt;
&lt;p&gt;기존의 JWT 를 활용해서 아무리 암호화를 잘 해놓았다고 한들, 클라이언트와 서버 사이에서 토큰 기반으로 정보를 통신하는 도중에 해커에게 토큰안에 들어있는 정보를 탈취당할 위험성이 존재합니다.&lt;/p&gt;
&lt;p&gt;Signature(서명값)은 분명 개발자가 지정해준 암호화 알고리즘(ex.HS256 알고리즘) 을 사용하더라도, 해커가 이를 악용해서 Payload 영역의 데이터를 손쉽게 얻어낼 수 있습니다.&lt;/p&gt;
&lt;p&gt;따라서 Payload 에는 정말 중요한 정보 (ex. 계좌정보, 아이디, 비밀번호) 등을 저장해서 통신하는 방식은 권장하지 않고 있는만큼 한계점이 드러냅니다.&lt;/p&gt;
&lt;h3 id=&quot;2-한번-탈취당한-토큰은-만료기간-전까지-끝도-없이-정보가-유출된다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%ED%95%9C%EB%B2%88-%ED%83%88%EC%B7%A8%EB%8B%B9%ED%95%9C-%ED%86%A0%ED%81%B0%EC%9D%80-%EB%A7%8C%EB%A3%8C%EA%B8%B0%EA%B0%84-%EC%A0%84%EA%B9%8C%EC%A7%80-%EB%81%9D%EB%8F%84-%EC%97%86%EC%9D%B4-%EC%A0%95%EB%B3%B4%EA%B0%80-%EC%9C%A0%EC%B6%9C%EB%90%9C%EB%8B%A4&quot; aria-label=&quot;2 한번 탈취당한 토큰은 만료기간 전까지 끝도 없이 정보가 유출된다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 한번 탈취당한 토큰은 만료기간 전까지 끝도 없이 정보가 유출된다.&lt;/h3&gt;
&lt;p&gt;또한, 기존 JWT 의 치명적인 단점은 &lt;strong&gt;한 번 발급된 경우 유효기간이 지날 때까지 계속 사용할 수 있기 때문에 만약 토큰이 악의적으로 활용될 경우 토큰의 유효기간이 다 될 때까지 정보가 계속 탈취당할 수 있다&lt;/strong&gt;는 점입니다.&lt;/p&gt;
&lt;p&gt;그렇다면 여기서 더 JWT 를 통한 방식에서 더 강화된 보안방식은 없는걸까요?
이때 등장한 것이 바로 Refresh Token 입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;refresh-token-의-등장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#refresh-token-%EC%9D%98-%EB%93%B1%EC%9E%A5&quot; aria-label=&quot;refresh token 의 등장 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Refresh Token 의 등장&lt;/h2&gt;
&lt;p&gt;이러한 JWT의 문제점을, Access Token의 유효기간을 줄이고, Refresh Token이라는 새로운 토큰을 발급함으로써 해결할 수 있습니다.&lt;/p&gt;
&lt;p&gt;기존 Access Token(JWT)를 통한 인증 방식의 문제는, 제3자에게 탈취당할 경우 유효기간이 끝날 때까지 계속 정보가 털릴 수 있다는 점이 있습니다.&lt;/p&gt;
&lt;p&gt;그렇다고 해서 Access Token의 유효기간을 막 줄이자니, 그만큼 사용자가 로그인을 자주 해서 Token을 다시 발급받아야 하므로 여간 불편하고 귀찮은 일이 아닐 수 없습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이런 상황에서, &quot;유효기간을 짧게 하면서 보안을 강화할 수 있는 방법이 있지 않을까?&quot; 라는 질문의 해답을 Refresh Token이 제공합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;refersh-token-이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#refersh-token-%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;refersh token 이란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Refersh Token 이란?&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/96c38310-84cf-476a-b929-ac273a1f68eb/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Refresh Token은 Access Token과 똑같은 형태의 JWT입니다. &lt;strong&gt;처음 로그인했을 때, Access Token과 동시에 발급이 되며, Access Token의 유효기간이 지났을 때 새롭게 발급해주는 역할을 합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;예를 들어 Access Token의 유효기간은 1시간이고, Refresh Token의 유효기간은 3주라고 하자. 이 때에 1시간이 지나서 Access Token 이 만료되더라도, 3주가 넘어가기 전까지는(= Refresh Token이 만료되기 전까지는) 새로운 Access Token을 계속 발급받을 수 있습니다.&lt;/p&gt;
&lt;p&gt;Access Token 과 Refesh Token 을 정리해보자면 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;AccessToken : resource 에 접근할 수 있는 필수적인 정보를 담은 토큰, 서버는 AccessToken을 통해 클라이언트를 식별하고 권한 유무를 판단할 수 있습니다.. 또한, 생명 주기를 가져 Expired 되면 RefreshToken을 통해 갱신해야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;RefreshToken : 새로운 AccessToken을 발급하기 위해 가지는 토큰&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;refresh-token-의-특징&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#refresh-token-%EC%9D%98-%ED%8A%B9%EC%A7%95&quot; aria-label=&quot;refresh token 의 특징 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Refresh Token 의 특징&lt;/h2&gt;
&lt;p&gt;Access Token의 보안 취약성은 여전히 존재하지만, 유효기간이 짧기 때문에 좀 더 안전하게 사용할 수 있게 되었습니다.&lt;/p&gt;
&lt;p&gt;Refresh Token은 유효기간이 지나면 다시 발급받아야 하고, 이 또한 탈취의 위험성이 있기 때문에 기간을 적절하게 설정해줘야 합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;특징1) JWT 토큰은 암호화하고자하는 데이터를 포함하여 발행할 수 있으며 정해진 만료기간이 지나면 토큰을 디코딩 할 수 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;특징2) 서비스의 중요도와 특징에 따라 Access Token, Refresh Token의 만료 기간은 상이합니다.
&lt;ul&gt;
&lt;li&gt;보통 AccessToken : 30분, RefreshToken : 2주 정도의 만료기간을 가지도록 설정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;accesstoken-refreshtoken-의-한계점--서버측의-검증-로직을-통한-보안강화&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#accesstoken-refreshtoken-%EC%9D%98-%ED%95%9C%EA%B3%84%EC%A0%90--%EC%84%9C%EB%B2%84%EC%B8%A1%EC%9D%98-%EA%B2%80%EC%A6%9D-%EB%A1%9C%EC%A7%81%EC%9D%84-%ED%86%B5%ED%95%9C-%EB%B3%B4%EC%95%88%EA%B0%95%ED%99%94&quot; aria-label=&quot;accesstoken refreshtoken 의 한계점  서버측의 검증 로직을 통한 보안강화 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AccessToken, RefreshToken 의 한계점 : 서버측의 검증 로직을 통한 보안강화&lt;/h2&gt;
&lt;p&gt;그렇다면 Refresh Token 자체가 탈취당할 위험성도 있지 않을까요? 이 점은 아직 완전히 극복하지 못한 토큰의 한계점이라고 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;Accesss Token 이 탈취당해도 만료기간 자체가 짧기 때문에 별 위험성이 없지만, Refresh Token 자체가 탈취당한다면 끝도 없이 계속 정보가 유출될겁니다.&lt;/p&gt;
&lt;p&gt;Refresh Token 이 탈취당하면, 해당 토큰의 만료기간 전까지 Access Token 을 무한정으로 생성해서 다시 해커가 정상적인 사용자로 위장할 수 있습니다.&lt;/p&gt;
&lt;p&gt;이를 그래도 해결하기 위한 서버측의 검증 로직을 적용할 수 있는데, &lt;strong&gt;데이터베이스에 발급받은 Refresh Token 을 저장해두고 요청으로 들어오는 Refresh Token 과 DB 에 저장된 Refresh Token 을 대조.비교하며 검증하는 것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;access-token-refresh-token-인증-과정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#access-token-refresh-token-%EC%9D%B8%EC%A6%9D-%EA%B3%BC%EC%A0%95&quot; aria-label=&quot;access token refresh token 인증 과정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Access Token, Refresh Token 인증 과정&lt;/h2&gt;
&lt;p&gt;토큰을 통한 인증방식의 상세한 과정을 알아봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d4759109-af4c-4411-83aa-6a328eb90b74/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;1.클라이언트에서 로그인을 요청합니다.&lt;/p&gt;
&lt;p&gt;2.서버는 로그인 요청을 받고, Response 로 Access Token, Refresh Token 을 발급해줍니다.&lt;/p&gt;
&lt;p&gt;3.클라이언트는 발급받은 Access Token, Refersh Token 을 별도로 Local Storage 에 저장해둡니다.&lt;/p&gt;
&lt;p&gt;4.클라이언트에서 서버에 대한 매 요청마다 HTTP Header 에 Access Token 을 포함해서 보냅니다.&lt;/p&gt;
&lt;p&gt;5-1. AccessToken이 유효하다면 서버는 요청을 받아들이고 그에 상응하는 Response 를 보내주면 됩니다.&lt;/p&gt;
&lt;p&gt;5-2. AccessToken이 유효하지 않다면, 즉 AccessToken이 만료되었다면 RefreshToken을 요구하는 상태코드와 메시지를 클라이언트로 반환합니다.&lt;/p&gt;
&lt;p&gt;6.(5-2의 경우) 클라이언트가 RefreshToken 이 필요하다는 Response 를 받게된다면, 그에 대해 서버에게 다시 요청으로 아까 local Storage 에 저장해뒀던 RefreshToken을 보내서 새로운 AccessToken 을 발급 받습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;이때 서버는 클라이언트로 RefreshToken을 검증하고 유효할 경우에 새로운 AccessToken을 발급하고 기존 요청을 처리한 후 헤더에 AccessToken과 함께 응답을 해야 합니다!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;RefreshToken 을 검증하는 과정에는 앞서 DB에 저장해놓은 RefreshToken 과 요청으로 들어온 RefreshToken 을 비교.대조해야 하고, 만료기간 또한 검증해서 유효성을 판단해야 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;RefreshToken이 동일하고 만료기간도 지나지 않았다면 새로운 Access Token을 발급해줍니다.&lt;/p&gt;
&lt;p&gt;7.클라이언트는 발급받은 새로운 AccessToken 으로 다시 서버에게 매 요청마다 Access Token 을 포함해서 보내면 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;이로써 Refresh Token 과 Access Token 의 특징에 대해 살펴봤습니다.
다만 코드 구현이 어떻게 이루어지는지 궁금하실텐데, 곧 바로 새로운 포스팅에서 코드 구현 내용을 다루어보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://jwt.io&quot;&gt;jwt.io&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/JSON_Web_Token&quot;&gt;JSON_WEB_TOKEN 위키피디아&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/questions/32060478/is-a-refresh-token-really-necessary-when-using-jwt-token-authentication&quot;&gt;스택오버플로우 when_using_token_authentication&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@park2348190/JWT%EC%97%90%EC%84%9C-Refresh-Token%EC%9D%80-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%9C%EA%B0%80&quot;&gt;https://velog.io/@park2348190/JWT에서-Refresh-Token은-왜-필요한가&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://kukekyakya.tistory.com/entry/Spring-boot-access-token-refresh-token-%EB%B0%9C%EA%B8%89%EB%B0%9B%EA%B8%B0jwt&quot;&gt;https://kukekyakya.tistory.com/entry/Spring-boot-access-token-refresh-token-%EB%B0%9C%EA%B8%89%EB%B0%9B%EA%B8%B0jwt&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.naver.com/PostView.naver?blogId=jinwoo6612&amp;#x26;logNo=222462211251&amp;#x26;parentCategoryNo=&amp;#x26;categoryNo=32&amp;#x26;viewDate=&amp;#x26;isShowPopularPosts=true&amp;#x26;from=search&quot;&gt;https://blog.naver.com/PostView.naver?blogId=jinwoo6612&amp;#x26;logNo=222462211251&amp;#x26;parentCategoryNo=&amp;#x26;categoryNo=32&amp;#x26;viewDate=&amp;#x26;isShowPopularPosts=true&amp;#x26;from=search&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-Access-Token-Refresh-Token-%EC%9B%90%EB%A6%AC-feat-JWT&quot;&gt;https://inpa.tistory.com/entry/WEB-📚-Access-Token-Refresh-Token-원리-feat-JWT&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Git 브렌치(Branch) 개념, 핵심 명령어 정리]]></title><description><![CDATA[Git 과 GitHub 를 입문하는 많은 분들이 브렌치에 많이 들어보셨을 것이라고 생각합니다. 그 만큼 브렌치(branch…]]></description><link>https://haon.site/haon/github/what-is-branch/</link><guid isPermaLink="false">https://haon.site/haon/github/what-is-branch/</guid><pubDate>Fri, 18 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Git 과 GitHub 를 입문하는 많은 분들이 브렌치에 많이 들어보셨을 것이라고 생각합니다. 그 만큼 브렌치(branch) 라는 개념은 정말 중요하며, 개인 프로젝트라면 상관없을 수 있으나 협업 프로젝트 만큼은 브렌치를 사용하지 않을 수가 없습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;브렌치branch-란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%8C%EB%A0%8C%EC%B9%98branch-%EB%9E%80&quot; aria-label=&quot;브렌치branch 란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;브렌치(Branch) 란?&lt;/h2&gt;
&lt;p&gt;브랜치란 독립적으로 어떤 작업을 진행하기 위한 개념입니다. 필요에 의해 만들어지는 각각의 브랜치는 다른 브랜치의 영향을 받지 않기 때문에, 여러 작업을 동시에 진행할 수 있습니다.&lt;/p&gt;
&lt;p&gt;또한 이렇게 만들어진 브랜치는 다른 브랜치와 병합(Merge)함으로써, 작업한 내용을 다시 새로운 하나의 브랜치로 모을 수 있습니다&lt;/p&gt;
&lt;p&gt;브랜치(Branch)를 통해 하나의 프로젝트를 여러 갈래로 나누어서 관리할 수 있습니다. 각각의 독립된 Branch에서 마음대로 소스코드를 변경하여 작업 한 후 원래 버전과 비교하여 또 하나의 새로운 버전을 만들어 낼 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/67b3065b-af7a-4ac0-bc6c-308d7ffa08f9/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위와 같이 작업을 진행하는 메인 브렌치가 Master 브렌치라고 가정해봅시다. 그러면 개발자 A 와 B 는 동일한 Master 브렌치에서 작업하는 것이 아닙니다. 각자 Master 브렌치에서 현재까지 작업한 내용(히스토리) 를 로컬로 가져와서, 브렌치 A와 B에서 작업을 진행하는 방식입니다.&lt;/p&gt;
&lt;p&gt;각자의 브렌치에서 작업한 내용은 나중에 Merge 라는 병합과정을 통해서 메인 브렌치인 Master 브렌치에 병합됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;merge브렌치-병합하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#merge%EB%B8%8C%EB%A0%8C%EC%B9%98-%EB%B3%91%ED%95%A9%ED%95%98%EA%B8%B0&quot; aria-label=&quot;merge브렌치 병합하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Merge(브렌치 병합하기)&lt;/h2&gt;
&lt;p&gt;그렇다면 각 개발자들이 각자의 브렌치에서 작업한 내용은 어떻게 합쳐질까요? 이는 바로 Merge 라는 과정을 통해 합쳐질 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/45d626b9-af69-4c58-bf31-ed365d0f8a57/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;예를들어 위와 같이 브렌치 A(보라색) 와 B에서(초록색)에서 버전을 생성하면서 작업한 내용들은 Master 브렌치에 PR(Pull Request) 와 Merge 과정을 통해서 병합될 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;여기서 Master 브렌치란 브렌치중에서 으뜸이 되는 브렌치입니다. 즉, 해당 레포지토리에 대해 모든 개발자들이 공유하는 메인 브렌치를 의미합니다. 또한 해당 프로젝트에 대해서 결과물이라 할 수 있는 코드들이 올라라가는 브렌치라고 보시면 됩니다. 이 브렌치는 꼭 기억해두시는게 좋습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;브렌치의-충돌conflict-발생&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%8C%EB%A0%8C%EC%B9%98%EC%9D%98-%EC%B6%A9%EB%8F%8Cconflict-%EB%B0%9C%EC%83%9D&quot; aria-label=&quot;브렌치의 충돌conflict 발생 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;브렌치의 충돌(Conflict) 발생&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;충돌이 발생하는 경우 : Git 을 이용하여 협업을 할 때, 브랜치는 여러개인 경우인데 여러명이 동시에 같은 브랜치의 같은 내용을 접근하고 수정했을때 충돌이 발생합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;각 개발자가 브렌치를 분할했다면 각자 맡은 파일 또는 파트를 작업하는 것이 원칙이나, 간혹 동일한 파일을 건드린다면 이는 곧 &quot;충돌(Conflict)&quot; 로 이어지는 경우가 자주 발생합니다.&lt;/p&gt;
&lt;p&gt;따라서 브렌치를 생성했다면 각자 맡은 파일과 영역에 대해서만 작업을 진행해야하며, master 또는 main 브렌치에 push 로컬에서 작업한 내용을 직접 push 하는 일을 지양하도록 합시다!&lt;/p&gt;
&lt;h4&gt;예시&lt;/h4&gt;
&lt;p&gt;예로 아래처럼 master 브렌치에서 작업한 커밋들이 존재한다고 해봅시다.
이떄 sub 라는 새로운 브렌치를 생성하고 커밋을 생성하는 경우를 가정해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/892624f1-a7f1-4092-bb12-06be5d5cb2ed/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;branch-관리를-위한-명령&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#branch-%EA%B4%80%EB%A6%AC%EB%A5%BC-%EC%9C%84%ED%95%9C-%EB%AA%85%EB%A0%B9&quot; aria-label=&quot;branch 관리를 위한 명령 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Branch 관리를 위한 명령&lt;/h2&gt;
&lt;p&gt;cf) HEAD : 여러 브랜치 중에서 현재 작업중인 브랜치가 무엇인지 알려주는 포인터&lt;/p&gt;
&lt;h4&gt;1 브렌치 생성 및 조회&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;git branch : 모든 브랜치 조회
git branch 브랜치명 : 새로운 브랜치 생성&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h4&gt;2. 브렌치 전환하기&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;git checkout 브렌치명
git switch 브렌치명&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;checkout 키워드가 활용되는 명령어들이 너무 많아져서, switch 라는 키워드의 명령어를 새롭게 만들어졌다고 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h4&gt;3. 새로운 브랜치 생성후, 해당 브랜치로 바로 이동하기&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;git checkout -b 새로운 브랜치명
git switch -c 새로운 브랜치명&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;새로운 브렌치를 생성하면서 이동하는 명령입니다. 즉, 위 명령어들은 아래 명령어를 한 줄로 줄인것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;git checkout my_branch
git checkout my_branch&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h4&gt;4. 브렌치 히스토리 조회 (git log 관련)&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;git log --oneline
git log --oneline --branches
git log --oneline --branches --graph&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;git log --oneline : git log 를 한줄로 간략히 조회&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git log --oneline --branches : 모든 브랜치의 커밋 상황을 확인&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git log --oneline --branches --graph : 모든 브랜치의 커밋 상황을 시각적으로 표현
(각 브랜치들의 커밋 생성순서 및 관계를 그래프 형태로 확인 가능합니다.)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4&gt;4. 두 브렌치간의 Commit 차이 조회&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;git log 브렌치1..브렌치2&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;git log master ..safecal : 브렌치 사이의 차이점을 확인합니다. 여기서는 master 와 safecal 브랜치의 차이를 확인합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;git log 브렌치1..브렌치2 : 브렌치1에는 없고 브렌치2에만 있는 커밋의 log 를 띄어줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git log 브렌치2..브렌치1 : 브렌치2에는 없고 브렌치1에만 있는 커밋의 log 를 띄어줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4&gt;5. 원격 레포지토리에서 브랜치 삭제하기&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;git push &amp;#x3C; remote &gt;--delete &amp;#x3C; branch &gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;예를 들어서 삭제하고 싶은 원격 브랜치 이름이 fix/authentication 이라면&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;git push origin --delete fix/authentication&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;와 같이 명령을 입력하시면 됩니다. 그러면 이제 이 브랜치는 원격에서 삭제됩니다.
참고로 더 짧은 버전의 명령어도 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;git push &amp;#x3C; remote &gt; :&amp;#x3C; branch &gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이렇게 쓰면 됩니다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Git의 4가지 작업 영역 (working, staging, local, remote)]]></title><description><![CDATA[Repository Repository…]]></description><link>https://haon.site/haon/github/areas/</link><guid isPermaLink="false">https://haon.site/haon/github/areas/</guid><pubDate>Fri, 18 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;repository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#repository&quot; aria-label=&quot;repository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Repository&lt;/h2&gt;
&lt;p&gt;Repository란 번역하면 말 그대로 &quot;저장소&quot;를 의미합니다. 깃허브로 협업시 코드를 공유하며 협업을 진행해야하는 경우가 자주 발생할텐데, 이를 위해 따로 공통적으로 관리 및 작업할 수 있는 작업 저장소가 필요할 것입니다.&lt;/p&gt;
&lt;p&gt;이때 저장소의 위치에 따라서 &quot;로컬 레포지토리(local Repository)&quot; 와 &quot;원격 저장소(Remote Repository)&quot; 로 구분됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;로컬-레포지토리-vs-원격-레포지토리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EC%BB%AC-%EB%A0%88%ED%8F%AC%EC%A7%80%ED%86%A0%EB%A6%AC-vs-%EC%9B%90%EA%B2%A9-%EB%A0%88%ED%8F%AC%EC%A7%80%ED%86%A0%EB%A6%AC&quot; aria-label=&quot;로컬 레포지토리 vs 원격 레포지토리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로컬 레포지토리 vs 원격 레포지토리&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a98f5ff2-cbd9-4eed-824b-bf010211c659/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;로컬-레포지토리local-repository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EC%BB%AC-%EB%A0%88%ED%8F%AC%EC%A7%80%ED%86%A0%EB%A6%AClocal-repository&quot; aria-label=&quot;로컬 레포지토리local repository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로컬 레포지토리(local repository)&lt;/h3&gt;
&lt;p&gt;우리가 작업하는 내용물들은 모두 각 개인의 로컬 작업환경(컴퓨터)에서 이루어질 것입니다. 당연한 소리죠?&lt;/p&gt;
&lt;p&gt;로컬 저장소란 각 개인 PC 에 파일에 저장되는 개인 전용 저장소를 의미합니다.
즉, 평소에 내 PC 에서 작업하는 내용은 곧 로컬 저장소에서 수행되는 것이죠.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;우리가 로컬에서 작업한 내용을 깃허브 상의 원격 레포지토리에 push 함으로써 팀원들이 해당 remote 레포지토리에서 소스코드를 공유 및 공동작업이 가능해집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;원격-레포지토리remote-repository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EA%B2%A9-%EB%A0%88%ED%8F%AC%EC%A7%80%ED%86%A0%EB%A6%ACremote-repository&quot; aria-label=&quot;원격 레포지토리remote repository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원격 레포지토리(Remote Repository)&lt;/h3&gt;
&lt;p&gt;원격 저장소란, &quot;깃허브(GitHub)&quot; 라는 웹 서비스상에 존재하는 저장소로써, 협업시 모든 개발자들이 공동으로 공유하는 저장소를 의미합니다.&lt;/p&gt;
&lt;p&gt;즉, 개인 PC 에서 작업한 내용을 타인과 공유 및 공동작업이 필요하다고 느낄때 깃허브의 원격 저장소에 소스코드를 올리는 것이죠.&lt;/p&gt;
&lt;p&gt;반대로 원격 저장소에서 다른 사람이 작업한 파일 및 내용을 로컬 저장소로 가져올 수도 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;git의-4가지-영역&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#git%EC%9D%98-4%EA%B0%80%EC%A7%80-%EC%98%81%EC%97%AD&quot; aria-label=&quot;git의 4가지 영역 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Git의 4가지 영역&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Git 의 작업파일은 관점 및 상태(status) 에 따라서 4가지 영역에 위치하게 된다고 할 수 있습니다. 그 영역 4가지는 아래와 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Working Directory (Working Tree)&lt;/li&gt;
&lt;li&gt;Staging Area&lt;/li&gt;
&lt;li&gt;Local Repository&lt;/li&gt;
&lt;li&gt;Remote Repository&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e61e0689-9f2a-4e0c-9ffb-6f00cc8643e2/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;로컬 저장소와 원격 저장소의 개념은 앞서 살펴봐서 무엇인지 아실 것입니다. 그렇다면 로컬 저장소에 작업 내용이 올라가기전의 해당 내용들이 Working Directory 와 Staging Area 에 올라간다는 것인데, 이들이 무엇인지 알아보겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;1-working-directory-작업-영역&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-working-directory-%EC%9E%91%EC%97%85-%EC%98%81%EC%97%AD&quot; aria-label=&quot;1 working directory 작업 영역 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Working Directory (작업 영역)&lt;/h3&gt;
&lt;p&gt;실제 작업 내용들이 작업이 이루어지는 영역이라고 할 수 있습니다.
즉, 실제 코드를 수정하고 추가하는 변경이 이루어지는 영역입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;파일 상태는 Untracked file, Tracked file 로 분류됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2-staging-area&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-staging-area&quot; aria-label=&quot;2 staging area permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Staging Area&lt;/h3&gt;
&lt;p&gt;앞선 Wokring Directory 작업한 내용을 Staging Area 로 올려보낼 수 있습니다.&lt;/p&gt;
&lt;p&gt;로컬 레포지토리로 정보가 저장되기 전의 준비영역으로써, 현재까지 진행한 작업내용들을 버전화(Version) 를 사켜서 레포지토리에 업로드시키고 싶을 떄, 어떤 파일 및 작업내용들을 묶음으로 묶어서 버전화를 시킬지를 Staging Area 에 올리는 것이라고 보시면 됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;파일 상태는 modified, unmodified 로 분류됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;3-local-repository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-local-repository&quot; aria-label=&quot;3 local repository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Local Repository&lt;/h3&gt;
&lt;p&gt;Staging Area 에서 버전화를 시키도록 했던 작업내용들을 로컬 저장소에 옮긴다면, 이들은 확정적으로 하나의 버전(Version) 을 생성해냅니다.&lt;/p&gt;
&lt;p&gt;로컬 저장소에 업로드 됨으로써 생성된 하나의 버전, 즉 히스토리는 &quot;git log&quot; 명령어로 조회할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;4-remote-repository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-remote-repository&quot; aria-label=&quot;4 remote repository permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Remote Repository&lt;/h3&gt;
&lt;p&gt;로컬에서 생성된 버전은 모두가 공유하기 위한 깃허브의 원격 저장소에 push 할 수 있습니다. 로컬 저장소와 원격 저장소의 개념은 앞서 살펴봤으므로 자세한 내용은 생략하겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;stash---git-의-숨겨진-영역이-있다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stash---git-%EC%9D%98-%EC%88%A8%EA%B2%A8%EC%A7%84-%EC%98%81%EC%97%AD%EC%9D%B4-%EC%9E%88%EB%8B%A4&quot; aria-label=&quot;stash   git 의 숨겨진 영역이 있다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&quot;Stash&quot; - git 의 숨겨진 영역이 있다?&lt;/h3&gt;
&lt;p&gt;위 4가지 영역외에도 사실 깃허브는 숨겨진 영역을 하나 보유하고 있습니다. 그것은 바로 &quot;Stash&quot; 영역입니다.&lt;/p&gt;
&lt;p&gt;이 영역은 현재 Working Directroy 에서 작업한 파일 및 내용을 일시적으로 가려주고, stash 영역에 잠시 &quot;임시저장&quot; 하는 영역입니다.&lt;/p&gt;
&lt;p&gt;이 영역과 관련한 명령어는 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;git stash : 현 작업내용에 대해 임시저장
git stash pop : 가장 최근에 stash 시킨 작업내용을 복구시킴
git stash list : stash 시킨 작업내용들을 조회&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[HTTP의 비연결성(Connectionless)]]></title><description><![CDATA[이번 포스팅에서는, 지난 포스팅에 이어서 http 의 특성중 하나인 비연결성에 대해 알아보겠습니다. 연결을 유지하는 모델(Connection model) TCP/IP 모델을 통해 클라이언트와 서버를 연결한 경우를 생각해봅시다.
기본적으로 TCP/IP…]]></description><link>https://haon.site/haon/http/connectionless/</link><guid isPermaLink="false">https://haon.site/haon/http/connectionless/</guid><pubDate>Wed, 16 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이번 포스팅에서는, 지난 포스팅에 이어서 http 의 특성중 하나인 비연결성에 대해 알아보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;연결을-유지하는-모델connection-model&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%B0%EA%B2%B0%EC%9D%84-%EC%9C%A0%EC%A7%80%ED%95%98%EB%8A%94-%EB%AA%A8%EB%8D%B8connection-model&quot; aria-label=&quot;연결을 유지하는 모델connection model permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;연결을 유지하는 모델(Connection model)&lt;/h2&gt;
&lt;p&gt;TCP/IP 모델을 통해 클라이언트와 서버를 연결한 경우를 생각해봅시다.
기본적으로 TCP/IP 모델은 연결을 유지하는 모델입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ed1e9863-78dd-40f1-8ddd-34ea50c3a16b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;클라이언트1이 서버와 TCP/IP 모델을 통해 연결을 하고 요청을 보내고 응답을 받습니다. 이때 응답을 받고난 뒤에도 클라이언트1과 서버는 계속 연결을 유지하고있는 상태가 됩니다.&lt;/p&gt;
&lt;p&gt;다음으로 클라이언트2,3 도 동일한 과정을 진행했다면, 요청-응답 이후에도 클라이언트2 와 서버는 연결을 계속 유지하고 있는 상태가 됩니다.&lt;/p&gt;
&lt;p&gt;또 다시 클라이언트1이 서버에게 요청을 하면 서버가 응답을 할겁니다.&lt;/p&gt;
&lt;p&gt;이런 방식의 문제점은, &lt;strong&gt;서버가 여러 클라이언트와 계속 연결을 유지하면서 서버 자원을 소모&lt;/strong&gt;하게 된다는 점입니다. 즉, 클라이언트 2와 3이 놀고있어도 계속 서버가 연결상태를 유지해줘야한다는 단점이 있는것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;연결을-유지하지-않는-모델connectionless-model&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%97%B0%EA%B2%B0%EC%9D%84-%EC%9C%A0%EC%A7%80%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EB%AA%A8%EB%8D%B8connectionless-model&quot; aria-label=&quot;연결을 유지하지 않는 모델connectionless model permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;연결을 유지하지 않는 모델(Connectionless model)&lt;/h2&gt;
&lt;p&gt;반대로 연결을 유지하지 않는 모델도 살펴봅시다. TCP/IP 모델을 통해 클라이언트1과 서버가 연결되고 요청과 응답의 과정이 이루어졌다면, 이 뒤로 연결을 계속 유지하는 것이 아닌 연결을 끊어버리는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7fd32cf4-773c-44ae-8444-fc5755b89414/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;보듯이, 클라이언트와 서버가 각자 볼일이 끝났다면 연결을 끊어버리는 것이죠.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/cf65313b-9175-4465-9745-567c7c575280/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이러한 특징은, &lt;strong&gt;서버가 클라이언트들과 연결 상태를 유지하지 않음으로써 최소한의 자원을 사용한다&lt;/strong&gt;는 장점이 생기게 됩니다. 클라이언트가 수만개라면 서버가 유지해야 할 자원을 딱 요청할떄만 받고 응답해주고 연결을 끊어버리므로 최소한의 자원으로 서버를 유지할 수 있다는 특징이 생기게 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;비연결성connectionless&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EC%97%B0%EA%B2%B0%EC%84%B1connectionless&quot; aria-label=&quot;비연결성connectionless permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비연결성(Connectionless)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;요청-응답을 통해 데이터를 짧은 시간동안 주고받고 연결을 끊어버린다.&lt;/li&gt;
&lt;li&gt;수천명의 유저가 서비스를 사용하더라도, 동시에 처리해야하는 요청이 줄어든다. (한번 요청을 처리하면 연결을 끊어버리고 자원을 절약하는 방식이므로)&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;HTTP 는 기본적으로 &lt;strong&gt;연결을 유지하지 않는 모델&lt;/strong&gt; 로,일반적으로 초 단위 이하의 빠른 속도로 요청 - 응답을 해주다는 특징을 지닙니다. 이런 장점이 뭐가 좋냐면, &lt;strong&gt;필요하고 원하는 데이터만 아주 잠깐 주고받고 연결을 끊어버릴 수 있다&lt;/strong&gt;는 것입니다.&lt;/p&gt;
&lt;p&gt;1시간동안 수천명이 서비스를 사용하더라도, 실제 서버에서 동시에 처리하는 요청은 수십개 이하로 매우 작습니다. 예를들어 수천명의 유저들이 웹 브라우저에서 계속 연속해서 계속 검색버튼을 누르지는 않겠죠?&lt;/p&gt;
&lt;p&gt;이에따라, http 의 비연결성 특징으로 인해 서버 자원을 정말 효율적으로 사용할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;비연결성의-한계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EC%97%B0%EA%B2%B0%EC%84%B1%EC%9D%98-%ED%95%9C%EA%B3%84&quot; aria-label=&quot;비연결성의 한계 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비연결성의 한계&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;TCP/IP 연결을 계속 새롭게 맺어줘야함 (3-way handshakee 시간 추가)&lt;/li&gt;
&lt;li&gt;웹 브라우저로 사이트를 요청하면 HTML 를 비롯한 수많은 자원들을 불필요하게 계속해서 다운로드 해야함 =&gt; &lt;strong&gt;현재는 HTTP 지속연결(Persistent Connections) 로 문제점을 해결&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;반면, 비연결성 특징으로 인한 한계점도 존재합니다. 우선 새롭게 연결을 시도할떄마다 &lt;strong&gt;TCP/IP 연결을 새롭게 계속 맺어야합니다.&lt;/strong&gt; 안그래도 TCP 의 &lt;strong&gt;3 way handshake 연결 방식&lt;/strong&gt;은 시간이 오래걸리는데, 연결을 끊고 새롭게 또 연결을 해줄때마다 이 방식이 사용되서 문제이죠.&lt;/p&gt;
&lt;p&gt;또한 &lt;strong&gt;웹 브라우저로 사이트를 요청하면,&lt;/strong&gt; 이 사이트 페이 하나를 띄우기위해 HTML뿐 아니라 JS, CSS, 추가 이미지등의 &lt;strong&gt;수 많은 자원이 함꼐 다운로드됩니다.&lt;/strong&gt; 요청하는 페이지가 바뀔때마다 수많은 자원들을 계속 요청해서 받고, 연결을 끊어버려서 자원들을 버리는 방식은 정말 비효율적입니다.&lt;/p&gt;
&lt;p&gt;이를 해결하기위해, 현재 HTTP는 &lt;strong&gt;지속 연결(Persistent Connections)&lt;/strong&gt; 방식으로 문제를 해결했습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;초기의-http--연결과-종료의-낭비성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B4%88%EA%B8%B0%EC%9D%98-http--%EC%97%B0%EA%B2%B0%EA%B3%BC-%EC%A2%85%EB%A3%8C%EC%9D%98-%EB%82%AD%EB%B9%84%EC%84%B1&quot; aria-label=&quot;초기의 http  연결과 종료의 낭비성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;초기의 HTTP : 연결과 종료의 낭비성&lt;/h2&gt;
&lt;p&gt;초창기의 HTTP 의 일련의 처리흐름을 살펴보겠습니다. TCP/IP 를 통해 클라이언트와 서버를 연결했습니다. 그러고 요청을 서버에개 보내서 HTML 파일을 응답받고 연결을 끊어버렸습니다.&lt;/p&gt;
&lt;p&gt;그 뒤로 확인해보니 자바스크립트, 이미지 등의 파일이 필요해서, &lt;strong&gt;또 TCP/IP 를 통해 클라이언트와 서버를 연결하고 파일들을 응답받고 연결을 끊어버리는 방식을 계속해서 반복&lt;/strong&gt;하는 비효율적인 방식이였습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/8e43681b-811c-4e64-a6db-ed9fee2ad37c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;http의-persistent-connections지속-연결&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http%EC%9D%98-persistent-connections%EC%A7%80%EC%86%8D-%EC%97%B0%EA%B2%B0&quot; aria-label=&quot;http의 persistent connections지속 연결 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP의 Persistent Connections(지속 연결)&lt;/h2&gt;
&lt;p&gt;반면, 위와 같이 순차적으로 파일들을 하나하나씩 재연결하고 건내받는 방식이아닌, 한번의 연결에서 모든 필요한 파일들을 건내받고 연결을 끊어버리는 방식입니다. 즉, &lt;strong&gt;필요한 모든 파일들을 건내받을 떄 까지 계속해서 연결을 끊지않고 지속하는 방식&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;그래서 왠만한 한 사이트 페이지를 완벽하게 구현해내고 브라우저에 띄어주기 전까지 연결을 끊지않고 기다리는 방식이죠.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4e5e490a-93dc-4241-b057-7c2753624a64/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;stateless-대용량-트래픽-처리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stateless-%EB%8C%80%EC%9A%A9%EB%9F%89-%ED%8A%B8%EB%9E%98%ED%94%BD-%EC%B2%98%EB%A6%AC&quot; aria-label=&quot;stateless 대용량 트래픽 처리 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Stateless: 대용량 트래픽 처리&lt;/h2&gt;
&lt;p&gt;저희는 HTTP 의 Stateless 특성에 대해 반드시 중요하게 생각해야합니다.&lt;/p&gt;
&lt;p&gt;정말 &lt;strong&gt;동시간대에 발생하는 대용량의 트래픽에 대해 처리한다면, 앞서 살펴본 비연결성(Connectionless) 이 정말 필요가 없어집니다.&lt;/strong&gt; 예들들어 저녁 6시에 1000명 치킨 할인 이벤트가 있다면, 해당 서비스에 수많은 동시 접속자가 발생할 수 있고 비연결성의 특징을 잘 살린다고 한들 의미가 없습니다.&lt;/p&gt;
&lt;p&gt;저희 서버 개발자는 어떻게든 머리를 쥐어짜서라도 &lt;strong&gt;Stateless의 특성을 잘 살려서, 대용량의 트래픽이 오더라도 서버를 그에 알맞게 확장시켜서 처리하도록 해야합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;예를들어, 로그인 페이지와 같이 정적 페이지를 서비스에 넣어서 순수한 HTML 파일만 띄우게하고 사용자들이 이벤트 참여방법을 읽어보도록 관심을 끌면서 이벤트 참여버튼을 동시에 누르는 사람을 최대한 줄이도록 하는 방식으로 트래픽을 최대한 줄일 수 있습니다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[HTTP Status Code 300, Redirection 과 PRG 패턴]]></title><description><![CDATA[지난 포스팅에 이어서 HTTP 의 300번대 상태코드에 대해 알아보겠습니다.
그리고 이에 꼭 아셔야할 이론인 Redirection 과 PRG 패턴이 무엇인지 살펴봅시다. 3xx (Redirection…]]></description><link>https://haon.site/haon/http/300-prg/</link><guid isPermaLink="false">https://haon.site/haon/http/300-prg/</guid><pubDate>Wed, 16 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;지난 포스팅에 이어서 HTTP 의 300번대 상태코드에 대해 알아보겠습니다.
그리고 이에 꼭 아셔야할 이론인 Redirection 과 PRG 패턴이 무엇인지 살펴봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3xx-redirection&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3xx-redirection&quot; aria-label=&quot;3xx redirection permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3xx (Redirection)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;클라이언튿가 보낸 요청을 완료하려면 서버에서 아직 추가적인 작업이 필요해서, 다시 클라이언트에게 보내는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;즉, 요청을 완료하기 위해 유저 에이전트에서 아직 추가 조치가 필요함을 의미합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;유저 에이전트란?&lt;/h4&gt;
&lt;p&gt;유저 에이전트란 클라이언트 프로그램, 즉 주로 웹브라우저를 통칭합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;리다이렉션이란-redirection--301-moved-permanently&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98%EC%9D%B4%EB%9E%80-redirection--301-moved-permanently&quot; aria-label=&quot;리다이렉션이란 redirection  301 moved permanently permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리다이렉션이란? (redirection) &amp;#x26; 301 Moved Permanently&lt;/h2&gt;
&lt;p&gt;저희희는 3xx 번대 상태코드에 포함된 개념인 redirection 에 대해 제대로 이해하고 넘어갈 필요가 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;웹 브라우저는 3xx 번대 &lt;strong&gt;응답(Response)의 결과에 Location 헤더가 있다면, Location 위치로 자동 이동(redirect)합니다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;즉, 리다이렉션 == location 이라는 URL 로 자동 이동하는 행위&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예를들어 기존 이벤트 페이지로 &quot;/event&quot; 라는 url 을 사용하고, 새로운 이벤트 페이지 url 로 &quot;/new-event&quot; 라는 것을 사용한다고 해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/1ec4902c-6e92-4d83-bfbf-aabcba6b6ba9/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;만일 new-event 라고 url 이 바뀐것을 모르고 기존 url 로 이벤트 페이지에 접속하려는 사용자들은 &quot;/event&quot; 를 검색해서 들어온다고 해봅시다.&lt;/p&gt;
&lt;p&gt;그러면 서버는 사용자(클라이언트)에게 더 이상 &quot;/event&quot; 라는 &lt;strong&gt;기존의 경로는 더 이상 사용하지 않고, 새로운 경로인 &quot;/new-event&quot; 를 사용한다고 알려줄 수 있습니다.&lt;/strong&gt; 이떄 사용하는 상태코드가 바로 &lt;strong&gt;301 상태코드 (301 Moved Permanently)&lt;/strong&gt; 입니다.&lt;/p&gt;
&lt;p&gt;아무튼, 301 상태코드와 함께 Location 헤더에 &quot;/new-event&quot; 라는 새로운 경로를 응답값으로 함께 실어서 클라이언트에게 보내줍니디.&lt;/p&gt;
&lt;p&gt;그러면 &lt;strong&gt;클라이언트(웹 브라우저)는 301번 상태코드임을 확인하고, Location 헤더 필드 부분을 확인&lt;/strong&gt;합니다. 그러고 Location 헤더 필드가 비어있지 않고 어떤 url 경로 값이 들어있다면, &lt;strong&gt;부여된 Location 경로 url 로 자동으로 리다이렉션합니다.&lt;/strong&gt; 즉, 자동으로 Location 경로로 GET 요청 서버에게 보내서 그에 대한 새로운 응답과 HTML 파일을 제공 받습니다. 이로써 새로운 웹페이지가 열리게 되는 것입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;즉, 사용자 입장에서는 옛날의 URL 로 GET 요청을 보냈지만 자동으로 새로운 URL 경로로 바뀌면서(Redirection 하면서) 해당 웹페이지로 접속할 수 있게 됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;사용자 입장에서는 자동으로 URL 이 입력되고 새로운 URL 로 리다이렉션 되는것이 너무 처리속도가 빨라서 인식을 못할 정도임!&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;리다이렉션의-종류&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98%EC%9D%98-%EC%A2%85%EB%A5%98&quot; aria-label=&quot;리다이렉션의 종류 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리다이렉션의 종류&lt;/h2&gt;
&lt;p&gt;이러한 리다이렉션은 크게 3가지의 종류로 구분할 수 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;영구 리다이렉션 : 특정 리소스의 URI 가 영구적으로 이동&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;일시 리다이렉션 : 일시적인 잠깐 URL 를 변경&lt;/li&gt;
&lt;li&gt;특수 리다이렉션 : 결과 대신 캐시를 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;cf) 영구 리다이렉션은 실무에서 잘 사용하지 않으니, 일시 리다이렉션을 중점으로 학습하시는 것을 권장드립니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;영구 리다이렉션&lt;/h4&gt;
&lt;p&gt;특정 리소스의 URI 가 영구적으로 변경되었음을 의미합니다.
직전에 예시로 말씀드린 리다이렉션이 이에 해당합니다. 예를들어 URL 이 &quot;/members-event&quot; 에서
&quot;/users-event&quot; 로 새롭게 변경된 경우입니다.&lt;/p&gt;
&lt;h4&gt;일시 리다이렉션&lt;/h4&gt;
&lt;p&gt;일시적으로 URI 를 잠깐동안 변경할 떄 사용합니다.
예를들어서 주문완료 후에 주문 내역 화면으로 일시적으로 잠깐 이동할떄 사용됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이와 관련해 &lt;strong&gt;RPG(Post/Redirect/Get) 이라는 패턴&lt;/strong&gt;이 자주 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;특수 리다이렉션&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;결과 대신 캐시를 사용하는 것을 의미&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;클라이언트에 캐시가 있는데 이 캐시가 만료된 것 같을떄, 클라이언트가 서버에게 이 캐시가 만료가 된것인지 요청을 보내고, 서버가 캐시 만료기간과 관련한 정보를 클라이언트에게 넘겨주는 방식입니다.&lt;/p&gt;
&lt;p&gt;이떄 클라이언트에게 캐시를 더 사용해도 되는지 안되는지를 알려주면서, 캐시가 아직 만료가 안되서 다운로드 받지 않아도 되는경우에 캐시에서 다시 조회하라고 클라이언트에게 서버가 응답을 보내는 방식입니다. 즉 결과 대신에 캐시를 사용하라고 알려주는 것&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;영구-리다이렉션--301-308&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%81%EA%B5%AC-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98--301-308&quot; aria-label=&quot;영구 리다이렉션  301 308 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;영구 리다이렉션 : 301, 308&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;리소스의 URI가 영구적으로 이동했다고 알려주는 것입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;cf) 영구 리다이렉션은 실무에서 잘 사용하지 않으니, 일시 리다이렉션을 중점으로 학습하시는 것을 권장드립니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;301-moved-permanently&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#301-moved-permanently&quot; aria-label=&quot;301 moved permanently permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;301 Moved Permanently&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;리디이렉스시에 요청 메소드가 POST, DELETE 등 어떤 요청 메소드로 보내도 &lt;strong&gt;GET 요청으로 자동 변환되고, 요청에 실어서보낸 Body 본문 자체 내용이 완전히 제거될 수도 있습니다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;308-permanent-redirect&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#308-permanent-redirect&quot; aria-label=&quot;308 permanent redirect permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;308 Permanent Redirect&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;301과 기능은 같으나, &lt;strong&gt;리다이렉트시에 요청 메소드와 본문(Body) 내용이 301과 달리 그대로 유지&lt;/strong&gt;되면서 리다이렉트 할떄도 그대로 함께 실어서 보내는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;301-moved-permanently-의-문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#301-moved-permanently-%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;301 moved permanently 의 문제점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;301 Moved Permanently 의 문제점&lt;/h3&gt;
&lt;p&gt;아래처럼 POST 요청과 함꼐 &quot;name=hello&amp;#x26;age=20&quot; 라는 메시지를 Body에 실어서 보냈습니다. 그런데 리다이렉션이 된다면 기존의 POST 요청이 자동으로 GET 요청을 변환되고, Body 에 존재하던 내용이 전부 사라지고 &quot;/new-event&quot; 페이지를 새롭게 브라우저가 띄워주는 방식입니다.&lt;/p&gt;
&lt;p&gt;여기서 문제점은, 이벤트 등록을 하기위해 POST 요청을 보냈다면 결과적으로 GET 요청으로 변하게 됩니다. 결국엔 사용자는 새로운 이벤트 페이지에서 이벤트 등록을 위한 정보들을 새롭게 또 다시 입력해야 하는 번거로운 상황이 발생하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5bf1f85a-94b7-4e38-83f5-efd1c8915908/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;308--301-의-문제점을-해결&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#308--301-%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90%EC%9D%84-%ED%95%B4%EA%B2%B0&quot; aria-label=&quot;308  301 의 문제점을 해결 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;308 : 301 의 문제점을 해결&lt;/h3&gt;
&lt;p&gt;반대로 308 은 리다이렉션을 해도 POST 요청을 계속 유지하고, Body 의 내용도 삭제시키지 않고 그대로 새로운 리디이렉션하는 페이지에 전달해준다고 했었죠? 이로써 301의 번거로움을 308 로 해결이 가능해집니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;일시적인-리다이렉션--302-307-303&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%BC%EC%8B%9C%EC%A0%81%EC%9D%B8-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98--302-307-303&quot; aria-label=&quot;일시적인 리다이렉션  302 307 303 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;일시적인 리다이렉션 : 302, 307, 303&lt;/h2&gt;
&lt;p&gt;일시적인 리다이렉션은 &lt;strong&gt;리소스의 URI가 일시적으로 변경&lt;/strong&gt;되는 것입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;302 Found : &quot;제거&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;리다이레트시에 **요청 메소드가 GET 으로 변하고, Body(본문)의 내용이 &quot;제거&quot;**될 수도 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;307 Temporary Redirect : &quot;유지&quot;
&lt;ul&gt;
&lt;li&gt;302과 기능은 같습니다.&lt;/li&gt;
&lt;li&gt;리다이렉트시에 **요청 메소드와 Body 의 내용을 그대로 &quot;유지&quot;**시킵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;303 See Other : &quot;GET 으로 변경&quot;
&lt;ul&gt;
&lt;li&gt;302과 기능은 같습니다.&lt;/li&gt;
&lt;li&gt;리다이렉트시에 **요청 메소드가 &quot;GET 으로 변경&quot;**됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;prg--postredirectget&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#prg--postredirectget&quot; aria-label=&quot;prg  postredirectget permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PRG : Post/Redirect/Get&lt;/h2&gt;
&lt;p&gt;일시적인 리다이렉션이 꼭 반드시 쓰이는 상황이있습니다. 그 상황은 바로 PRG 입니다.&lt;/p&gt;
&lt;p&gt;예를들어, 주문페이지에서 원하는 상품을 POST 요청으로 주문하면 POST 요청으로 인해 원하는 뎅터가 넘어갈 겁니다. 그러고 웹 브라우저를 새로고침하면, 서버에 중복해서 주문에 또 들어갈 수도 있습니다.&lt;/p&gt;
&lt;p&gt;즉, 내가 요청했던 POST 요청이 새로고침을 하면 또 POST 요청이 들어갈 수 있는겁니다. 이를 해결하려면 PRG 패턴을 사용해야합니다.&lt;/p&gt;
&lt;h4&gt;상황가정 : 주문을 잘 못하는 경우(POST 요청을 새로고침에서 또 보내는 경우)&lt;/h4&gt;
&lt;p&gt;예를들어 아래처럼 &quot;/order&quot; 라는 페이지에 POST 요청으로 주문을 요청하는 상황을 생각해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e5b5d0e0-c80a-4add-b821-2f7c8c24a85e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;사용자가 마우스를 1개 구매하는 상황을 가정하면, Body에 &quot;itemid=mouse&amp;#x26;count=1&quot; 과 같은 내용을 실어서 주문 내용을 보낼겁니다. 그러면 주문 데이터베이스에 상품을 1개 저장하고, 동시에 200 ok 라는 응답을 사용자에게 보내게 됩니다.&lt;/p&gt;
&lt;p&gt;만일 실수로 새로고침을 한다면, 마지막 요청을 기준으로 요청을 처리하므로 사용자가 의도치않게 한번 더 주문이 들어가게 될 수 있습니다. 이럴 때 PRG 패턴을 사용하면 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;prg-패턴-적용하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#prg-%ED%8C%A8%ED%84%B4-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; aria-label=&quot;prg 패턴 적용하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PRG 패턴 적용하기&lt;/h3&gt;
&lt;p&gt;위와 같이 POST로 주문후에 새로 고침으로 인한 중복 주문을 방지하기 위해, &lt;strong&gt;클라이언트에서 POST 로 주문후에 주문 결과 화면을 GET 메소드로 리다이렉트 시켜서 해결&lt;/strong&gt;할 수 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;즉, 주문 완료후에 주문 완료 결과 페이지로 넘어가도록(리다이렉트) 되도록 하면 됩니다.
=&gt; 새로고침을 해도 주문 결과화면을 GET 으로 조회하도록 해서 &lt;strong&gt;중복으로 주문하는 POST 대신에, 주문 결과 화면만 GET 으로 다시 요청&lt;/strong&gt; 하도록 구현하면 됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/05d0896a-c076-427a-ab76-a44cf16ff1a7/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;보듯이 &lt;strong&gt;주문을 완료한 후에 클라이언트에 대한 응답으로 200 OK 가 아닌, 302 Found 를 응답으로 주면 됩니다.&lt;/strong&gt; (또는 303 See Other 를 줘도 됩니다!) 동시에 리다이렉션할 Location 정보도 응답을 주면 됩니다.&lt;/p&gt;
&lt;p&gt;그러면 클라이언트는 302 를 확인하고 새로운 URL 로 GET 요청을 통해 리다이렉션을 하면서, 주문 완료 페이지를 띄어주면 됩니다. 이때 주문 완료 페이지에 주문 데이터를 띄어주기위해, 직전에 주문한 내역을 DB 로 부터 조회해서 이 정보를 기반으로 HTML 화면을 구성하면 됩니다. 즉 200 OK 와 함께 HTML 내용을 클라이언트에 보내주면 됩니다.&lt;/p&gt;
&lt;p&gt;이 상황에서, 즉 주문 결과화면에서 새로고침을 하면 POST 가 아닌 GET 요청이 들어오면서 주문 내역을 보여주는 화면으로 계쏙 GET 요청이 들어오게 되는 방식이 된것입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PRG 과정 이후에 리다이렉트를 해도, URL 이 이미 POST 에서 GET 으로 리다이렉트 되어서 새로고림을 하더라도 GET 으로 주문 결과 화면만 조회됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;redirection-302-307-303-중-어떤것을-써야할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redirection-302-307-303-%EC%A4%91-%EC%96%B4%EB%96%A4%EA%B2%83%EC%9D%84-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;redirection 302 307 303 중 어떤것을 써야할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redirection 302, 307, 303 중 어떤것을 써야할까?&lt;/h3&gt;
&lt;p&gt;역사를 잠시 살펴보자면, 모호한 302 을 대신하는 명확한 307, 303이 등장했습니다.
302는 GET 요청으로 반드시 꼭 바꿔주는 것이 아니라, 간혹 GET 으로 바꿔주지 않는 상황이 있기 떄문입니다.&lt;/p&gt;
&lt;p&gt;따라서 307, 303 을 권장하지만, 그러나 현실적으로는 이미 많은 애플리케이션 라이브러리들이 302를 기본값으로 사용하고 있습니다. &lt;strong&gt;따라서 자동 리다이렉션시에 GET 으로 변해도 되면 그냥 302를 사용해도 큰 문제가 없습니다.&lt;/strong&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[HTTP의 비상태성(Stateless)]]></title><description><![CDATA[안녕하세요, 이번 포스팅에서는 HTTP 의 비상태성이라는 특징에 대해 알아보겠습니다.
비상태성, 즉 Stateless 를 이해하기 위해선 Stateful 과의 차이를 비교해봐야 할텐데, 이를 자세히 살펴보는 방식으로 진행해보겠습니다. HTTP…]]></description><link>https://haon.site/haon/http/stateless/</link><guid isPermaLink="false">https://haon.site/haon/http/stateless/</guid><pubDate>Wed, 16 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;안녕하세요, 이번 포스팅에서는 HTTP 의 비상태성이라는 특징에 대해 알아보겠습니다.
비상태성, 즉 Stateless 를 이해하기 위해선 Stateful 과의 차이를 비교해봐야 할텐데, 이를 자세히 살펴보는 방식으로 진행해보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;http의-특성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http%EC%9D%98-%ED%8A%B9%EC%84%B1&quot; aria-label=&quot;http의 특성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP의 특성&lt;/h2&gt;
&lt;p&gt;HTTP 의 특징의 크게 3가지를 가진다고 보시면됩니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;무상태 프로토콜 : Stateless&lt;/li&gt;
&lt;li&gt;비 연결성 : Connectionless&lt;/li&gt;
&lt;li&gt;HTTP 메시지 관련&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;이들에 대해 자세히 알아보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;무상태-프로토콜--stateless&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AC%B4%EC%83%81%ED%83%9C-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C--stateless&quot; aria-label=&quot;무상태 프로토콜  stateless permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;무상태 프로토콜 : Stateless&lt;/h2&gt;
&lt;p&gt;HTTP 의 중요한 특징중 하나는 무상태 프로토콜을 지향한다는 것입니다. 이를 영어로 표현하면 Stateless 라고 표현합니다. 이게 무슨 말이냐면, &lt;strong&gt;서버가 클라이언트가 보내준 정보를 저장 하지 않는다. 즉, 상태를 계속해서 보존하지 않는다&lt;/strong&gt;라는 것입니다.&lt;/p&gt;
&lt;p&gt;Stateless 의 뜻을 잘 이해하기 위해서, 저희는 Stateful 과 Stateless 의 차이에 알 필요가
있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;stateful-vs-stateless&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stateful-vs-stateless&quot; aria-label=&quot;stateful vs stateless permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Stateful VS Stateless&lt;/h2&gt;
&lt;p&gt;설명에 앞서, 미리 정리를 해보면 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Stateless&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트가 보내준 정보를 서버에 저장(유지)하지 않고, 보내준 정보를 기반으로 일련의 과정들을 처리후에 클라이언트에게 응답해주는 방식&lt;/li&gt;
&lt;li&gt;장점 : 스케일 아웃 - 수평 확장에 있어서 유리 (동일한 기능을 수행하는 수많은 서버로 확장가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stateful 은 항상 같은 상태의 정보를 들고있는 상태의 서버가 지속되어야 합니다..
만일 클라이언트가 A가 서버에게 여러 전자기기중 &quot;노트북&quot;을 사겠다는 정보를 보내고, 뒤이어서 &quot;2개&quot;를 구매하겠다는 정보를 보냈다고 가정해봅시다. 이때 여러 서버중에서 서버1이 &quot;노트북&quot;, &quot;2개&quot; 라는 키워드를 저장 및 유지하고 있는 상태가 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ff7e2376-b31a-43dc-9105-2331b05fb8b1/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그런데 만일 서버1이 터졌다면, &quot;노트북&quot;, 그리고 &quot;2개&quot; 에 대한 정보를 잃게될겁니다.&lt;/p&gt;
&lt;p&gt;클라이언트는 노트북 2개에 대한 가격을 알수가없어서, 클라이언트는 처음부터 다시 결제 과정을 진행해야할겁니다.&lt;/p&gt;
&lt;p&gt;여기서 알수 있는것은, &lt;strong&gt;Stateful하게 서버를 유지하는 것은 힘들다&lt;/strong&gt;는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/01f4d8df-ad5f-4b5e-acfd-ef979d53de24/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;반면 무상태(Stateless) 에서는 애초에 맨처음부터 클라이언트가 &quot;노트북&quot;을 &quot;2개&quot; 사겠다는 정보를 가지고 요청을 보냅니다. 그러면 서버는 노트북, 2개에 대한 정보를 보관(유지)하지 않습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/0640a5fa-8262-4f10-88a7-c84426b1b5ba/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이 무상태, Stateless 의 장점은 뭘까요?
만일 서버1이 터지더라도, 서버2가 대신해서 클라이언트로부터 받은 &quot;노트북&quot;, &quot;2개&quot; 라는 정보를 기반으로 결제를 진행해주면 되는 것입니다. 이는 Stateless 특징으로 인해 가능해진 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/fcb7791b-d4e2-44ec-a6e4-bd9027b13ec0/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;또한 이 Stateless 는 스케일 아웃, 즉 수평확장에 있어서 정말 유리합니다. 같은 기능을 하는 서버를 여러개로 확장시킬 수 있는 것이죠.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/8a1b0cf8-6821-42c1-bc31-72f1e37ed8bb/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;stateless의-한계-cookie-과-session쿠키-세션&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stateless%EC%9D%98-%ED%95%9C%EA%B3%84-cookie-%EA%B3%BC-session%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98&quot; aria-label=&quot;stateless의 한계 cookie 과 session쿠키 세션 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Stateless의 한계, Cookie 과 Session(쿠키&amp;#x26; 세션)&lt;/h2&gt;
&lt;p&gt;그런데 이런 Stateless 에도 한계가 있습니다.
모든 것을 무상태로 설계할 수 잇는 경우도 있겠지만, 반대로 무상태로 설계할 수 없는 경우도 존재합니다.&lt;/p&gt;
&lt;h3 id=&quot;1-로그인-상태여부--쿠키cookie-세션session&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%83%81%ED%83%9C%EC%97%AC%EB%B6%80--%EC%BF%A0%ED%82%A4cookie-%EC%84%B8%EC%85%98session&quot; aria-label=&quot;1 로그인 상태여부  쿠키cookie 세션session permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 로그인 상태여부 : 쿠키(Cookie), 세션(Session)&lt;/h3&gt;
&lt;p&gt;대표적으로, 상태를 유지해야 하는 경우가 바로 &quot;로그인&quot;입니다. &lt;strong&gt;서버에 현재 로그인되어 있는 유저에 대한 정보를 계속 유지해야 하는데, Stateless 의 특성상 서버에 유저에 대한 정보를 계속 유지를 시키지 못합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;즉, 로그인한 사용자에 대해 로그인 했다는 상태를 서버에 계속 유지시켜야하는데, statesless 특성상 서버에 로그인 상태여부를 저장하지 못하는 것입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;그래서, 일반적으로 &lt;strong&gt;브라우저의 쿠키와 서버 세션등을 사용해서 상태를 유지&lt;/strong&gt;하는 방식으로 로그인 상태여부를 유지시켜줄 수 있습니다. 이렇게 했을떄 세션정보가 날라가거나, 세션 정보가 만료가되어서 죽어버린다면 로그인이 해제되는 방식인 것이죠.&lt;/p&gt;
&lt;h3 id=&quot;2-stateful-를-최소한으로-사용하자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-stateful-%EB%A5%BC-%EC%B5%9C%EC%86%8C%ED%95%9C%EC%9C%BC%EB%A1%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EC%9E%90&quot; aria-label=&quot;2 stateful 를 최소한으로 사용하자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Stateful 를 최소한으로 사용하자.&lt;/h3&gt;
&lt;p&gt;또한 상태 유지는 최소한으로 사용하며 꼭 필요한 경우에만 어쩔 수 없이 사용해야 합니다. 방금 말씀드린 로그인 관련 이슈에 대해서 상태 유지를 시켜야하므로, 이럴떄만 사용해야 하겠죠?&lt;/p&gt;
&lt;h3 id=&quot;3-서버-입장에서-한번에-많은-데이터를-수용해야한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%EC%84%9C%EB%B2%84-%EC%9E%85%EC%9E%A5%EC%97%90%EC%84%9C-%ED%95%9C%EB%B2%88%EC%97%90-%EB%A7%8E%EC%9D%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%88%98%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;3 서버 입장에서 한번에 많은 데이터를 수용해야한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 서버 입장에서 한번에 많은 데이터를 수용해야한다.&lt;/h3&gt;
&lt;p&gt;마지막으로 클라이언트로 부터 서버는 정말 많은 데이터를 받아야합니다.&lt;/p&gt;
&lt;p&gt;Stateful 의 경우는 클라이언트로 부터 하나씩 차근차근 &quot;노트북&quot;, &quot;2개&quot; 라는 정보를 받아서 서버에 계속 저장하고 유지해나가는 방식입니다.&lt;/p&gt;
&lt;p&gt;그러나 Stateless 의 경우는 클라이언트로 부터 한번에 &quot;노트북&quot;, &quot;2개&quot; 라는 정보를 받아야합니다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[HTTP 메소드의 멱등성과 캐시 개념]]></title><description><![CDATA[이번 포스팅에서는 HTTP 메소드의 특성인 안전, 멱등(Idempotent), 캐시(Cacheable) 에 대해 알아보겠습니다. 안전(Safe) 여러번 해당 메소드를 호출해도 리소스(resource…]]></description><link>https://haon.site/haon/http/imponents-caching/</link><guid isPermaLink="false">https://haon.site/haon/http/imponents-caching/</guid><pubDate>Wed, 16 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이번 포스팅에서는 HTTP 메소드의 특성인 안전, 멱등(Idempotent), 캐시(Cacheable) 에 대해 알아보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;안전safe&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%95%88%EC%A0%84safe&quot; aria-label=&quot;안전safe permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;안전(Safe)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;여러번 해당 메소드를 호출해도 리소스(resource) 를 변경하지 않는것을 안전하다&lt;/strong&gt;라고 표현하는 것입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;GET 메소드 : 안전한 메소드 (리소스를 변경하지 않으므로)&lt;/li&gt;
&lt;li&gt;POST, PUT, PATCH, DELETE 메소드 : 안전하지 않는 메소드 (리소스를 변경시키므로)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;즉 여러번 메소드를 여러번 호출하면 안전하지 않다는 것으로, GET 을 제외한 POST, PUT, DELETE 와 같은 메소드들을 호출하는 것은 안전하지 않다는 것입니다. 반대로 여러번 호출해도 변경사항이 없는 것을 안전하다고 표현합니다.&lt;/p&gt;
&lt;p&gt;그런데 아래와 같은 질문을 할 수 있다.&lt;/p&gt;
&lt;p&gt;계속 호출을해서 서버에 로그같은 것들이 쌓여서 터져서 서버에 장애가 발생할 수 있는데, 이에 대해 안전이라는 개념은 고려를 하지 않습니다. &lt;strong&gt;즉, 안전이라는 개념은 해당 리소스가 변하냐 변하지 않느냐만을 고려하지, 세부적인 사항들은 고려하지 않습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;멱등idempotent&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%B1%EB%93%B1idempotent&quot; aria-label=&quot;멱등idempotent permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;멱등(Idempotent)&lt;/h2&gt;
&lt;p&gt;멱등이라는 것은 &lt;strong&gt;한번 호출하든, 수백번 여러번 호출하든 결과가 계속 똑같은&lt;/strong&gt;것을 의미합니다.
수학에서 많이 보셨을 개념인데, 기호로 표현하자면 &lt;strong&gt;&quot;f(f(x)) = f(x)&quot;&lt;/strong&gt; 가 되는 것입니다.&lt;/p&gt;
&lt;p&gt;저희는 멱등인 메소드와, 멱등이 아닌 메소드로 구분지을 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;1멱등-메소드--get-put-delete&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1%EB%A9%B1%EB%93%B1-%EB%A9%94%EC%86%8C%EB%93%9C--get-put-delete&quot; aria-label=&quot;1멱등 메소드  get put delete permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.멱등 메소드 : GET, PUT, DELETE&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;GET : 한번 조회하든, 두번 조회하든 같은 결과가 조회되므로 멱등입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;PUT : 결과를 대체한다. 따라서 같은 요청을 여러번 해도 최종 결과는 같다.&lt;/li&gt;
&lt;li&gt;DELETE : 결과를 삭제한다. 같은 요청을 여러번 해도 리소스가 삭제된 결과는 똑같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;PUT 이 왜 멱등 메소드일까?&lt;/h4&gt;
&lt;p&gt;여기서 PUT 은 왜 멱등 메소드일까요?
PUT 은 기존것을 아예 날려버리고 내가 새롭게 보낸것으로 내용을 새롭게 덮어버리는 특성을 지닙니다.&lt;/p&gt;
&lt;p&gt;PUT 요청을 첫번째로 보낼경우 그에 맞춰서 데이터가 생성이 될겁니다. 그 뒤로 2번째 PUT 요청을 인풋값을 동일하게 맞추어서 보내면 첫번쨰와 두번쨰 결과가 동일할겁니다. 이렇게 &lt;strong&gt;동일한 인풋을 기반으로 계속 PUT 요청을 수백번 보내도, 결과적으로 생성되는 데이터 결과는 동일합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;DELETE 는 왜 멱등 메소드일까?&lt;/h4&gt;
&lt;p&gt;DELETE 도 생각해봅시다.
원하는 리소스에 대해 삭제하는 DELETE 요청을 보내면 리소스가 삭제가 될겁니다. 그런데 또 동일하게 DELETE 를 요청하면, 그대로 리소스는 이전의 DELETE 요청과 동일하게 리소스가 삭제된 상태가 동일하게 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;2멱등이-아닌-메소드--post&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2%EB%A9%B1%EB%93%B1%EC%9D%B4-%EC%95%84%EB%8B%8C-%EB%A9%94%EC%86%8C%EB%93%9C--post&quot; aria-label=&quot;2멱등이 아닌 메소드  post permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.멱등이 아닌 메소드 : POST&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;POST : 멱등이 아니다. 결제 기능을 생각해보면, 1번 결제하는것과 100번 결제하는 것은 다르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;POST 는 멱등이 아닙니다. 결제 기능을 POST 요청으로 처리하는 것을 생각해봅시다.
앨범을 구매하는 결제를 1번하는 것과 2번 하는 결과가 동일할까요? 당연히 아닙니다. POST 요청으로 결제를 1번한다면 1개의 앨범이 생성되고, 2번한다면 2개의 앨범이 생성됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;멱등을-언제-사용하는가--자동복구-메커니즘&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%B1%EB%93%B1%EC%9D%84-%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80--%EC%9E%90%EB%8F%99%EB%B3%B5%EA%B5%AC-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98&quot; aria-label=&quot;멱등을 언제 사용하는가  자동복구 메커니즘 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;멱등을 언제 사용하는가? : 자동복구 메커니즘&lt;/h2&gt;
&lt;p&gt;멱등이라는 개념을 언제 활용하면 좋을지 생각해본다면, &lt;strong&gt;동일한 메소드를 또 다시 호출해야하는
상황에서 호출해도 상관없는지 여부를 판단할떄 활용&lt;/strong&gt;할 수 있습니다.&lt;/p&gt;
&lt;p&gt;예륻들어 자동복구 메커니즘을 생각해봅시다. DELETE 를 호출했는데 서버에서 응답이 없는경우, DELETE 메소드가 수행이 잘 된것인지 판단할 떄 클라이언트가 또 다시 한번 DELETE 를 호출해도 딥니다.&lt;/p&gt;
&lt;p&gt;왜냐하면 DELELE 메소드는 멱등 메소드로써, 똑같은 요청을 다시해도 어짜피 동일한 결과가 나와서 결과가 이전 DELETE 메소드 호출 결과와 달라지지 않기 떄문입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;멱등의-유의사항--외부요인으로-리소스가-변경된다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%B1%EB%93%B1%EC%9D%98-%EC%9C%A0%EC%9D%98%EC%82%AC%ED%95%AD--%EC%99%B8%EB%B6%80%EC%9A%94%EC%9D%B8%EC%9C%BC%EB%A1%9C-%EB%A6%AC%EC%86%8C%EC%8A%A4%EA%B0%80-%EB%B3%80%EA%B2%BD%EB%90%9C%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;멱등의 유의사항  외부요인으로 리소스가 변경된다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;멱등의 유의사항 : 외부요인으로 리소스가 변경된다면?&lt;/h2&gt;
&lt;p&gt;멱등을 더 정확히 정의하면, 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;멱등은 외부 요인으로 인해 중간에 갑자기 리소스가 변경되는 것 까지는 멱등에서 고려하지 않는다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;내가 요청한 것을 또 내가 다시 요청했을 떄만 결과가 동일한것을 멱등이라 칭합니다. (타인이 리소스를 변경해서 리턴된 결과가 달라지는 것은 따지지 않는다)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;상황을 가정해봅시다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;사용자1이 GET 을 요청했을 때 나오는 정보가 age:20 이라고 해봅시다.&lt;/li&gt;
&lt;li&gt;그런데, 사용자2가 (외부에서) PUT 요청을 통해 age 필드를 30으로 수정했다고 해봅시다.&lt;/li&gt;
&lt;li&gt;그러고 사용자1이 다시 GET 을 요청한다면 age:20 이 아닌, age:30 이 결과로 나올겁니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;=&gt; 이렇게 중간에 내가아닌 외부요인으로 인해 리소스가 변경되는 상황은 멱등에서 제외합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;캐시가능cacheable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BA%90%EC%8B%9C%EA%B0%80%EB%8A%A5cacheable&quot; aria-label=&quot;캐시가능cacheable permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;캐시가능(Cacheable)&lt;/h2&gt;
&lt;p&gt;응답 결과 리소스를 캐시에서 사용 가능한지를 생각해봅시다.&lt;/p&gt;
&lt;p&gt;만일 대용량의 큰 데이터(리소스)를 웹브라우저에게 보내달라고 요청을 하는 상황을 가정해봅시다.
한번 요청하고나면, 그 다음으로 또 그렇게나 큰 데이터 소스를 달라고 다시 재요청하고 인터넷으로부터 다운로드 받는것은 비효율적일 것이다.&lt;/p&gt;
&lt;p&gt;그래서 &lt;strong&gt;내 로컬에 응답 결과 리소스를 저장하고 있는 것이 캐시라고 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;GET, POST, PATCH, HEAD 가 캐시가 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;그런데 실제 실무에서는 GET, HEAD 정도만 캐시로 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;=&gt; 실제로는 GET, HEAD 만 캐시로 사용한다. 왜냐면 캐시를 하려면 똑같은 리소스에 대해서 캐시 KEY 가 정확히 맞아야한다. POST, PATCH 는 본문(Body) 내용까지 캐시 키로 고려해야 하는데, 구현이 쉽지 않아서 그렇습니다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[HTTP Status Code]]></title><description><![CDATA[안녕하세요, 이번 포스팅에서는 HTTP 의 상태코드에 대해 알아보겠습니다.
설명에 앞서, 300번대 상태코드는 내용이 길고 많이 때문에 따로 포스팅을 진행하도록 하겠습니다. HTTP 상태코드(Status Code)란? HTTP…]]></description><link>https://haon.site/haon/http/status-code/</link><guid isPermaLink="false">https://haon.site/haon/http/status-code/</guid><pubDate>Wed, 16 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;안녕하세요, 이번 포스팅에서는 HTTP 의 상태코드에 대해 알아보겠습니다.
설명에 앞서, 300번대 상태코드는 내용이 길고 많이 때문에 따로 포스팅을 진행하도록 하겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;http-상태코드status-code란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-%EC%83%81%ED%83%9C%EC%BD%94%EB%93%9Cstatus-code%EB%9E%80&quot; aria-label=&quot;http 상태코드status code란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP 상태코드(Status Code)란?&lt;/h2&gt;
&lt;p&gt;HTTP 상태코드는 클라이언트가 서버로 요청을 보내면, &lt;strong&gt;그 요청이 잘 처리되었는지 아니면 문제가 있는지 등의 상태를 Response(응답)을 통해 알려주는 기능&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;http-상태코드의-종류&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#http-%EC%83%81%ED%83%9C%EC%BD%94%EB%93%9C%EC%9D%98-%EC%A2%85%EB%A5%98&quot; aria-label=&quot;http 상태코드의 종류 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HTTP 상태코드의 종류&lt;/h2&gt;
&lt;p&gt;종류는 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;1xx (Informational) : 요청이 수신되어 처리중임을 의미&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;2xx (Successful) : 요청이 정상 처리되었음을 의미&lt;/li&gt;
&lt;li&gt;3xx (Redirection) : 요청을 완료하려면 아직 추가적인 행동이 필요함을 의미&lt;/li&gt;
&lt;li&gt;4xx (Client Error) : 클라이언트의 오류, 또는 잘못된 문법등으로 인해 서버가 요청을 수행할 수 없음을 의미&lt;/li&gt;
&lt;li&gt;5xx (Server Error) : 서버 오류로, 서버가 정상적인 요청을 처리하지 못함을 의미&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;만약-모르는-상태의-코드가-나온다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%8C%EC%95%BD-%EB%AA%A8%EB%A5%B4%EB%8A%94-%EC%83%81%ED%83%9C%EC%9D%98-%EC%BD%94%EB%93%9C%EA%B0%80-%EB%82%98%EC%98%A8%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;만약 모르는 상태의 코드가 나온다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;만약 모르는 상태의 코드가 나온다면?&lt;/h3&gt;
&lt;p&gt;미래에 새롭게 정의된 HTTP 상태코드가 나온다면, 디테일한 부분까지 이해할 필요를 하실필요는 없고 그냥 몇백번대의 코드인지만을 보시고 그에따라 코드를 처리해주시면 됩니다.&lt;/p&gt;
&lt;p&gt;예를들어 299번과 같은 잘 모르는 상태코드를 보더라도, 2백번대 코드인것만을 인지하고 해석해서 처리해주시면 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1xx-informational&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1xx-informational&quot; aria-label=&quot;1xx informational permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1xx (Informational)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;요청이 수신되어 처리중이라는 뜻읹데, &lt;strong&gt;거의 사용하지 않으므로 스킵하셔도 됩니다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2xx&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2xx&quot; aria-label=&quot;2xx permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2xx&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;클라이언트가 보낸 요청을 서버에서 정상적으로 잘 처리되었음을 의미&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;200-ok&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#200-ok&quot; aria-label=&quot;200 ok permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;200 OK&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;요청에 대한 처리를 성공했음을 의미합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;201-created&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#201-created&quot; aria-label=&quot;201 created permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;201 Created&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;요청에 성공해서 새로운 리소스가 생성되었음을 의미합니다.&lt;/li&gt;
&lt;li&gt;이때 HTTP 헤더에 Location 이라는 것을 자동으로 넣어줍니다. 생성된 리소스는 응답의 Location 해더 필드로 식별합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;202-accepted&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#202-accepted&quot; aria-label=&quot;202 accepted permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;202 Accepted&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;요청이 접수되었으나, 아직 처리가 완료되지 않았음을 의미합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;활용 ex) 배치를 1시간뒤에 처리해달라는 요청일 경우, 요청을 받자마자 처리를 하나는 것이 아닌 1시간뒤에 처리를 해야합니다. 즉 이 요청을 받았다면 접수는 되었으나 아직 처리를 완료하지 않았을 떄 이러한 상태코드를 받을 수 있는 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cf) 잘 사용하지 않는 경우의 상태코드임!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;204-no-content&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#204-no-content&quot; aria-label=&quot;204 no content permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;204 No Content&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;서버가 요청을 성공적으로 수행했지만, 응답 페이로드 본문에 보낼 데이터가 없는 경우를 의미합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;활용 ex) 문서 편집기에서 save 저장버튼 : save 버튼을 눌러서 문서를 저장한다고 한들, 굳이 어떤 응답값을 받을 필요는 없을 겁니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;4xx-vs-5xx&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4xx-vs-5xx&quot; aria-label=&quot;4xx vs 5xx permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4xx VS 5xx&lt;/h2&gt;
&lt;p&gt;다음으로는 4xx 번대 에러와, 5xx 번대 에러 상태코드에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4xx 번대는 클라이언트가 잘못한 경우, 5xx 번대는 서버에 오류가 있을 때 발생합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;400번대 오류 : 4xx 번대 오류는 클라이언트가 이미 잘못된 요청을 보내고 있기 떄문에, 똑같은 요청을 계속 재시도하더도 계속 실패하는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;500번대 오류 :&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;4xx-client-error&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4xx-client-error&quot; aria-label=&quot;4xx client error permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4xx (Client Error)&lt;/h2&gt;
&lt;p&gt;클라이언트의 요청에 잘못된 문법등으로 인해, 서버가 그 요청 자체를 받아서 수행할 수가 없을 때 발생하는 상태코드&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;즉, &lt;strong&gt;오류의 원인이 클라이언트에게 있는 경우&lt;/strong&gt;입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이때 중요한것이, 4xx 번대 오류는 클라이언트가 이미 잘못된 요청을 보내고 있기 떄문에, 똑같은 요청을 계속 재시도하더도 계속 실패하는 것입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;즉, 400번대 오류는 클라이언트가 제대로 올바른 요청을 보낼 때 까지 서버가 요청을 절대 못받는 상황입니다. (반면 500번대 오류는 클라이언트가 요청을 수정하지 않고, 나중에 똑같은 요청을 다시 보낸다면 성공할 가능성이 있습니다.)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;400-bad-request&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#400-bad-request&quot; aria-label=&quot;400 bad request permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;400 Bad Request&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;클라이언트가 잘못된 요청을 해서 서버가 요청을 처리할 수 없을 경우&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;주로 요청 구문이나, 메시지 등에 오류가 있을떄 발생하는 오류입니다.
&lt;strong&gt;클라이언트는 요청 내용을 다시 검토하고, 보내야합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ex) 요청 파라미터가 잘못되거나, API 스펙이 맞지 않을때(예를들어 문자열을 보내야하는데 숫자를 보낸 경우)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;백엔드 개발자는 API 스펙이 맞지 않는경우들에 대해 400번대 오류를 팅켜주면서 잘 처리를 해줘야한다. 그래야지 프론트가 백엔드에게 탓을 돌리지 않을수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;만일 400번대 오류로 백엔드 쪽에서 잘 처리해주지 않아서 500번대를 상태코드를 리턴하게 한다면, 클라이언트 입장에서는 서버쪽에서 잘못했다고 오해할 수도 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;401-unauthorized&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#401-unauthorized&quot; aria-label=&quot;401 unauthorized permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;401 Unauthorized&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;클라이언트가 해당 리소스에 대한 인증이 필요한 경우. 즉 인증(Authentication) 이 되지 않은경우다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;401 오류가 발생한다면, 응답(Response) 에 WWW-Authenticate 헤더와 함꼐 인증 방법을 설명해줘야 합니다.&lt;/p&gt;
&lt;p&gt;이 상태코드와 관련해 참고하면 좋으실 개념은 아래와 같습니다.&lt;/p&gt;
&lt;h3 id=&quot;인증authentication-인가authorization&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EC%A6%9Dauthentication-%EC%9D%B8%EA%B0%80authorization&quot; aria-label=&quot;인증authentication 인가authorization permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인증(Authentication), 인가(Authorization)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;인증(Authentication) : 본인이 누구인지 확인하는 과정으로, 로그인을 의미합니다. (로그인 자체가 안되면 인증이 안되는 것이다)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;인가(Authorization) : 권한부여 (admin 권한처럼 특정 리소스에 접근할 수 있는 권한. 인증이 있어야 인가가 있을 수 있습니다.)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;로그인할 수 있는 것과, 권한이 있는 것은 다릅니다. &lt;strong&gt;즉 인가라는 것은 로그인을 하고난(인증을 마친) 사용자에 대한 특정 리소스에 접근할 수 있는 권한의 레벨을 의미합니다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;403-forbidden&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#403-forbidden&quot; aria-label=&quot;403 forbidden permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;403 Forbidden&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;서버가 클라이언트로부터 받은 요청을 이해했지만, 승인을 거부한 경우&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;주로 인증 자격 증명은 있지만, &lt;strong&gt;접근 권한이 불충분한 경우에 발생&lt;/strong&gt;합니다.
예를들어 admin 레벨이 아닌 사용자가 로그인은 했지만, admin 만 접근할 수 있는 리소스에 접근하려는 경우에 발생합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;404-not-found&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#404-not-found&quot; aria-label=&quot;404 not found permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;404 Not Found&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;요청 리소스를 찾을 수 없는 경우로, 요청 리소스가 서버에 없는 경우입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;이게 왜 클라이언트쪽의 오류나면,** 서버 입장에서는 이런 리소스 자체가 없는데도 클라이언트가 리소스를 달라고 요청을 보낸 경우**입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예를들어 클라이언트가 url 을 잘못 입력했다면, 서버 입장에서는 나는 이런 리소스(url) 이 없어서 클라이언트에게 제공해줄 수 없다는 것을 의미합니다.&lt;/p&gt;
&lt;p&gt;또는 &lt;strong&gt;클라이언트가 권한이 부족한 리소스에 접근할 떄,&lt;/strong&gt; 403 Forbidden 상태코드를 리턴하는 대신에 &lt;strong&gt;해당 리소스를 숨기고 싶을 떄&lt;/strong&gt; 404 에러를 띄어줄 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;5xx-server-error&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5xx-server-error&quot; aria-label=&quot;5xx server error permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5xx (Server Error)&lt;/h2&gt;
&lt;p&gt;이름 그대로, 서버의 문제로 오류가 발생하 경우입니다.
&lt;strong&gt;서버에 문제가 있기 떄문에 재시도하면, 서버가 복구된다면 가끔씩 성공할 수도 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;백엔드 입장에서는, 왠만하면 &lt;strong&gt;500번대 에러는 절대 발생시키면 안됩니다.&lt;/strong&gt;
**이 에러는 진짜로 서버 내부에 심각한 문제가 터졌을 떄만 500번대 에러를 만들어야 합니다. **&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;예를들어 NullPointerException 예외가 터지거나, 데이터베이스에 접근이 불가능해지거나 하는 경우과 같이 서버에 내부적으로 큰 문제가 발생했을 떄만 500번대 에러를 발생시키도록 해야합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;500-internal-server-error&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#500-internal-server-error&quot; aria-label=&quot;500 internal server error permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;500 Internal Server Error&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;서버 내부 안에 문제가 발생한 경우로, 대부분 백엔드 입장에서 애매한 문제들을 500번대 에러로 발생합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;503-service-unavailable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#503-service-unavailable&quot; aria-label=&quot;503 service unavailable permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;503 Service Unavailable&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;서비스 이용불가 상태&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;서버가 일시적으로 과부화되거나, 또는 일시적으로 예정된 작업으로인해 잠시 요청을 처리할 수 없는 경우입니다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Django의 Mapping & DB 접근방식]]></title><description><![CDATA[배경 항상 Django 프로젝트를 진행하다가, 햇갈리는 점이 정말 많았습니다. 분명 Django 에서는 당연하게 사용했던 것들이 왜 작동이 안되지? 왜 이런방식으로 사용해야하는걸까? 하는 의문이 많이 생겼던 것 같습니다. Django…]]></description><link>https://haon.site/haon/django/</link><guid isPermaLink="false">https://haon.site/haon/django/</guid><pubDate>Mon, 14 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배경&lt;/h2&gt;
&lt;p&gt;항상 Django 프로젝트를 진행하다가, 햇갈리는 점이 정말 많았습니다. 분명 Django 에서는 당연하게 사용했던 것들이 왜 작동이 안되지? 왜 이런방식으로 사용해야하는걸까? 하는 의문이 많이 생겼던 것 같습니다.&lt;/p&gt;
&lt;p&gt;Django 로 개발하던 당시에는 &quot;객체를 데이터베이스에 저장한다&quot; 라는 표현을 자주 사용했었으나 이 표현이 이상하다는 생각도 듭니다. 객체를 저장하는 것이 아니라, 데이터베이스와 매핑을 시켜주는 역할이 별도로 필요하다 말을 듣고 혼동이 왔습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ormobject-relational-mapping&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ormobject-relational-mapping&quot; aria-label=&quot;ormobject relational mapping permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ORM(Object Relational Mapping)&lt;/h2&gt;
&lt;p&gt;우선 장고와 스프링을 대조, 비교하기 위해선 ORM 의 개념에 대해 알아야합니다. 그렇다면 ORM 이 뭘까요?&lt;/p&gt;
&lt;p&gt;ORM 이란 이름에서도 알 수 있듯이,&lt;strong&gt;객체와 관계형 데이터베이스를 Mapping 시켜주는 역할&lt;/strong&gt; 을 하는 녀석들을 의미합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;객체 &amp;#x3C;=&gt; 매핑(ORM) &amp;#x3C;=&gt; 데이터베이스&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;다시 강조하면, 객체와 데이터베이스를 중간에서 연결시켜주는 것이 바로 ORM 인겁니다. 이는 다시말해사, &lt;strong&gt;객체 자체를 데이터베이스에 저장한다는 행위는 엄밀히는 틀린 표현&lt;/strong&gt;이 될수 있겠습니다. 객체를 저장하는 행위가 발생하는 것은 곧 ORM 을 통해서 변형된 데이터들이 데이터베이스에 저장된다고 볼 수 있겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;orm이-없다면--sql을-일일이-작성해야-할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#orm%EC%9D%B4-%EC%97%86%EB%8B%A4%EB%A9%B4--sql%EC%9D%84-%EC%9D%BC%EC%9D%BC%EC%9D%B4-%EC%9E%91%EC%84%B1%ED%95%B4%EC%95%BC-%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;orm이 없다면  sql을 일일이 작성해야 할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ORM이 없다면? : SQL을 일일이 작성해야 할까?&lt;/h2&gt;
&lt;p&gt;저희는 로직을 구성하면서 CRUD 를 위해 SQL 을 작성할 필요없이, 객체를 정의하고 &lt;strong&gt;ORM 이 제공하는 메소드를 사용하여 데이터베이스에 쉽게 접근할 수 있습니다.&lt;/strong&gt; 이 과정에서 자동으로 SQL 쿼리문을 생성하면서 매핑이 되는 원리인거죠.&lt;/p&gt;
&lt;p&gt;ORM 이 없는 상황이라면 개발자들은 DB 에 접근하기 위해서 각 논리연산 단위에 대해 SQL 을 작성해서 접근해야 합니다. 이렇게 되면 비즈니식 로직이 꽤나 복잡해지고 가독성이 줄어들겁니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;orm을-제공하는-framework-타입&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#orm%EC%9D%84-%EC%A0%9C%EA%B3%B5%ED%95%98%EB%8A%94-framework-%ED%83%80%EC%9E%85&quot; aria-label=&quot;orm을 제공하는 framework 타입 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ORM을 제공하는 Framework 타입&lt;/h2&gt;
&lt;p&gt;ORM 을 제공하는 프레임워크는 여러가지가 있는데, 대표적으로는 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Sequelize (Node.js)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;JPA/Hibernate (Java)&lt;/li&gt;
&lt;li&gt;Django의 ORM (Django 에서 자체적으로 제공)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;django에서-제공하는-orm&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#django%EC%97%90%EC%84%9C-%EC%A0%9C%EA%B3%B5%ED%95%98%EB%8A%94-orm&quot; aria-label=&quot;django에서 제공하는 orm permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Django에서 제공하는 ORM&lt;/h2&gt;
&lt;p&gt;바로 앞서 말씀드렸듯이 Django 에서는 자체적으로 ORM 을 제공해줍니다. 이로인해 DB 에 접근하는 방식을 깊이 이해할 필요없이, 또 SQL 을 굳이 알필요없이 &lt;strong&gt;자동으로 객체와 데이터베이스를 이어주는 기능&lt;/strong&gt;을 보유하고 있다고 볼 수 있죠.&lt;/p&gt;
&lt;p&gt;Django의 모델은 데이터베이스에 매핑되는 객체입니다. 보통 하나의 모델은 하나의 테이블에 Mapping 되고, 클래스내의 Attribute 들은 테이블내의 필드를 나타냅니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;jdbc-template--sql을-통해-직접-db에-접근해야한다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jdbc-template--sql%EC%9D%84-%ED%86%B5%ED%95%B4-%EC%A7%81%EC%A0%91-db%EC%97%90-%EC%A0%91%EA%B7%BC%ED%95%B4%EC%95%BC%ED%95%9C%EB%8B%A4&quot; aria-label=&quot;jdbc template  sql을 통해 직접 db에 접근해야한다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JDBC Template : SQL을 통해 직접 DB에 접근해야한다👴&lt;/h2&gt;
&lt;p&gt;위에도 언급해놓았듯이, 이 방식은 &lt;strong&gt;SQL 쿼리문을 일일이 작성해서 힘들게 DB에 접근하는 방식&lt;/strong&gt;입니다. 잘 이해가 안가신다면, 아래 코드를 보시죠! 최근에 제가 작성했던 코드입니다.&lt;/p&gt;
&lt;p&gt;참고로 스프링에는 마치 ORM 이 없는듯한 뉘양스로 말씀드린것같은데, 스프링에도 JPA/Hibernate 라는 편리한 ORM 을 제공하고 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;manytomany-nn-관계-구현방식의-차이&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#manytomany-nn-%EA%B4%80%EA%B3%84-%EA%B5%AC%ED%98%84%EB%B0%A9%EC%8B%9D%EC%9D%98-%EC%B0%A8%EC%9D%B4&quot; aria-label=&quot;manytomany nn 관계 구현방식의 차이 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ManyToMany (N:N 관계) 구현방식의 차이&lt;/h2&gt;
&lt;h3 id=&quot;jdbctemplate&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jdbctemplate&quot; aria-label=&quot;jdbctemplate permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JdbcTemplate&lt;/h3&gt;
&lt;p&gt;다대다관계, 즉 N:N 관계를 활용한 예시입니다. 우선 아래의 코드는 teamIdx 에 해당하는 Team 에 대한 모든 User 들을 조회하는 API 입니다. 즉, 특정팀에 속하는 모든 사용자들을 GET 요청으로 조회하는 API 라고 보시면 됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;아래 코드는 완전히 이해하실 필요가 없습니다. 저희가 짚고 넘어갈 중요한 사항은 바로 &lt;strong&gt;ORM을 사용했을 떄와 사용하지 않았을 때의 간결성 및 직관성의 차이&lt;/strong&gt;입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GetUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getTeamMembers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; teamIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; getTeamAllUserIdxQuery &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;select userIdx from Mapping where teamIdx = ?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// teamIdx 값에 속하는 팀에 속하는 유저들의 userIdx 값들을 추출&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// userIdx 값들이 추출해서 userIdx값들이 저장된 GetUserIdx 리스트 생성&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GetUserIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GetUserIdxList&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jdbcTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;getTeamAllUserIdxQuery&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rowNum&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GetUserIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                        rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;userIdx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; teamIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; userIdxlist &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 각 GetUserIdx 객체로부터 userIdx 값을 추출해서 int형 리스트 생성&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GetUserIdx&lt;/span&gt; getUserIdx &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GetUserIdxList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            userIdxlist&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;getUserIdx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// 반복문으로 순환하면서 얻어온 각 userIdx 값을 기반으로 GetUser 리스트 생성&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GetUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; getUserList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GetUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GetUserQuery&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;select * from User where userIdx = ?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; eachUserIdx &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; userIdxlist&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// DB 로 부터 User 를 얻어와서&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;GetUser&lt;/span&gt; eachUser &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jdbcTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;queryForObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GetUserQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rowNum&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GetUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                            rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;userIdx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTimestamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;createdAt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;is_connected&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;nickname&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;password&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTimestamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;updatedAt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;userProfileImgUrl&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    eachUserIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// GetUser 리스트에 추가&lt;/span&gt;
            getUserList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;eachUser&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; getUserList&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;보시듯이 SQL 을 통해 데이터베이스에 접근하는 방식은 정말 귀찮아지는 작업입니다. 따라서 ORM 은 선택이 아닌 필수가 되었다고 할 정도이며, 위와 같은 방식은 이미 10년정도나 지난 오래된 개발 방식입니다.&lt;/p&gt;
&lt;h3 id=&quot;django의-orm을-활용했다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#django%EC%9D%98-orm%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%96%88%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;django의 orm을 활용했다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Django의 ORM을 활용했다면?&lt;/h3&gt;
&lt;p&gt;위의 예시와 다른 서비스에서 제가 개발했던 코드를 가져와봤습니다. 하지만 동일한 원리와 로직을 지닌 코드인데, 보시듯이 직접 DB 에 접근하는 구시대 방식에 비해 ORM 을 활용하니 코드가 훨씬 직관적이며 간결해졌습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/741dda4a-9722-4c55-90e0-0fb3ff9f60f1/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;django의-migration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#django%EC%9D%98-migration&quot; aria-label=&quot;django의 migration permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Django의 Migration&lt;/h2&gt;
&lt;p&gt;스프링과 달리 (JPA와 같은 ORM 제외) Django에서는 손쉽게 Model 에 대한 변경사항을 손쉽게 반영할 수 있습니다. Django의 Migration 이란 Model 의 변경, 수정사항등을 데이터베이스의 스키마에 반영하는 과정입니다.&lt;/p&gt;
&lt;p&gt;Django 로 개발을 한번이라도 하셨던 분들이라면 Sqlite3 와 같은 데이터베이스에 변경사항을 반영해주기 위해 아래와 같은 코드 단 한줄로 손쉽게 변경사항을 반영하는 것이 가능한 것을 아셨을 겁니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;python manage.py makemigrations&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;orm의-필요성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#orm%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1&quot; aria-label=&quot;orm의 필요성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ORM의 필요성&lt;/h2&gt;
&lt;p&gt;ORM 은 이로써 백엔드 개발자라면 꼭 알야할 지식이라는 생각이듭니다.
ORM 을 사용함으로써 어떤 이점이 존재하는지 정리해보면 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;쿼리를 객체 관점으로 작성함으로써 재사용성을 높일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;반복되는 코드를 줄일 수 있다.&lt;/li&gt;
&lt;li&gt;특정 데이터베이스가 아닌 범용적으로 작성할 수 있다.&lt;/li&gt;
&lt;li&gt;생산성을 높일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;몰론 ORM 도 단점은 존재합니다. ORM 관련 메소드를 잘 모른다면 문제를 해결하기 힘들며, 잘못 사용할 경우 오히려 최적화를 하려던것이 성능이 매우 떨어질 수 있으며, 연관관계 또한 잘못 맺을경우에 원하는 흐름대로 작성되지 않을 수도 있습니다.&lt;/p&gt;
&lt;p&gt;ORM 을 사용하는 것은 정말 좋습니다. &lt;strong&gt;다만 왜 사용을 해야하는지, 어떤 방식으로 DB 에 접근하는 것인지 등을 충분히 이해하시고나서 ORM 을 깊게 학습하는것을 권장드립니다.&lt;/strong&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Git Rebase란, Fast-Forward Merge]]></title><description><![CDATA[안녕하세요, 이번 포스팅에서는 Rebase 에 대해 알아보겠습니다.
Rebase 를 잘 활용할 수 있다면 Merge 에 비해 commit 히스토리가 더 깔끔하게 관리가 되며, Conflict…]]></description><link>https://haon.site/haon/github/rebase/</link><guid isPermaLink="false">https://haon.site/haon/github/rebase/</guid><pubDate>Tue, 01 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;안녕하세요, 이번 포스팅에서는 Rebase 에 대해 알아보겠습니다.
Rebase 를 잘 활용할 수 있다면 Merge 에 비해 commit 히스토리가 더 깔끔하게 관리가 되며, Conflict 가 발생할 일도 훨씬 적어집니다.&lt;/p&gt;
&lt;p&gt;본 포스팅에서는 &lt;strong&gt;Rebase 를 Merge 와 계속 비교하며 설명을 진행&lt;/strong&gt;하도록 하겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;rebase란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rebase%EB%9E%80&quot; aria-label=&quot;rebase란 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rebase란?&lt;/h2&gt;
&lt;p&gt;Rebase 는 말 그대로 &lt;strong&gt;base를 재설정한다는 의미&lt;/strong&gt;로, 하나의 브랜치가 다른 브랜치에서 파생되서 나온 경우, 다른 브랜치에서 진행된 커밋을 다시 가져와서 base 를 재설정한다는 것입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Rebase 는 커밋의 시간에 관계없이 마지막에 merge 되는 브랜치의 커밋을 가장 뒤에 붙이는 전략입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;언제 사용하기 적합한가?
=&gt; 내가 작업하던 브랜치에 main 브랜치 내용이 필요해서 적용시키고 싶을 때 사용
ex) 내 브랜치를 작업하는 동안 main 브랜치가 변경되었고 Merge 하려고 했으나 충돌이 발생할때,
내 브랜치에서 main 브랜치를 rebase 하고 main 으로 Merge 하면된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;특징 요약&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Merge 와 Rebase 는 실행결과는 같아도, 커밋 히스토리는 달라진다.&lt;/li&gt;
&lt;li&gt;Rebase 는 base 를 새롭게 설정한다는 의미로 이해하면 좋다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;rebase-와-merge-의-특징&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rebase-%EC%99%80-merge-%EC%9D%98-%ED%8A%B9%EC%A7%95&quot; aria-label=&quot;rebase 와 merge 의 특징 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rebase 와 Merge 의 특징&lt;/h2&gt;
&lt;h3 id=&quot;merge&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#merge&quot; aria-label=&quot;merge permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Merge&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Merge 는 &lt;strong&gt;브랜치를 통합&lt;/strong&gt;하는 것입니다.&lt;/li&gt;
&lt;li&gt;병합을 하면 합쳐진 브랜치의 커밋 메시지가 중복으로 쌓이며, &lt;strong&gt;새로운 Merge 커밋을 생성&lt;/strong&gt;합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;rebase&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rebase&quot; aria-label=&quot;rebase permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rebase&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;base를 재설정한다는 의미로, 브랜치의 base를 옮깁니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;브랜치는 base 지점을 가지고 있어, base 에서부터 코드를 수정합니다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;rebase-vs-merge&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rebase-vs-merge&quot; aria-label=&quot;rebase vs merge permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rebase VS Merge&lt;/h2&gt;
&lt;h3 id=&quot;merge-과정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#merge-%EA%B3%BC%EC%A0%95&quot; aria-label=&quot;merge 과정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Merge 과정&lt;/h3&gt;
&lt;p&gt;아래처럼 서브 브랜치(Feature 브랜치) 에다 main 브랜치를 Merge 를 시도할 경우, main 브랜치의 커밋을 feature 브랜치로 &lt;strong&gt;병합을 함으로써 feature 브랜치에 새로운 커밋이 발생&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/90efb11f-5e2c-4c57-81e4-c6b4d65c946b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;rebase-과정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rebase-%EA%B3%BC%EC%A0%95&quot; aria-label=&quot;rebase 과정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rebase 과정&lt;/h3&gt;
&lt;p&gt;main 브랜치에 커밋 A,B 가 존재하고 feature 브랜치가 생성되었다 해봅시다. 그리고 feature 브랜치에는 커밋 D, E 를 생성했을 때, main 브랜치에서 feature 브랜치를 rebase 시켜봅시다.&lt;/p&gt;
&lt;p&gt;이는 곧 main 브랜치에서 feature 브랜치의 커밋 히스토리를 base 로 해서 커밋 이력을 재쟁렬한다는 의미입니다.&lt;/p&gt;
&lt;p&gt;그러면 아래처럼 main 브랜치의 마지막 커밋 히스토리 맨뒤쪽에 이어서 feature 브랜치들의 커밋들이 뒤 이어서 붙게되는 구조를 보이게 됩니다.&lt;/p&gt;
&lt;p&gt;즉, rebase 하여 C 지점으로 base 를 이동시켜서 두 브랜치의 코드를 합치는데, 이때 D, E 커밋은 새로 지정된 C 지점 이후로 정렬됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/b81b773a-9a1b-4769-a8d1-d80b31897040/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;브랜치 A 에 B 를 Rebase 한다면, 브랜치 A 의 맨 마지막 커밋에 꼬리를 물듯이 B 의 커밋 내역들이 뒤어서 달라붙게 됩니다.
=&gt; 즉, &lt;strong&gt;선형적인 히스토리 구조&lt;/strong&gt; 를 만들게됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;rebase-가-뭔지-잘-이해가-안간다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rebase-%EA%B0%80-%EB%AD%94%EC%A7%80-%EC%9E%98-%EC%9D%B4%ED%95%B4%EA%B0%80-%EC%95%88%EA%B0%84%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;rebase 가 뭔지 잘 이해가 안간다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rebase 가 뭔지 잘 이해가 안간다면?&lt;/h2&gt;
&lt;p&gt;자, 위의 말만 들어서는 무슨 소리인지 햇갈리실 수 있다고 생각합니다.
아래의 말을 잘 기억하신다면 rebase 에 대해 다시는 햇갈리지 않을 수 있다고 생각하니, 꼭 기억해두세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Rebase 란 현재 작업하는 브랜치에서 대상 브랜치를 base로 해서 커밋 이력을 재정렬한다고 볼 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;즉, Rebase란 현재 작업하는 브랜치를 대상 브랜치의 HEAD 로 부터 분기된 브랜치로 간주하겠다는 뜻이 되기도 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예시를 통해 보시면 더 이해가 잘 되실겁니다. 자, 우선 Merge 먼저 다시 보겠습니다. master 브랜치로부터 a 브랜치가 생성이 되고 Merge 가 되는 과정은 잘 아실것이라고 생각합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/2a15275b-c45b-4a50-99b3-848dda2f30d7/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;master-브랜치에서-a-브랜치를-rebase-하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#master-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%97%90%EC%84%9C-a-%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A5%BC-rebase-%ED%95%98%EA%B8%B0&quot; aria-label=&quot;master 브랜치에서 a 브랜치를 rebase 하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;master 브랜치에서 a 브랜치를 rebase 하기&lt;/h3&gt;
&lt;p&gt;다음으로는 rebase 를 다시 살펴보죠. master 브랜치에서 a 브랜치를 rebase 한다면, a 브랜치의 HEAD 로 부터 master 브랜치가 분기된 브랜치로 간주하겠다는 의미가 됩니다. ( = master 브랜치의 base를, 기존의 base 에서 a 브랜치의 HEAD 를 base 로 새롭게 잡은 것 )&lt;/p&gt;
&lt;p&gt;이때 a 브랜치의 HEAD 는 커밋 a2 가 있는 곳입니다. 따라서 커밋 a2 뒤쪽으로 부터 master 브랜치가 파생(분기)되어 커밋들이 생성되는 구조를 이루게 되죠.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/b454012c-0ccd-433a-910c-01e004ea03c6/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;a-브랜치에서-master-브랜치를-rebase-하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#a-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%97%90%EC%84%9C-master-%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A5%BC-rebase-%ED%95%98%EA%B8%B0&quot; aria-label=&quot;a 브랜치에서 master 브랜치를 rebase 하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;a 브랜치에서 master 브랜치를 rebase 하기&lt;/h3&gt;
&lt;p&gt;반대로 a 브랜치에서 master 브랜치를 rebase 한다면, master 브랜치의 HEAD 로 부터 a 브랜치가 파생(분기)되어 나오는 구조를 이루겠죠? 따라서 master 브랜치의 HEAD 인 커밋 m3 로부터 a 브랜치의 커밋들이 이어지는 구조를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d41562f1-8040-40ba-8491-8847bfa9af3b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;rebase의-conflict-발생조건&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rebase%EC%9D%98-conflict-%EB%B0%9C%EC%83%9D%EC%A1%B0%EA%B1%B4&quot; aria-label=&quot;rebase의 conflict 발생조건 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rebase의 Conflict 발생조건&lt;/h2&gt;
&lt;h3 id=&quot;1-main-브랜치-내용이-작업-중간에-변경되어서-충돌이-발생할-수-있는-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-main-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EB%82%B4%EC%9A%A9%EC%9D%B4-%EC%9E%91%EC%97%85-%EC%A4%91%EA%B0%84%EC%97%90-%EB%B3%80%EA%B2%BD%EB%90%98%EC%96%B4%EC%84%9C-%EC%B6%A9%EB%8F%8C%EC%9D%B4-%EB%B0%9C%EC%83%9D%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;1 main 브랜치 내용이 작업 중간에 변경되어서 충돌이 발생할 수 있는 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. main 브랜치 내용이 작업 중간에 변경되어서, 충돌이 발생할 수 있는 경우&lt;/h3&gt;
&lt;p&gt;앞서 언급 드렸지만, Conlict 를 해결하기 위해선 다음과 같은 상황에서 rebase 가 사용되면 좋습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;내가 작업하던 브랜치에 main 브랜치 내용이 필요해서 적용시키고 싶을 때 사용
ex) 내 브랜치를 작업하는 동안 main 브랜치가 변경되었고 Merge 하려고 했으나 충돌이 발생할때,
내 브랜치에서 main 브랜치를 rebase 하고 main 으로 Merge 하면된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;2-main-브랜치에서-rebase를-한-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-main-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%97%90%EC%84%9C-rebase%EB%A5%BC-%ED%95%9C-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;2 main 브랜치에서 rebase를 한 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. main 브랜치에서 rebase를 한 경우&lt;/h3&gt;
&lt;p&gt;결론부터 말씀드리면 충돌(Conflict) 를 피하고자 하면 다음과 같이 하면 됩니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;main 브랜치를 rebase 하는 행위는 가급적이면 피하자! (충돌 발생원인)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;예제를 통해 Conflict 가 발생하는 상황을 직접 봅시다.
master 브랜치로부터 a 브랜치와 b 브랜치가 분기된 상황을 가정해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/73a9e100-c3d8-4d34-84c1-fc4d186aac64/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;master 브랜치에서 a 브랜치를 rebase 하면 아래와 같이 m3 커밋이 없어지고 이를 대신한 새로운 커밋 m3 와 m4 가 자동 생성됩니다. ( master 브랜치가 a 브랜치의 HEAD 를 base 로 잡았기 때문이겠죠? )&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/b594dc87-9f2b-4db6-8fe0-b937fdf43549/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그러면 기존에 있던 커밋 m3 가 존재하던 master 브랜치로부터 b 브랜치가 분기되었는데, d 브랜치는 master 브랜치로부터 분기한 커밋 m3 가 없어지게 된 꼴이 됩니다.&lt;/p&gt;
&lt;p&gt;추후에 b 브랜치를 master 브랜치에 병합시에 무수히 많은 Conflict 를 발생키므로, master 브랜치를 reabase 하는 행위를 피하는게 좋습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;fast-forward-merge&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fast-forward-merge&quot; aria-label=&quot;fast forward merge permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Fast-forward merge&lt;/h2&gt;
&lt;p&gt;Merge 를 통해 발생하는 &lt;strong&gt;불필요한 Merge 커밋을 제거&lt;/strong&gt; 할 수 있습니다. Merge 를 하면 커밋 이력이 남기때문에 히스토리에 불필요한 커밋이 늘어나게 됩니다.&lt;/p&gt;
&lt;p&gt;하지만 rebase 는 병합이 아닌 커밋 히스토리가 남기 때문에 작업이력을 확인하기 편합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;즉, 다른 브랜치의 커밋 이력 위에서 master 브랜치를 기준으로 다른 브랜치의 커밋이력을 깔끔하게 재정렬합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이 또한 예시를 살펴보죠.
만일 git merge 만을 사용하여 히스토리를 관리했다면 아래와 같은 상황이 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7d140df8-af30-4fbe-b7dd-369f1507a355/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그 뒤로 a 브렌치와 b 브렌치에서 차례대로 master 브렌치를 rebase 하는 상황을 생각해봅시다. 우선 전체적인 흐름은 아래와 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/2c600e04-792b-48e1-b0ae-b8eb44edee52/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;a 브렌치에서 master 브렌치를 rebase 하고 merge 를 시도한다면 아래와 같은 상황이 됩니다. (여기서 m4 커밋은 Merge를 하면서 자동 생성된 Merge 커밋입니다)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/8298b6bf-b58e-491c-b024-f78e5493697c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이어서 b 브렌치도 master 브렌치를 rebase 한뒤에 merge 를 하면 아래와 같은 상황이 될 겁니다. (여기서 m5 커밋은 Merge를 하면서 자동 생성된 Merge 커밋입니다)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6d605ac6-db98-4e37-9e18-3fb9e346a365/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;마치 a 브랜치의 작업이 끝난 뒤 b 브랜치가 분기하여 작업한 것 처럼 보이게 커밋 이력을 관리할 수 있습니다.&lt;/p&gt;
&lt;p&gt;이렇게 하면 브랜치 끼리 커밋 이력이 섞이지 않아 커밋 이력 관리가 편해집니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;rebase-관련-명령어-예시&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rebase-%EA%B4%80%EB%A0%A8-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%98%88%EC%8B%9C&quot; aria-label=&quot;rebase 관련 명령어 예시 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rebase 관련 명령어 예시&lt;/h2&gt;
&lt;p&gt;명령어의 흐름 구조를 직접 살펴보겠습니다. 아래와 같은 명령어를 수행하는 상황을 가정해봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ git checkout sub_branch
$ git rebase main
$ git checkout develop
$ git merge sub_branch&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;1-상황가정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EC%83%81%ED%99%A9%EA%B0%80%EC%A0%95&quot; aria-label=&quot;1 상황가정 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 상황가정&lt;/h3&gt;
&lt;p&gt;그리고 아래와 같은 초기의 커밋 히스토리를 가지고 있다고 가정해봅시다.
아래 브랜치는 main 브랜치로, 현재 커밋 1,2 를 보유하고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ea867b5d-0a38-4219-bc5b-ac5a89f47a14/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이 상태에서 sub_branch 를 생성하고, 커밋 5, 6, 7 을 생성했습니다.&lt;/p&gt;
&lt;p&gt;그리고 동시에 main 브랜치에서도 작업이 일어났습니다. sub_branch 외에 또 다른 서브 브랜치가 생성되었고 커밋 3을 생성하고 main 브랜치에 Merge 를 진행해서 Merge 커밋 4가 생성된 상황입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/11958e07-a675-4337-968c-0b4e4c92401b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그리고 각 브랜치를 쪼개보자면, 아래와 같은 커밋 히스트로리를 각각 보유하고 있을 겁니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/69b02ddf-9040-4345-be0c-d8040d1be8f6/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이 상태에서 만일 sub_branch 의 작업내용인 커밋 5, 6, 7의 내용을 main 에 그냥 Merge 하려고 한다면 충돌이 발생하겠죠?&lt;/p&gt;
&lt;p&gt;main 에 커밋이 1,2 가 그대로 있다면 상관없을텐데, 커밋의 변화가 생겼습니다.&lt;/p&gt;
&lt;p&gt;즉, main 브랜치의 커밋 2에 이어서 뒤쪽에 sub 브랜치의 내용을 병합해서 새로운 Merge 커밋을 생성해야 하는데, &lt;strong&gt;sub 브랜치 입장에서는 main 브랜치에 왠 뜬금없는 커밋 3, 4 가 존재해서 자신의 커밋 내용들을 병합시키지 못하고 충돌이 발생하게 될 겁니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-서브-브랜치에서-main-브랜치를-rebase-하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EC%84%9C%EB%B8%8C-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%97%90%EC%84%9C-main-%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A5%BC-rebase-%ED%95%98%EA%B8%B0&quot; aria-label=&quot;2 서브 브랜치에서 main 브랜치를 rebase 하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 서브 브랜치에서 main 브랜치를 rebase 하기&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;git checkout sub_branch&lt;/li&gt;
&lt;li&gt;git rebase main&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;이떄를 위해 하는것이 뭐다? 지금까지 배웠던 Rebase 이겠죠. sub 브랜치에서 main 브랜치의 새로운 작업내용들인 커밋 3, 4를 끌고 와서 작업을 하고 Merge 시켜준다면 될겁니다. &lt;strong&gt;즉, sub 브랜치에 main 의 커밋 내역을 카피해오고, 커밋들을 만들고 main 에 Merge 해주면 되는 것이죠!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/733839fd-86c6-432f-ad25-7bc36e1c80e1/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위 명령어들을 수행한 결과는 위와 같은 그림이 나올것입니다. rebase main 을 해줌으로써 커밋 3, 4가 sub 에도 생기게 되는 것이죠.&lt;/p&gt;
&lt;p&gt;rebase 를 하고나서, sub 브랜치에서 커밋 5,6,7 을 생성하는 작업을 진행합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-main-브랜치에다-서브-브랜치의-내용을-병합시키기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-main-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%97%90%EB%8B%A4-%EC%84%9C%EB%B8%8C-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%9D%98-%EB%82%B4%EC%9A%A9%EC%9D%84-%EB%B3%91%ED%95%A9%EC%8B%9C%ED%82%A4%EA%B8%B0&quot; aria-label=&quot;3 main 브랜치에다 서브 브랜치의 내용을 병합시키기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. main 브랜치에다 서브 브랜치의 내용을 병합시키기&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;git checkout develop&lt;/li&gt;
&lt;li&gt;git merge sub_branch&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;자, sub 브랜치에서 커밋 5,6,7 을 생성하고 main 브랜치에 Merge 를 시켜줍시다.&lt;/p&gt;
&lt;p&gt;기존의 main 브랜치가 아래와 같았다면,&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/fe5e4b6e-a4a6-4359-b120-4ba222730445/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Merge 를 하면 main 브랜치의 아래와 같이 변화할 겁니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6c26c6b5-324e-493b-aafb-21a9b4740d40/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;뭐, 당연한 소리지만 sub 브랜치의 상황은 그대로겠죠. 변화가 없을겁니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4342f566-3d47-4a7f-8211-a3c8d6a83719/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h4&gt;실제코드&lt;/h4&gt;
&lt;p&gt;master 브랜치의 히스토리 : init함수 -&gt; add 함수 -&gt; sub 추가
sub_branch 브랜치의 히스토리 : init함수 -&gt; add 함수 -&gt; cal2 클래스 -&gt; cal2 클래스에 add 추가&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e08dded4-576a-4d0d-90b0-0455bdc3f26b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;sub_branch 에서 master 브랜치를 rebase 하기&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d4c63ff1-3a00-4937-9345-e4315122358a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;rebase 결과, master 브랜치의 HEAD 인 &quot;sub 추가&quot; 커밋뒤에 sub_branch 의 커밋들이 뒤이어 붙어있는 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/53c0cdcc-9b76-456e-b088-1b1876b22783/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그리고 master 브랜치로 이동해서 sub_branch 를 Merge 해줍시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ba5beecb-5847-4ec4-9fb3-4f5ea311f6e1/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그러면 위와 같이 master 와 sub_branch 가 똑같은 커밋 히스토리를 보이고 있습니다. 또한 쓸모없는 Merge 커밋도 생기지 않았습니다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[협업을 위한 Git Commit Convension (커밋 컨벤션)]]></title><description><![CDATA[안녕하세요, 이번 포스팅에서는 Git 의 커밋 메시지 컨벤션에 대해 알아보겠습니다. 각 회사마다 각자의 git flow 전략을 가지고 브랜치를 관리하듯이, 그에 따라가며 자연스래 commit 을 작성하는 규칙도 정말 중요하다고 볼 수 있습니다. Git…]]></description><link>https://haon.site/haon/github/convention/</link><guid isPermaLink="false">https://haon.site/haon/github/convention/</guid><pubDate>Tue, 01 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;안녕하세요, 이번 포스팅에서는 Git 의 커밋 메시지 컨벤션에 대해 알아보겠습니다. 각 회사마다 각자의 git flow 전략을 가지고 브랜치를 관리하듯이, 그에 따라가며 자연스래 commit 을 작성하는 규칙도 정말 중요하다고 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;Git Flow 와 함께 이번 커밋 컨벤션 포스팅도 병행해서 학습하시는 것을 꼭 권장드립니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;commit-convension-은-왜-필요할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#commit-convension-%EC%9D%80-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;commit convension 은 왜 필요할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Commit Convension 은 왜 필요할까?&lt;/h2&gt;
&lt;h3 id=&quot;커밋-컨벤션을-사용하지-않은-경우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EB%B0%8B-%EC%BB%A8%EB%B2%A4%EC%85%98%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%80-%EA%B2%BD%EC%9A%B0&quot; aria-label=&quot;커밋 컨벤션을 사용하지 않은 경우 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커밋 컨벤션을 사용하지 않은 경우&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/d3f6eac4-aae3-4f6d-a7f8-5a26f743b4c8/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;커밋 컨벤션을 적용시키지 않아도 겉보기에는 특정 커밋을 보고 충분히 어떤 내용의 커밋인지 충분히 이해할 수 있다고 생각이 들 수 있습니다. 그러나 Commit Message 가 누적될수록 &lt;strong&gt;가독성이 매우 떨어집니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;또한 1인 프로젝트가 아닌, 협업을 진행해야하는 경우 본인만이 이해할 수 있는 커밋 메시지라면 상황은 더 악화됩니다. 혼자만의 커밋 메시지 규칙을 정하고, 원하는 방식으로 히스토리를 관리한다면 &lt;strong&gt;협업시에 유지.보수성이 떨어질 것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;커밋-컨벤션을-적용시킨-결과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EB%B0%8B-%EC%BB%A8%EB%B2%A4%EC%85%98%EC%9D%84-%EC%A0%81%EC%9A%A9%EC%8B%9C%ED%82%A8-%EA%B2%B0%EA%B3%BC&quot; aria-label=&quot;커밋 컨벤션을 적용시킨 결과 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커밋 컨벤션을 적용시킨 결과&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5012ce51-83cb-43c7-9599-60fcef63b256/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;따라서 모든 팀원이 충분히 이해할 수 있는 &lt;strong&gt;공통적인 커밋 메시지 규약&lt;/strong&gt; 을 정하고, 가독성을 높어야만 개발 속도도 빨라지고 각자 개발한 코드에 대한 코드 리뷰도 수월해집니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;commit-message-구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#commit-message-%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;commit message 구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Commit Message 구조&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;기본적으로 커밋 메시지는 &lt;strong&gt;제목/본문/꼬리말&lt;/strong&gt;로 구성합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;각 파트는 아래와 같은 형태로 빈줄 하나를 두고 구분을 시켜주면됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;type(옵션): [#issueNumber-]Subject    // 제목

body(옵션)                           // 본문

footer(옵션)                         // 꼬리말&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;type : 어떤 의도로 커밋했는지를 type 에 명시 (ex. feat, fix, docs)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Subject : &lt;strong&gt;제목&lt;/strong&gt;. 코드 변경사항에 대한 짧은 요약을 나타냅니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;body : 긴 설명이 필요한 경우에만 본문 내용으로써 작성해주시면 됩니다. 어떻게 작성했는지가 아닌, &lt;strong&gt;무엇을 왜 했는지&lt;/strong&gt; 를 작성해주면 됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;즉, 부연설명이 필요하거나 커밋의 이유를 설명할 경우 작성해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;footer : issue tracker ID 를 명시하고 싶은 경우에 작성합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;( 각 구조에 대한 특징과 설명은 아래에서 자세히 게속 설명 드리겠습니다. )&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1-타입commit-type&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%ED%83%80%EC%9E%85commit-type&quot; aria-label=&quot;1 타입commit type permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 타입(Commit Type)&lt;/h2&gt;
&lt;p&gt;커밋메시지의 타입(type) 은 아래와 같은 규약을 지키면서 작성해주시면 됩니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;타입은 &lt;strong&gt;&quot; 태그(tag) + 제목(subject) &quot; 으로 구성&lt;/strong&gt;되며, 태그는 영어로 쓰되, &lt;strong&gt;첫 문자는 대문자&lt;/strong&gt; 로 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&quot;태그: 제목&quot; 의 형태이며, &quot;:&quot; 뒤에 space 가 있음에 유의합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;ex) Feat: buy album api (Feat 가 태그이고, buy album api 가 제목입니다.)&lt;/p&gt;
&lt;h3 id=&quot;자주-사용하는-태그-종류&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EC%A3%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%ED%83%9C%EA%B7%B8-%EC%A2%85%EB%A5%98&quot; aria-label=&quot;자주 사용하는 태그 종류 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자주 사용하는 태그 종류&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Feat&lt;/strong&gt; : 새로운 기능을 추가하는 경우&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; : 버그를 고친경우&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docs&lt;/strong&gt; : 문서를 수정한 경우&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Style&lt;/strong&gt; : 코드 포맷 변경, 세미콜론 누락, 코드 수정이 없는경우&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refactor&lt;/strong&gt; : 코드 리펙토링&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test&lt;/strong&gt; : 테스트 코드. 리펙토링 테스트 코드를 추가했을 때&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chore&lt;/strong&gt; : 빌드 업무 수정, 패키지 매니저 수정&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Design&lt;/strong&gt; : CSS 등 사용자가 UI 디자인을 변경했을 때&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rename&lt;/strong&gt; : 파일명(or 폴더명) 을 수정한 경우&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remove&lt;/strong&gt; : 코드(파일) 의 삭제가 있을 때. &quot;Clean&quot;, &quot;Eliminate&quot; 를 사용하기도 함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;기타-태그-타입들&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%ED%83%80-%ED%83%9C%EA%B7%B8-%ED%83%80%EC%9E%85%EB%93%A4&quot; aria-label=&quot;기타 태그 타입들 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기타 태그 타입들&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Add&lt;/strong&gt; : 코드나 테스트, 예제, 문서등의 추가 생성이 있는경우- &lt;strong&gt;Improve&lt;/strong&gt; : 향상이 있는 경우. 호환성, 검증 기능, 접근성 등이 될수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implement&lt;/strong&gt; : 코드가 추가된 정도보다 더 주목할만한 구현체를 완성시켰을 때&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Move&lt;/strong&gt; : 코드의 이동이 있는경우&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Updated&lt;/strong&gt; : 계정이나 버전 업데이트가 있을 때 사용. 주로 코드보다는 문서나, 리소스, 라이브러리등에 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Comment&lt;/strong&gt; : 필요한 주석 추가 및 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;제목subject&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%9C%EB%AA%A9subject&quot; aria-label=&quot;제목subject permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제목(Subject)&lt;/h3&gt;
&lt;p&gt;제목은 코드의 변경 사항에 대해 짧은 요약을 나타냅니다.
아래와 같은 규칙을 지켜주도록 합시다.&lt;/p&gt;
&lt;h4&gt;영어로 제목을 작성하는 경우&lt;/h4&gt;
&lt;p&gt;만일 영어로 작성한다면 다음의 규칙을 따릅니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;제목은 50자를 넘기지 않고, 대문자로 작성하며 마침표를 붙이지 않습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;제목은 &lt;strong&gt;과거형을 사용하지 않고, 명령조로 시작&lt;/strong&gt;합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;ex) 제목을 Fixed 가 아닌, Fix 로 작성합시다.
( 커밋메시지를 예를들어 Fix : &quot;Modify album buy bug&quot; 로 작성하기 )&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;한글로 제목을 작성하는 경우&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;고침&quot;, &quot;추가&quot;, &quot;변경&quot; 등의 &lt;strong&gt;명령조&lt;/strong&gt; 로 시작합니다.
ex) Feat: &quot;추가 get data api 함수&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2-본문body&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EB%B3%B8%EB%AC%B8body&quot; aria-label=&quot;2 본문body permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 본문(Body)&lt;/h2&gt;
&lt;p&gt;본문은 아래와 같은 규칙에 따라 작성해주도록 합시다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;선택사항입니다. (본문은 꼭 작성 안해도 됨)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;부연설명이 필요하거나 커밋의 이유를 설명할 경우 작성해주면 됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;본문 내용은 어떻게 변경했는지 보다, &lt;strong&gt;무엇을 변경했는지&lt;/strong&gt; 또는 &lt;strong&gt;왜 변경했는지&lt;/strong&gt; 를 설명하도록 합시다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;제목과 구분되기 위해 공백 한 줄을 띄워서 작성해줍시다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3-footer-꼬리말&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-footer-%EA%BC%AC%EB%A6%AC%EB%A7%90&quot; aria-label=&quot;3 footer 꼬리말 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. footer (꼬리말)&lt;/h2&gt;
&lt;p&gt;footer 도 마찬가지로 아래 규칙들에 따라 작성해주도록 합시다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;선택사항 (꼭 작성할 필요x)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;issue tracker id 를 작성할 때 사용합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;형식 : 꼬리말은 &lt;strong&gt;&quot;유형: #이슈 번호&quot;&lt;/strong&gt; 형식으로 사용합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;issue tracker 유형은 다음 중 하나를 사용합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fixes : 이슈 수정중 (아직 해결되지 않은 경우)&lt;/li&gt;
&lt;li&gt;Resolves : 이슈를 해결했을 때 사용&lt;/li&gt;
&lt;li&gt;Ref : 참고할 이슈가 있을 때 사용&lt;/li&gt;
&lt;li&gt;Related to : 해당 커밋에 관련된 이슈번호 (아직 해결되지 않은 경우)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ex) Fixes: #45 Related to: #34, #23&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;예시-커밋-메세지를-작성해보자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%88%EC%8B%9C-%EC%BB%A4%EB%B0%8B-%EB%A9%94%EC%84%B8%EC%A7%80%EB%A5%BC-%EC%9E%91%EC%84%B1%ED%95%B4%EB%B3%B4%EC%9E%90&quot; aria-label=&quot;예시 커밋 메세지를 작성해보자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;예시: 커밋 메세지를 작성해보자!&lt;/h2&gt;
&lt;p&gt;로그인 API 를 개발한 내용을 커밋할 때, 앞서 살펴본 커밋 메시지 규칙들을 지키며 작성해보면 아래와 같습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Feat: &quot;Add login API&quot;        // 타입: 제목

로그인 API 개발               // 본문

Resolves: #123              // 꼬리말 =&gt; 이슈 123을 해결했으며,
Ref: #456                                 이슈 456 를 참고해야하며,
Related to: #48, #45         현재 커밋에서 아직 이슈 48 과 45 가 해결되지 않았다.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;출처--참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B6%9C%EC%B2%98--%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;출처  참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;출처 &amp;#x26; 참고&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@imbolic0301/Merge-%EA%B8%B0%EB%B0%98%EC%9D%98-git-convention-%EC%98%88%EC%8B%9C&quot;&gt;https://velog.io/@imbolic0301/Merge-%EA%B8%B0%EB%B0%98%EC%9D%98-git-convention-%EC%98%88%EC%8B%9C&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://doublesprogramming.tistory.com/256&quot;&gt;https://doublesprogramming.tistory.com/256&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://overcome-the-limits.tistory.com/6#%EB%8C%80%EC%B6%A9-%EC%8D%BC%EB%8D%98-git-commit-message&quot;&gt;https://overcome-the-limits.tistory.com/6#%EB%8C%80%EC%B6%A9-%EC%8D%BC%EB%8D%98-git-commit-message&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sujinlee.me/professional-github/&quot;&gt;https://sujinlee.me/professional-github/&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Git 브랜치 3가지 Merge 전략 (Squash, Rebase, Merge)]]></title><description><![CDATA[이번 포스팅에서는 Squash 에 대해 알아보겠습니다. 지난 포스팅에서 살펴본 Rebase 와 함께 커밋 히스토리를 관리하기 위한 방법 중 하나로써, Merge 할때 사용된다고 보시면 됩니다. 본 포스팅의 내용 흐름은 Squash…]]></description><link>https://haon.site/haon/github/merge-strategy/</link><guid isPermaLink="false">https://haon.site/haon/github/merge-strategy/</guid><pubDate>Fri, 28 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이번 포스팅에서는 Squash 에 대해 알아보겠습니다. 지난 포스팅에서 살펴본 Rebase 와 함께 커밋 히스토리를 관리하기 위한 방법 중 하나로써, Merge 할때 사용된다고 보시면 됩니다.&lt;/p&gt;
&lt;p&gt;본 포스팅의 내용 흐름은 Squash 에 대한 이론을 설명드리고, &lt;strong&gt;3가지의 Merge 전략(Merge, Squash and Merge, Rebase and Merge) 들의 특징을 비교&lt;/strong&gt;하는 방식으로 진행해 보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;기존의-merge-방식은&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EC%A1%B4%EC%9D%98-merge-%EB%B0%A9%EC%8B%9D%EC%9D%80&quot; aria-label=&quot;기존의 merge 방식은 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기존의 Merge 방식은?&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/78e6ae6a-3fdd-4b69-b359-7d5c795d4a75/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;각 서브 브랜치의 작업 내용을 main 브랜치에 병합시에, Git 에서 제공하는 가장 간단한 방식으로 Merge 가 있었습니다. 병합시에는 새로운 Merge 커밋이 생성되는 모습을 확인할 수 있을겁니다.&lt;/p&gt;
&lt;p&gt;Merge 에 대한 이론을 잘 모르신다면, 지난번 제 포스팅 링크를 참고하셔도 좋습니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/%EB%B8%8C%EB%A0%8C%EC%B9%98Branch%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-Merge-%EA%B3%BC-Conflic&quot;&gt;https://velog.io/@msung99/%EB%B8%8C%EB%A0%8C%EC%B9%98Branch%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-Merge-%EA%B3%BC-Conflic&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;위 예시의 경우, my_branch 브랜치에서 커밋 A, B, C 를 생성하고 main 브랜치에 병합하는 상황입니다. 명령어로 표현하면 아래와 같을 것입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;git checkout main
git merge my_branch&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;squash&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#squash&quot; aria-label=&quot;squash permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Squash&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;여러번 커밋한 이력을 하나의 커밋 이력으로&lt;/strong&gt; 합친후 Merge 하는데 사용합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/07bf5f64-504d-4049-a917-31cac47ca1c6/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;my_branch 브랜치에서 생성한 각 커밋 A, B, C 를 합쳐서 하나의 새로운 커밋을 생성하고 브랜치에 추가하는 명령이 바로 Squash 입니다.&lt;/p&gt;
&lt;p&gt;이때 Squash 를 통해 새롭게 생성된 통합 커밋(커밋 A+B+C) 은 부모 커밋을 하나만 가지게 됩니다. 즉, Init 이라는 커밋이 있을때 통합 커밋은 Init 가 직전 커밋(부모 커밋) 이 되는 것이죠.&lt;/p&gt;
&lt;p&gt;Squash 를 통해 통합커밋을 생성하는 명령은 아래와 같습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;git checkout main               // main 브랜치로 이동해서
git merge --squash my-branch    // my-branch 브랜치에 대한 통합 커밋을 생성하고
                                   main 브랜치에 병합하기 위한 명령
git commit -m &quot;commit message&quot;  // 통합 커밋을 생성&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;정리해보면, Squash 의 사용목적은 서브 브랜치의 여러 커밋들을 하나로 합쳐서 깔끔하기 만들기 위해 사용한다고 할 수 있겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;squash-vs-rebase&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#squash-vs-rebase&quot; aria-label=&quot;squash vs rebase permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Squash VS Rebase&lt;/h2&gt;
&lt;p&gt;rebase 를 아직 잘 모르시면 아래의 제 이전 포스팅 링크를 참고하세요!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@msung99/Git-Rebase%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot;&gt;https://velog.io/@msung99/Git-Rebase%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;그렇다면 Squash 와 Rebase 의 차이에 대해서도 살펴보죠.
정리를 먼저 하자면 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Squash : 서브 브랜치를 master 브랜치에 &lt;strong&gt;병합시, 서브 브랜치의 커밋들에 대한 통합커밋 하나를 생성하고 병합시키는 방식&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;Rebase : Squash 처럼 따로 통합커밋을 하나 생성하고 master 브랜치에다 병합하는 방식이 아닌, &lt;strong&gt;서브 브랜치의 여러 커밋의 뭉탱이를 뒤에 이어서 붙여주는 방식&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;쉽게말해 Squash 는 통합커밋 딱 하나를 master 브랜치 뒤에 붙여주는 방식이고, Rebase 는 서브브랜치의 여러 커밋들을 뒤에 붙여주는 방식이다.&lt;/p&gt;
&lt;p&gt;예시를 보면 더 이해가 잘 될겁니다. 아래의 방식은 서브 브랜치(my-feature) 를 Squash 해서 통합 커밋 ab 를 생성하고, 이 통합커밋을 develop 브랜치에서 merge 하는 방식입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/f0bf52d6-d461-4c7f-9381-878a47450aa4/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;명령어로 표현하면 아래와 같습니다. (위에서 봤던 명령어랑 동일!)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ git checkout develop
$ git merge --squash sub_branch
$ git commit -m &quot;your-commit-message&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;반면 아래는 rebase 방식입니다. 통합커밋 하나를 생성하는 것이 아닌, 서브 브랜치의 커밋 히스토리를 몽땅 다 가져와서 develop 브랜치에다 merge 하는 방식입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a19c6950-46b1-41e4-aca6-ba416331e722/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;명령어로 표현하면 아래와 같습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ git checkout sub_branch
$ git rebase develop
$ git checkout develop
$ git merge sub_branch&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;서브 브랜치는 rebase 를 진행해서 기존의 develop 브랜치에 커밋들이 이어서 추가가 되었습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;squash-와-rebase-를-적극-활용해보자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#squash-%EC%99%80-rebase-%EB%A5%BC-%EC%A0%81%EA%B7%B9-%ED%99%9C%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%9E%90&quot; aria-label=&quot;squash 와 rebase 를 적극 활용해보자 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Squash 와 Rebase 를 적극 활용해보자&lt;/h2&gt;
&lt;p&gt;만일 Merge 만 사용한다면 아래와 같은 복잡한 커밋 히스토리 구조가 나올 수 있으나,&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/202c180d-244f-4b1b-aa17-2900f2ee2502/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Squash 와 Rebase 를 잘만 사용한다면 아래처럼 커밋 히스토리가 단순한 구조로 예쁘게 변경되는 것을 볼 수 있을것입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/234bd264-c052-405d-839e-d02e6cf022a8/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;merge-전략들을-비교&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#merge-%EC%A0%84%EB%9E%B5%EB%93%A4%EC%9D%84-%EB%B9%84%EA%B5%90&quot; aria-label=&quot;merge 전략들을 비교 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Merge 전략들을 비교&lt;/h2&gt;
&lt;p&gt;지금껏 브랜치를 병합하기 위한 3가지 Merge 전략(3-Way-Merge) 에 대해 살펴봤습니다. 각 전략에 대한 특징을 정리해보자면 아래와 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5976f35f-38ef-4c08-8b14-b5023f2096d7/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;팀이 실력이 아직 부족하다 느끼면, 이제 막 git으로 버전관리하는 법을 배우기 시작했다면 Squash and Merge가 좋을 겁니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;참고--출처&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0--%EC%B6%9C%EC%B2%98&quot; aria-label=&quot;참고  출처 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 &amp;#x26; 출처&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://cjw-awdsd.tistory.com/49&quot;&gt;https://cjw-awdsd.tistory.com/49&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://datalibrary.tistory.com/194&quot;&gt;https://datalibrary.tistory.com/194&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@kmg2933/Git-Merge-Squash-Rebase-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot;&gt;https://velog.io/@kmg2933/Git-Merge-Squash-Rebase-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://inmoonlight.github.io/2021/07/11/Git-merge-strategy/&quot;&gt;https://inmoonlight.github.io/2021/07/11/Git-merge-strategy/&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Git 브랜치 관리전략. Git Flow, GitHub Flow]]></title><description><![CDATA[안녕하세요, 이번 포스팅에서는 Git 브랜치 전략이라는 것에 대해 알아보도록 하겠습니다. Git 브랜치 전략이란 브랜치를 프로젝트의 목적성에 맞게 효과적으로 분할하고 관리하는 전략으로, 아주 중요한 브랜치 관리 전략이라고 할 수 있겠습니다. Git…]]></description><link>https://haon.site/haon/github/git-flow-strategy/</link><guid isPermaLink="false">https://haon.site/haon/github/git-flow-strategy/</guid><pubDate>Fri, 28 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;안녕하세요, 이번 포스팅에서는 Git 브랜치 전략이라는 것에 대해 알아보도록 하겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;git-브랜치-전략이란-브랜치를-프로젝트의-목적성에-맞게-효과적으로-분할하고-관리하는-전략으로-아주-중요한-브랜치-관리-전략이라고-할-수-있겠습니다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#git-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EC%A0%84%EB%9E%B5%EC%9D%B4%EB%9E%80-%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A5%BC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%9D%98-%EB%AA%A9%EC%A0%81%EC%84%B1%EC%97%90-%EB%A7%9E%EA%B2%8C-%ED%9A%A8%EA%B3%BC%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%B6%84%ED%95%A0%ED%95%98%EA%B3%A0-%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94-%EC%A0%84%EB%9E%B5%EC%9C%BC%EB%A1%9C-%EC%95%84%EC%A3%BC-%EC%A4%91%EC%9A%94%ED%95%9C-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EA%B4%80%EB%A6%AC-%EC%A0%84%EB%9E%B5%EC%9D%B4%EB%9D%BC%EA%B3%A0-%ED%95%A0-%EC%88%98-%EC%9E%88%EA%B2%A0%EC%8A%B5%EB%8B%88%EB%8B%A4&quot; aria-label=&quot;git 브랜치 전략이란 브랜치를 프로젝트의 목적성에 맞게 효과적으로 분할하고 관리하는 전략으로 아주 중요한 브랜치 관리 전략이라고 할 수 있겠습니다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Git 브랜치 전략이란 브랜치를 프로젝트의 목적성에 맞게 효과적으로 분할하고 관리하는 전략으로, 아주 중요한 브랜치 관리 전략이라고 할 수 있겠습니다.&lt;/h2&gt;
&lt;h2 id=&quot;git-branching-전략들&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#git-branching-%EC%A0%84%EB%9E%B5%EB%93%A4&quot; aria-label=&quot;git branching 전략들 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Git Branching 전략들&lt;/h2&gt;
&lt;p&gt;대표적인 브랜칭(Branching) 전략으로는 크게 2가지로 나뉩니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;GitHub-Flow : GitHub 에서 간단하게 자제 제작한 단순한 구조의 브랜치 전략으로써, &lt;strong&gt;Master 브랜치를 중심으로 운영&lt;/strong&gt;됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Git-Flow : 브랜치를 크게 &lt;strong&gt;5개의 브랜치로 구분 및 분할하여 관리&lt;/strong&gt;하는 전략으로, 현재 많은 기업들에서 사용하는 브랜치 전략입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;추가적으로 GitLab Flow 라는 것이 있지만, 이번 포스팅에서는 다루지 않도록 하겠습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;아래에서 더 자세히 설명드리겠지만, GitHub Flow 는 전략 특성상 GitHub 로 프로젝트를 아직 많이는 접해보지 못하신 분들이 많이 사용하기 좋은 전략입니다. (몰론 상황에 따라 다르지만요!)&lt;/p&gt;
&lt;p&gt;브랜치를 복잡하게 여러개로 구분짓는 Git-Flow 전략에 비해서, 단순히 기능별 브랜치를 생성하고 Master 브랜치에 Merge 하는 전략이기 떄문에, 복잡성을 따졌을 때 GitHub Flow 가 확실히 직관적으로 이해하기 쉽고 복잡하지 않은 프로젝트 수준에서 사용하기 좋을 것입니다. 신경써야할 것들이 많이 다르죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;github-flow-전략&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#github-flow-%EC%A0%84%EB%9E%B5&quot; aria-label=&quot;github flow 전략 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GitHub Flow 전략&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a213e1a0-cfb1-43b2-bf9b-58c72a4bb877/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Master 브랜치를 중심으로 브랜치들을 관리하는 전략&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;기능추가 와 버그수정 작업을 하는 브랜치를 Master 브랜치로 부터 파생시켜서 작업후 Master 브랜치에 Merge 하는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Git-Flow 가 GitHub 에서 사용하기 복잡하기 때문에, 이에 대한 방안으로 나오게 된 브랜칭(Branching) 전략입니다.&lt;/p&gt;
&lt;p&gt;앞서 설명드렸듯이 GitHub 에서 만든 단순한 구조의 브랜치 전략으로,
GitHub Flow 와 달리 기능 개발, 버그 수정 등의 &lt;strong&gt;작업용 브랜치를 별도로 구분하지 않는 단순한 구조&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이 브랜칭 전략은 수시로, 자주 CI 및 배포가 일어나는 프로젝트에 유용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;github-flow흐름-순서&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#github-flow%ED%9D%90%EB%A6%84-%EC%88%9C%EC%84%9C&quot; aria-label=&quot;github flow흐름 순서 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GitHub Flow(흐름) 순서&lt;/h3&gt;
&lt;p&gt;브랜치 생성부터 시작해서 Merge를 하고, 공개 및 테스트까지 하는 일련의 과정 및 흐름속에서 GitHub Flow 에서 제공하는 방법에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4f2dd465-ce7a-47a6-acc7-5df516c6ad0c/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4&gt;1. 브랜치 생성&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Master 브랜치로부터 기능추가, 버그수정을 위한 새로운 브랜치가 셍성되어야 합니다. 즉, &lt;strong&gt;기능추가 와 버그수정 작업을 하는 브랜치는 모두 Mater 브랜치로부터 파생&lt;/strong&gt;되어 나옵니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;새로운 기능을 추가하거나, 버그 해결을 위한 브랜치들의 이름은 &lt;strong&gt;자세하게 어떤 일을 하고 있는지에 대해서 작성&lt;/strong&gt;해주도록 합시다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Git-Flow 와 달리 feature, develop 브랜치가 존재하지 않습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. commit message&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;커밋 메시지는 명확하고 간결히 작성되어야 합니다.&lt;/li&gt;
&lt;li&gt;작업을 하며 각 기능별로 commit을 진행해주어야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;commit 은 서버(원격 레포지토리)의 동일한 브랜치에 push 해주어야 합니다. (Git Flow 와의 차이점)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3. PR (Pull Request)&lt;/h4&gt;
&lt;p&gt;앞서 브랜치를 생성할때 기능추가 와 버그수정 작업을 진행하기 위한 브랜치를 생성했을겁니다. 생성한 브랜치에서 기능 또는 오류 수정등을 완료했다면, PR 을 Master 브랜치에 보내면 됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;즉, &lt;strong&gt;PR 은 코드 리뷰를 도와주는 시스템&lt;/strong&gt;으로써 보고, Merge 할 준비가 완료되었다면 PR 를 올려서 자신의 코드를 공유하고 피드백과 리뷰를 받는 방식입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4. 리뷰와 논의하기&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;PR 에 대한 코드를 읽어보고, 리뷰어는 피드백을 작성하면 됩니다.&lt;/li&gt;
&lt;li&gt;Merge 직전의 단계로써, 곧바로 Product 로 반영이 될 기능이므로 충분히 논의와 리뷰를 하고나서 반영하도록 합시다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ex) 리뷰내용 : 작성한 코딩 스타일이 프로젝트 가이드라인과 부합하지 않는다.&lt;/p&gt;
&lt;h4&gt;5. 공개 및 테스트&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;깃허브에서는 Master 브랜치에 &lt;strong&gt;Merge 이전에, 긱 브랜치에서 코드를 공개 및 테스트 할 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PR 에 대한 논의와 테스트가 끝난 경우 테스트 버전으로써 공개할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Branch의 검증이 완료되면 메인 브랜치에 합치면 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;만일 오류가 발생했다면, 기존의 Master 브랜치를 다시 배포하고 roll back 하는 방식으로 되돌아와서 오류를 수정하면 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;git-flow-전략&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#git-flow-%EC%A0%84%EB%9E%B5&quot; aria-label=&quot;git flow 전략 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Git Flow 전략&lt;/h2&gt;
&lt;p&gt;다음은 Git Flow 전략입니다. 현재 많은 기업에서 표준으로 사용하고 있는 브랜치 전략입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4a4674c3-080b-4cb2-b0f6-ca141b5fd677/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ccf0954b-7f54-42aa-ae08-b680a91c1b77/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;GitHub Flow 와 다르게 크게 5개의 브랜치로 운영하여 관리하는 전략입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;메인 브랜치 : master, develop&lt;/li&gt;
&lt;li&gt;보조 브랜치 : feature, release, hotfix&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;배포 주기가 길고 팀의 여력이 있는 경우가 사용하기 적잡한 전략입니다.
그리고 각 브랜치의 특징을 정의해보자면 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;메인 브랜치&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;master : 실제 제품으로 배포 및 출시되는 브랜치&lt;/li&gt;
&lt;li&gt;develop : 다음 출시 버전을 기다리는 브랜치&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;보조 브랜치&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;feature : 기능을 개발하는 브랜치&lt;/li&gt;
&lt;li&gt;release(QA) : 이번 출시 버전을 준비하는 브랜치. QA 브랜치라고도 불린다.&lt;/li&gt;
&lt;li&gt;hotfix : 출시 버전에서 발생한 버그를 수정하는 브랜치. 수정이 완료되면 삭제된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;메인-브랜치main-branch&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%94%EC%9D%B8-%EB%B8%8C%EB%9E%9C%EC%B9%98main-branch&quot; aria-label=&quot;메인 브랜치main branch permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메인 브랜치(Main Branch)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/bb536d76-b096-4e98-b286-a2c160a40438/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;master 와 develop 브랜치 두 종류를 보통 메인 브랜치로써 병행해서 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;master 브랜치&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;배포 가능한 상태만을 관리하는 브랜치&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;develop 브랜치&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;다음 버전으로 배포할 것을 개발하는 브랜치&lt;/li&gt;
&lt;li&gt;develop 브랜치는 통합 브랜치의 역할을하며, 평소에는 이 브랜치를 기반으로 개발을 진행&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;보조-브랜치sub-branch&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B3%B4%EC%A1%B0-%EB%B8%8C%EB%9E%9C%EC%B9%98sub-branch&quot; aria-label=&quot;보조 브랜치sub branch permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;보조 브랜치(Sub branch)&lt;/h3&gt;
&lt;p&gt;보조 브랜치에는 feature, release(QA), hotfix 총 3가지의 브랜치가 있습니다.
&lt;strong&gt;사용을 다 마치면 브랜치를 삭제한다는 특징&lt;/strong&gt; 을 지니고 있습니다.&lt;/p&gt;
&lt;h4&gt;1. Feature 브랜치&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e1092a94-69a9-4b93-8402-ebbc1bd3c044/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;특징1) feature 브랜치는 하나의 &lt;strong&gt;새로운 기능을 만들 때 생성&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;feature 브랜치는 새로운 기능 단위를 만들고자 할떄 develop 브랜치로부터 분기(파생)됩니다.&lt;/p&gt;
&lt;p&gt;다음 배포에 확실히 넣을 것이라고 판단될 떄 merge 하고, 결과가 실망스럽다면 아예 버리는 방식입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;특징2) 브랜치가 분기된 곳/Merge 해야하는 곳 : develop 브랜치&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;기능을 개발하는 브랜치들로써, develop 브랜치로부터 분기한 브랜치입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;특징3) 이름 : feature-브랜치명&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;master, develop, release-&lt;em&gt;, hotfix-&lt;/em&gt; 를 제외한 모든 이름을 지을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위 브랜치 명들은 feature 브랜치를 제외한 나머지 4가지의 브랜치로, 당연히 이러한 브랜치 명을 적는다면 브랜치의 명확성이 떨어지게 될 것입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;특징4) develop 에 merge 한 이후에는 해당 브랜치는 삭제시켜준다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;말 그대로 기능 개발이 완료되었고 develop 브랜치에 merge 해주었다면 해당 feature 브랜치를 삭제시켜주도록 하는것이 원칙입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;특징5) merge 할때는 &lt;strong&gt;--no--ff 키워드를 사용해서 기록을 그룹화&lt;/strong&gt;한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;--no--ff : Fast-forward 관계에 있어도 merge commit 을 생성해서 해당 브랜치가 존재했다는 정보를 남길 수 있고, commit 기록을 되돌리기 편해집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위 키워드를 사용하지 않는다면, 브랜치 존재 여부를 몰라서 어떤 commit 부터 해당 기능을 구현했는지 확인하기가 어렵습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h4&gt;2. Release 브랜치&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/60749f57-0c83-4540-a885-4a19c7ce9e36/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;특징1) 다음 버전 출시를 위해 QA 를 진행하는 브랜치&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;배포를 위한 최종적인 버그 수정등의 개발을 수행하는 브랜치입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;특징2)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;브랜치가 분기(파생)되는곳 : develop&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;merge 되는 곳 : develop &amp;#x26; master&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;develop 브랜치에 이번 버전에 포함되는 기능이 merge 되었다면, QA 를 위해 develop 브랜치에서부터 release 브랜치를 생성합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;배포 가능한 상태가 되면 master 브랜치로 merge 시키고, 출시된 master 브랜치에 버전 태그를 추가합니다. 즉, master 브랜치로 merge 이후에는 tag 명령을 통해 버전을 명시해야 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;release 브랜치에서 기능을 점검하며 발견한 버그 수정 사항은 develop 브랜치에도 적용해 주어야함! 그러므로 배포 완료 후 develop 브랜치에 대해서도 merge 작업을 수행합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;특징3) merge 할떄 &lt;strong&gt;--no--ff 를 사용하여 기록을 그룹화해야한다.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;feature 브랜치와 마찬가지로 --no --ff 를 사용하여 merge commit 을 생성할 수 있습니다. 생성된 merge commit 을 활용하는 것은 앞서 설명했습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;특징4) 이름 : release-브랜치명&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h4&gt;3. hotfix 브랜치&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/7d8d7532-7559-4489-87fa-8bd05e44357d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;특징1) Production 에 버그가 발생하면 빠른 수정을 위해 생성하는 브랜치&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;버그를 잡는 사람이 일하는 동안에도, 즉 한 사람이 Production 코드를 수정하는 중에도 다른 사람들은 develop 브랜치에서 계속 하고있던 개발을 계속할 수 있다는 장점이 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;특징2)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;브랜치가 분기(파생)되는 곳 : master&lt;/li&gt;
&lt;li&gt;merge 되는 곳 : develop &amp;#x26; master&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;특징3) 이름 : hotfix-브랜치명&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;브랜치명을 짓는 방법은 앞서 살핀 브랜치들을 봤다면 잘 이해되실 겁니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;특징4) master 로 merge한 이후에는 &lt;strong&gt;tag 명령을 통해 이전 버전보다 더 높은 버전임을 명시&lt;/strong&gt; 힌디/&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;예를들어 1.6 -&gt; 1.6.1 과 같이 명시하면 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;어떤-branch-전략을-사용할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%96%A4-branch-%EC%A0%84%EB%9E%B5%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C&quot; aria-label=&quot;어떤 branch 전략을 사용할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어떤 Branch 전략을 사용할까?🤔&lt;/h2&gt;
&lt;p&gt;조직의 규모, 서비스의 특징, 프로젝트에 참여하고 있는 구성원들이 제각기 다르기 때문에, 상황에 따라 사용하기 적합한 전략이 다릅니다.&lt;/p&gt;
&lt;p&gt;팀의 브랜칭 전략은 조직의 규모, 서비스 특징등을 고려하고 협의를 진행해서 전략을 결정하는 것이 좋겠습니다.&lt;/p&gt;
&lt;p&gt;그래도 일반화를 시켜보자면 아래와 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Git Flow : Production 의 공식 배포 주기가 길고, QA, 테스트, hotifx 등의 여력이 있다면 Git Flow 가 적절합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;GitHub Flow : 지속적으로 테스트 및 배포를 하는 팀의 경우 간단한 GitHub Flow 를 사용하는 것이 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;Work-Flow 를 필요 이상으로 복잡하게 만드는 것은 좋지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;출처--참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B6%9C%EC%B2%98--%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;출처  참고 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;출처 &amp;#x26; 참고&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.github.com/en/get-started/quickstart/github-flow&quot;&gt;https://docs.github.com/en/get-started/quickstart/github-flow&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/extales/git%EC%9D%84-%EB%8B%A4%EB%A3%A8%EB%8A%94-workflow-gitflow-github-flow-gitlab-flow-849d4e4104d9&quot;&gt;https://medium.com/extales/git%EC%9D%84-%EB%8B%A4%EB%A3%A8%EB%8A%94-workflow-gitflow-github-flow-gitlab-flow-849d4e4104d9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hellowoori.tistory.com/56&quot;&gt;https://hellowoori.tistory.com/56&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dev-jejecrunch.tistory.com/116&quot;&gt;https://dev-jejecrunch.tistory.com/116&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.slipp.net/questions/244&quot;&gt;https://www.slipp.net/questions/244&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.programster.org/git-workflows&quot;&gt;https://blog.programster.org/git-workflows&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[글쓰기가 정말 나에게 도움이될까?]]></title><description><![CDATA[…]]></description><link>https://haon.site/회고/meta-congnition/</link><guid isPermaLink="false">https://haon.site/회고/meta-congnition/</guid><pubDate>Mon, 04 Apr 2022 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;현 포스팅은 많은 분들에게 도움이 되었으면 하는 바람과 함께 제 학습법을 공유합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;작성동기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%91%EC%84%B1%EB%8F%99%EA%B8%B0&quot; aria-label=&quot;작성동기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;작성동기&lt;/h2&gt;
&lt;p&gt;안녕하세요 여러분들! 지금 포스팅을 보시는 모든 분들에게 저만의 학습법을 전해보고자 해요. 그저 평범한 대학생의 독학으로 프로그래밍 학습법을 이어가는 스토리에 불과하니, 이 점 감안해주세요. 피드백도 주시면 너무너무 감사할 것 같구요!&lt;/p&gt;
&lt;p&gt;글쓰기가 왜 중요한지, 또 어떤 방법으로 글쓰기를 이어가야지 긍정적인 학습효과를 만들수 있는 것인지 회고하며, 많은 분들에게도 공유해볼까하여 이번 글을 작성하게 되었습니다.&lt;strong&gt;특히 저처럼 독학을 이어가며 어려움을 느끼는 분들에게 조금이나마 도움이 되었으면 하는 바람에 작성합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;사실 이 주제와 관련한 내용은 &lt;a href=&quot;https://velog.io/@msung99/%EB%8A%A5%EB%8F%99%EC%A0%81%EC%9D%B8-%ED%95%99%EC%8A%B5%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%B8%94%EB%A1%9C%EA%B9%85-%EC%84%A4%EB%AA%85%ED%95%98%EA%B8%B0%EC%9D%98-%ED%95%99%EC%8A%B5%ED%9A%A8%EA%B3%BC-p60yepgo&quot;&gt;책임감 있는 블로깅 : 설명하기의 학습효과와 능동적인 공부법&lt;/a&gt; 에서도 간단히 언급했었지만, 내용이 짧고 부실했다보니 이번에 훨씬 자세한 학습법을 공유해보고자 합니다. 따라서 제 포스팅을 읽고 댓글로 함께 말씀을 나눠보고 싶은 점이 있다면 편하게 남겨주시면 좋을 것 같아요 🥹&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;현재 포스팅 내용을 읽으시기전에, &lt;a href=&quot;https://velog.io/@msung99/%EB%8A%A5%EB%8F%99%EC%A0%81%EC%9D%B8-%ED%95%99%EC%8A%B5%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%B8%94%EB%A1%9C%EA%B9%85-%EC%84%A4%EB%AA%85%ED%95%98%EA%B8%B0%EC%9D%98-%ED%95%99%EC%8A%B5%ED%9A%A8%EA%B3%BC-p60yepgo&quot;&gt;책임감 있는 블로깅 : 설명하기의 학습효과와 능동적인 공부법&lt;/a&gt; 도 한번씩 꼭!! 먼저 읽어보셨으면 해요 🙂&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;독학하는-과정속에서-꾸준히-작성하는-글쓰기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%85%ED%95%99%ED%95%98%EB%8A%94-%EA%B3%BC%EC%A0%95%EC%86%8D%EC%97%90%EC%84%9C-%EA%BE%B8%EC%A4%80%ED%9E%88-%EC%9E%91%EC%84%B1%ED%95%98%EB%8A%94-%EA%B8%80%EC%93%B0%EA%B8%B0&quot; aria-label=&quot;독학하는 과정속에서 꾸준히 작성하는 글쓰기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;독학하는 과정속에서 꾸준히 작성하는 글쓰기&lt;/h3&gt;
&lt;p&gt;본격적인 글쓰기 학습법에 대해 소개드리지 전에, 저에 대해 간단히 소개를 드려보고 싶습니다. &lt;strong&gt;지금껏 저는 꾸준히 평균 2~4일 간격으로 꾸준히 프로그래밍을 학습하며 글쓰기를 이어왔습니다.&lt;/strong&gt; 개념을 아예 처음 접하는 사람들도, 심지어 비전공자들도 쉽게 이해할 수 있는 글쓰기를 몇년간 이어왔습니다. 비록 독학이라서 글쓰기가 서툴고 잘못된 내용을 기제한 경우가 간혹 발생하지만, 더 공신력있는 웹사이트와 공식문서를 열심히 참고하면서 좋은 포스팅을 작성하는데 노력해왔습니다.&lt;/p&gt;
&lt;p&gt;이렇게 간단한 소개를 마무리짓고, 지금부터 학습법에 대해 자세히 소개해볼게요! 🙂&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;버즈니아-ntl-연구기관의-학습효율성-피라미드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B2%84%EC%A6%88%EB%8B%88%EC%95%84-ntl-%EC%97%B0%EA%B5%AC%EA%B8%B0%EA%B4%80%EC%9D%98-%ED%95%99%EC%8A%B5%ED%9A%A8%EC%9C%A8%EC%84%B1-%ED%94%BC%EB%9D%BC%EB%AF%B8%EB%93%9C&quot; aria-label=&quot;버즈니아 ntl 연구기관의 학습효율성 피라미드 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;버즈니아 NTL 연구기관의 학습효율성 피라미드&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ad98cdcc-3227-44cf-adad-c513eec183b4/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;우선 &lt;a href=&quot;https://velog.io/@msung99/%EB%8A%A5%EB%8F%99%EC%A0%81%EC%9D%B8-%ED%95%99%EC%8A%B5%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%B8%94%EB%A1%9C%EA%B9%85-%EC%84%A4%EB%AA%85%ED%95%98%EA%B8%B0%EC%9D%98-%ED%95%99%EC%8A%B5%ED%9A%A8%EA%B3%BC-p60yepgo&quot;&gt;책임감 있는 블로깅 : 설명하기의 학습효과와 능동적인 공부법&lt;/a&gt; 에서도 언급 드렸던 내용입니다. 위 자료는 미국 버즈니아 NTL 연구기관이 출처로 되어있는 &lt;code class=&quot;language-text&quot;&gt;학습효율성 파라미드(learning pyramid)&lt;/code&gt;입니다. 다른 말로는 &lt;code class=&quot;language-text&quot;&gt;경험의 원뿔(cone of experience)&lt;/code&gt; 라고도 부릅니다. &lt;strong&gt;제가 이 자료를 처음 접했을 당시가 중학교 3학년 무렵이네요. 2014년 EBS 다큐프라임 &quot;우리는 왜 대학에 가는가&quot; 를 접할때 이 자료를 처음 접했을 떄의 충격은 아직도 잊을 수 없습니다.&lt;/strong&gt; 항상 독서실에서 가만히 책을 오랜시간 읽는것이 최고의 방법이라 생각했었는데, 꽤나 신박한 방법이였습니다.&lt;/p&gt;
&lt;p&gt;피라미드를 자세히 살펴보면 강의듣기(Lecture), 읽기(Reading), 시청하기(Audiovisual) 는 낮은 학습효과를 보이고 있습니다. &lt;strong&gt;이런 방법은 학습자가 배움을 수동적으로 입력(input) 받는 방식입니다.&lt;/strong&gt; 반면 토론하기(Discussion), Practice doing(연습하기), Teach others(가르치기) 가 가장 높은 학습효과를 보과를 보이고 있습니다. &lt;strong&gt;이는 학습자가 배움을 능동적으로 인출(output) 하는 방식입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;능동적으로-배움에-과정에-참여하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8A%A5%EB%8F%99%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%B0%B0%EC%9B%80%EC%97%90-%EA%B3%BC%EC%A0%95%EC%97%90-%EC%B0%B8%EC%97%AC%ED%95%98%EA%B8%B0&quot; aria-label=&quot;능동적으로 배움에 과정에 참여하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;능동적으로 배움에 과정에 참여하기&lt;/h3&gt;
&lt;p&gt;이 연구결과가 시사하는 것은 무엇일까요? &lt;strong&gt;학습자가 높은 학습효과를 내기위해선 &quot;왜&quot; 라는 질문을 가지고 항상 능동적으로 배움의 과정에 참여하라는 것입니다.&lt;/strong&gt; 대부분의 사람들은 학창시절부터 항상 주입식 교육을 받아왔기에, 끊임없이 누군가 지식과 정답을 주입시켜주길 원합니다. 이를 &quot;수동적 참여&quot; 라고 정의내려 볼 수 있을 것 같네요. 몇년간 끊임없이 정답이 있는 문제들만 풀어왔던 것 입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;메타인지로-가는-길&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%94%ED%83%80%EC%9D%B8%EC%A7%80%EB%A1%9C-%EA%B0%80%EB%8A%94-%EA%B8%B8&quot; aria-label=&quot;메타인지로 가는 길 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메타인지로 가는 길&lt;/h2&gt;
&lt;h3 id=&quot;고통스럽지-않은-수동적-입력&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%A0%ED%86%B5%EC%8A%A4%EB%9F%BD%EC%A7%80-%EC%95%8A%EC%9D%80-%EC%88%98%EB%8F%99%EC%A0%81-%EC%9E%85%EB%A0%A5&quot; aria-label=&quot;고통스럽지 않은 수동적 입력 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;고통스럽지 않은 수동적 입력&lt;/h3&gt;
&lt;p&gt;타인으로부터 매번 수동적으로 지식을 입력받는 방식은 고통스럽지 않습니다. 그저 해결법을 제시해준다면, 그 방식대로 암기하고 받아들이면 끝이기 떄문입니다. 하지만 &lt;strong&gt;개발자라는 직업이 이 세상에 존재하는 이유는 뭘까요?&lt;/strong&gt; 제 생각엔 세상에 존재하는 많은 문제들을 접하고, 이를 코드라는 수단으로 해결하는 사람이라고 봅니다. 몇십년간 수많은 문제들을 접했을 때, 누군가 정답을 알려줄 때 까지 기다려야할까요? 만약 기다린다면, 그 해결사가 또 찾아올때까지 매번 기다려야할까요? &lt;strong&gt;아무도 정답을 알려주지 않는 환경에 들어서게되면, 간단한 문제조차 해결하지 못해서 그것이야말로 큰 고통일겁니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;메타인지로&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A9%94%ED%83%80%EC%9D%B8%EC%A7%80%EB%A1%9C&quot; aria-label=&quot;메타인지로 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메타인지로&lt;/h3&gt;
&lt;p&gt;저희는 개발자로써 성장하기 위해 항상 끊임없이 고민하고, 도전해야합니다. &lt;strong&gt;단순히 머릿속에 지식을 넣고 암기하는 것이 학습의 주목적이 되선 안된다고 생각합니다.&lt;/strong&gt; 능동적으로 배움에 참여하여, 그 누구보다 시끄럽게 소통하고, 많이 고민하면서 머리가 아파야합니다.&lt;/p&gt;
&lt;h3 id=&quot;글쓰기로-지식을-인출하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%80%EC%93%B0%EA%B8%B0%EB%A1%9C-%EC%A7%80%EC%8B%9D%EC%9D%84-%EC%9D%B8%EC%B6%9C%ED%95%98%EA%B8%B0&quot; aria-label=&quot;글쓰기로 지식을 인출하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;글쓰기로 지식을 인출하기&lt;/h3&gt;
&lt;p&gt;앞선 자료를 살펴봤듯이, 누군가에가 가르치고 설명하며 배움에 과정에 능동적으로 참여하는 것은 높은 학습효과를 보인다고 했었습니다. 이를 블로깅, 즉 글쓰기로 실천해봅시다. 즉, 블로깅이야말로 &lt;code class=&quot;language-text&quot;&gt;메타인지&lt;/code&gt; 를 적극 실천할 수 있는 아주 좋은 학습수단이라고 생각합니다. &lt;strong&gt;같은 문제를 겪을 다른 분들을 위해, 강사처럼 설명하는 글쓰기 방법을 실천해봅시다.&lt;/strong&gt; 지금부터 아래에서 계속 글쓰기 방법에 대해 자세히 소개해보고자 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;글을-꾸준히-써야하는-이유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%80%EC%9D%84-%EA%BE%B8%EC%A4%80%ED%9E%88-%EC%8D%A8%EC%95%BC%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0&quot; aria-label=&quot;글을 꾸준히 써야하는 이유 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;글을 꾸준히 써야하는 이유&lt;/h2&gt;
&lt;h3 id=&quot;자기-객관화를-통한-성장하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EA%B8%B0-%EA%B0%9D%EA%B4%80%ED%99%94%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%84%B1%EC%9E%A5%ED%95%98%EA%B8%B0&quot; aria-label=&quot;자기 객관화를 통한 성장하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자기 객관화를 통한 성장하기&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/f2034c8f-7a63-46d9-b733-0a5c72514cca/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&quot;장님 코끼리 만지기&quot; 라는 비유는, &lt;code class=&quot;language-text&quot;&gt;맹인모상(盲人摸象)&lt;/code&gt; 이라는 말로나온 우화입니다. 사람은 누구나 본인이 아는만큼만 아는척하고 고집하기 마련이죠. &quot;장님 코끼리 만지기&quot; 처럼, 저희는 자기자신에 대해 정확히 알지 못합니다. 메타인지가 활성화되지 않은 사람은 본인이 무엇을 잘못했으며, 앞으로 무엇을 해야하는지 알지 못합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;다른 이들에게 보여주고 설명하는 글을 쓰다보면, 스스로 &quot;왜&quot; 를 묻게되고, 이는 자연스래 자기 객관화로 이어집니다.&lt;/strong&gt; 본인에 대한 인식을 높인다면 본인이 무엇을 해야하는지 항상 알아차리고, 그 일에 항상 장기간 몰입할 수 있게됩니다.&lt;/p&gt;
&lt;h3 id=&quot;성장의-정량적-지표&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%B1%EC%9E%A5%EC%9D%98-%EC%A0%95%EB%9F%89%EC%A0%81-%EC%A7%80%ED%91%9C&quot; aria-label=&quot;성장의 정량적 지표 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;성장의 정량적 지표&lt;/h3&gt;
&lt;p&gt;글은 기록이자 성장의 지표입니다. 장기간 모여서 쌓인 지식들은 더욱이 처계화되며 성장의 큰 힘을 발휘합니다. 기록의 습관은 &lt;strong&gt;머릿속에서 겉도는 지식들을 체계화시켜주며, 성장의 정량적 지표가 되기도합니다.&lt;/strong&gt; 장기간 학습을 이어가다가 성장이 정체된것 같은 생각이 든다면, 과거 열심히 작성했던 포스트들을 돌이켜봅시다. 이전 포스트를 보면 &lt;strong&gt;&quot;아, 예전의 나는 이런것도 학습 했었구나. 이런 고민과 생각들로 많은 어려움을 겪었는데, 지금의 나는 이만큼 성장해있구나!&quot;&lt;/strong&gt; 라는 생각을 가질 수 있게 됩니다. &lt;strong&gt;즉, 열심히 쌓아온 학습 기록들은 학습의 정량적 지표가 되며, 본인의 꾸준한 성장에 큰 동기부여가 되기도합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;지식을-인출하기-위한-최고의-수단&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%80%EC%8B%9D%EC%9D%84-%EC%9D%B8%EC%B6%9C%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%EC%B5%9C%EA%B3%A0%EC%9D%98-%EC%88%98%EB%8B%A8&quot; aria-label=&quot;지식을 인출하기 위한 최고의 수단 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;지식을 인출하기 위한 최고의 수단&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;글쓰기는 메타인지를 언제 어디서든 손쉽게 활성화하기 가장 쉬운 최고의 수단&lt;/strong&gt;이라고 생각합니다. 학습과정에서 배운것을 본인만의 언어로 풀어내는 습관은 메타인지의 핵심이라고 봅니다. 이를 확인하기 위한건 글쓰기만큼 접근성이 용이한 것이 없다고 생각합니다.&lt;/p&gt;
&lt;p&gt;남을 위해 지식을 설명하다보면, 내가 부족한게 무엇인지 금방 알아차릴 수 있습니다. 이보다 더 좋은 수단은 스터디, 토론등에 열심히 참여하는것이지만, 현실적으로 24시간 다른 사람들과 함께 있는다는 것은 힘들겠죠. &lt;strong&gt;이렇듯 글쓰기는 본인이 원하는 상황, 시간, 장소에 구애받지 않고 메타인지를 활성화하기 위한 최고의 수단입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;인각의 망각의 동물입니다. 아무리 효과적으로 공부해도 단 한번만 학습하면 결국 금방 잊게됩니다. 따라서 한번 학습할 때 오랫동안 기억에 남을 수 있어야하죠. 글쓰기를 실천하면 메타인지는 몰론이고, 추후 본인이 지식이 휘발되어도 열심히 작성한 글을 다시 돌이켜보며 금방 기억을 되찾을 수도 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;효과적인-글쓰기를-작성하기-위한-방법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%9A%A8%EA%B3%BC%EC%A0%81%EC%9D%B8-%EA%B8%80%EC%93%B0%EA%B8%B0%EB%A5%BC-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%EB%B0%A9%EB%B2%95&quot; aria-label=&quot;효과적인 글쓰기를 작성하기 위한 방법 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;효과적인 글쓰기를 작성하기 위한 방법&lt;/h2&gt;
&lt;h3 id=&quot;공유하고-설명하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B3%B5%EC%9C%A0%ED%95%98%EA%B3%A0-%EC%84%A4%EB%AA%85%ED%95%98%EA%B8%B0&quot; aria-label=&quot;공유하고 설명하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;공유하고, 설명하기&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ab9165ad-c313-4a1f-a5a2-7f75502df19d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;위 내용을 읽었다면 이 말도 무엇인지 알아차릴 수 있을겁니다. 그날 공부한 개념이 있다면 잘 모르는 다른 이들도 이해할 수 있는 글을 쓰는것이 최고의 학습법이라고 생각합니다. 저는 지금껏 &lt;strong&gt;평균 3일에 하나의 글을 작성하니, 어느새 메타인지가 자연스래 향상되어 있었고 누군가에게 설명하고, 토론하는 습관이 길들어져 있게 되었습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;의도적으로-성장을-위한-피드백의-수단으로-만들기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%98%EB%8F%84%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%84%B1%EC%9E%A5%EC%9D%84-%EC%9C%84%ED%95%9C-%ED%94%BC%EB%93%9C%EB%B0%B1%EC%9D%98-%EC%88%98%EB%8B%A8%EC%9C%BC%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0&quot; aria-label=&quot;의도적으로 성장을 위한 피드백의 수단으로 만들기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;의도적으로 성장을 위한 피드백의 수단으로 만들기&lt;/h3&gt;
&lt;p&gt;기록하는 습관을 길들이는 것은 정말 좋습니다. 하지만 &lt;strong&gt;기왕이면 글쓰기가 나를 크게 성장시키는 수단 중 하나입니다. 단순 일지, 메모장 형태로 적는것은 나를 크게 성장시키기 어렵습니다.&lt;/strong&gt; &lt;a href=&quot;https://velog.io/@msung99/%EB%8A%A5%EB%8F%99%EC%A0%81%EC%9D%B8-%ED%95%99%EC%8A%B5%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%B8%94%EB%A1%9C%EA%B9%85-%EC%84%A4%EB%AA%85%ED%95%98%EA%B8%B0%EC%9D%98-%ED%95%99%EC%8A%B5%ED%9A%A8%EA%B3%BC-p60yepgo&quot;&gt;책임감 있는 블로깅 : 설명하기의 학습효과와 능동적인 공부법&lt;/a&gt; 에서 말했듯이, 아무 의미없고 지식이 집적되지 않은 글에는 아무도 방문하지 않고, 굳이 블로그에 글을 작성할 필요가 없습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;혼자만 보기위한 오답노트, 일기를 적는것은 성장의 지체로 이어집니다. 성장의 핵심은 여러 사람들의 피드백입니다.&lt;/strong&gt; 자신의 자세한 생각을 블로그에 노출하지 않고서는 성장하기 어렵습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;좋은 성장 기회를 놓치기 쉬운 글쓰기 마인드들&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;내가 쓴 글을 다른 사람이 보면 어떻게하지?&lt;/li&gt;
&lt;li&gt;누군가 내 글에 지적을 하면 안되는데, 글을 비공개 처리할까?&lt;/li&gt;
&lt;li&gt;비난의 댓글이 달리는건 너무 무섭다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;잡담하고-토론하기-위한-글을-작성하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%A1%EB%8B%B4%ED%95%98%EA%B3%A0-%ED%86%A0%EB%A1%A0%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%EA%B8%80%EC%9D%84-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0&quot; aria-label=&quot;잡담하고 토론하기 위한 글을 작성하기 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;잡담하고, 토론하기 위한 글을 작성하기&lt;/h3&gt;
&lt;p&gt;본인만을 위한 글을 작성하지 맙시다. 그 마인드는 본인의 성장을 가로막는 행위가 다름 없습니다. 앞서 말했듯이, 메타인지의 활성화는 시끄럽게 떠들고, 잡담하고, 소통하며, 토론해야합니다. 능동적으로 배움에 참여해야하는 습관이 길들어져야하고, 지&lt;strong&gt;식을 습득하는 과정이 힘들고 고통스러워야만 메타인지가 더 향상되는 것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;저처럼-독학하는-모든-분들에게-꼭-전하고-싶은-말&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%80%EC%B2%98%EB%9F%BC-%EB%8F%85%ED%95%99%ED%95%98%EB%8A%94-%EB%AA%A8%EB%93%A0-%EB%B6%84%EB%93%A4%EC%97%90%EA%B2%8C-%EA%BC%AD-%EC%A0%84%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%9D%80-%EB%A7%90&quot; aria-label=&quot;저처럼 독학하는 모든 분들에게 꼭 전하고 싶은 말 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;저처럼 독학하는 모든 분들에게 꼭 전하고 싶은 말&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/6d4a8b65-59cf-4f96-bb42-e8fd2bf765ba/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;독학의-한계로-인해-혼자서-힘들었던-경험들&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%85%ED%95%99%EC%9D%98-%ED%95%9C%EA%B3%84%EB%A1%9C-%EC%9D%B8%ED%95%B4-%ED%98%BC%EC%9E%90%EC%84%9C-%ED%9E%98%EB%93%A4%EC%97%88%EB%8D%98-%EA%B2%BD%ED%97%98%EB%93%A4&quot; aria-label=&quot;독학의 한계로 인해 혼자서 힘들었던 경험들 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;독학의 한계로 인해 혼자서 힘들었던 경험들&lt;/h3&gt;
&lt;p&gt;지금껏 저도 모든 프로그래밍 학습을 독학으로 이어왔습니다. 여태껏 컴퓨터 학원, 교육기관, 회사등의 도움을 받지 않아서 배움의 한계를 느끼고 있는게 사실입니다. &lt;strong&gt;주변에 좋은 교육기관, 회사에서 멘토와 사수들의 도움을 받으며 엄청난 속도로 빨리 성장해가는 사람들을 보면 자존감이 떨어지고, 타인과 저를 비교하기 시작했었어요.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;하지만 독학이니 &quot;난 열심히 해도 성장할 수 없어!&quot; 라며 가만히 아무것도 안하고 누군가에게 도움을 받기를 계속 가만히 기다려야할까요? 누군가에게 도움을 받는 환경에 있는가 여부와 별개로, &lt;strong&gt;본인만의 밀도있는 학습법과 성장방법을 찾지못한다면 향후 어떤 노력을 이어가도 큰 성장을 기대하기란 어려울 수 있다고 생각합니다.&lt;/strong&gt; 본인만의 올바른 평생 학습법을 찾고, 깊게 고민하는 것은 인생에서 꼭 필요한 과정이라고 생각합니다.&lt;/p&gt;
&lt;p&gt;또한 힘든 과정들, 자존감 하락, 슬럼프등은 성장과정속에서 자연스래 존재하는 과정입니다. 저도 학습법을 찾는데까지 당연히 힘들고 많은 시행착오가 있었습니다. 학습법을 찾은 지금도 두려울 때가 많고, 방황할 때가 많이 있지많요. 하지만, &lt;strong&gt;그 힘든 순간은 금방 지나리라 굳게 믿고, 이럴때마다 과거의 저를 돌이켜보며 지금의 제가 얼마나 성장해있는지 살펴봐요.&lt;/strong&gt; 특히 과거의 작성했던 포스트들을 돌이켜보며, 현재의 나는 얼만큼 성장해있는지 살펴보며 다시 자존감을 되찾고자 노력합니다.&lt;/p&gt;
&lt;h3 id=&quot;많은-분들에게-용기와-동기부여가-되었으면하는-바람&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%8E%EC%9D%80-%EB%B6%84%EB%93%A4%EC%97%90%EA%B2%8C-%EC%9A%A9%EA%B8%B0%EC%99%80-%EB%8F%99%EA%B8%B0%EB%B6%80%EC%97%AC%EA%B0%80-%EB%90%98%EC%97%88%EC%9C%BC%EB%A9%B4%ED%95%98%EB%8A%94-%EB%B0%94%EB%9E%8C&quot; aria-label=&quot;많은 분들에게 용기와 동기부여가 되었으면하는 바람 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;많은 분들에게 용기와 동기부여가 되었으면하는 바람&lt;/h3&gt;
&lt;p&gt;저처럼 열심히 무언가를 배워가고 싶고, 혼자서 많은 시도를 해봤지만 성장이 정체되었다고 느낀 분들이 분명히 많다고 생각합니다. &lt;strong&gt;저도 같은 입장으로써 지금껏 다양한 시도를 하며 지금도 방황을 많이 하고 있어요.&lt;/strong&gt; 하지만 분명 본인에게 재밌는 학습방법이 찾는다면 조금 더 불안감이 해소되지 않을 것 같나요? &lt;strong&gt;저는 프로그래밍을 더욱이 재밌게 공부할 수 있는 요소가 생긴다면, 분명 언젠간 큰 성장을 이룰 수 있을것이라고 굳게 믿고있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;제가 뭐라고 이런 글을 작성할까요? &lt;strong&gt;저에겐 화려한 스팩이 있는것도 아니고, 그저 제 꿈을향해 열심히 달려가고있는 대학생에 불과해요. 하지만 저처럼 어려움에 쳐해있고 많은 방황을 하고있는 분들에게 용기가 되었으면 하는 바람에 현재 글을 작성하게 되었습니다.&lt;/strong&gt; 본인만의 좋은 경험이 있다면 혼자 간직하지 말고, 공유하고 함께 성장하면 더 좋잖아요?😉 제 글이 큰 도움이 되었을진 몰라도, 동기부여가 되길하는 바람과 함께 이만 글을 마무리짓겠습니다 😆😆&lt;/p&gt;
&lt;p&gt;궁금하신 점, 또는 함께 소통하고 싶은 점이 있다면 언제든 댓글이나 개인적인 메일로 컨텍해주시면 감사하겠습니다!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[수평확장과 수직확장의 개념, 프록시 서버란?]]></title><description><![CDATA[Proxy? 어떤 Proxy 인가? 프록시란 너무 범용적인 단어입니다. Spring Proxy, Proxy 패턴, Network Proxy 등에서 널리 사용되는 단어인데, 이번에 저희가 살펴볼 내용은 Network Proxy 입니다. Proxy…]]></description><link>https://haon.site/haon/server/extend/</link><guid isPermaLink="false">https://haon.site/haon/server/extend/</guid><pubDate>Mon, 24 Jan 2022 00:00:00 GMT</pubDate><content:encoded>&lt;hr&gt;
&lt;h2 id=&quot;proxy-어떤-proxy-인가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#proxy-%EC%96%B4%EB%96%A4-proxy-%EC%9D%B8%EA%B0%80&quot; aria-label=&quot;proxy 어떤 proxy 인가 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Proxy? 어떤 Proxy 인가?&lt;/h2&gt;
&lt;p&gt;프록시란 너무 범용적인 단어입니다. Spring Proxy, Proxy 패턴, Network Proxy 등에서 널리 사용되는 단어인데, 이번에 저희가 살펴볼 내용은 Network Proxy 입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;proxy-server-가-없다면&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#proxy-server-%EA%B0%80-%EC%97%86%EB%8B%A4%EB%A9%B4&quot; aria-label=&quot;proxy server 가 없다면 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Proxy Server 가 없다면?&lt;/h2&gt;
&lt;p&gt;프록시 서버란 쉽게말해 &lt;strong&gt;&quot;통신을 대신 처리하는 서버&quot;&lt;/strong&gt; 입니다. 클라이언트와 서버간의 중계 서버로, 통신을 대리 수행하는 서버입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/ed9e1904-b061-47e7-8690-b49dcea19b51/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;몰론 위와 같이 프록시 서버가 없이 클라이언트와 서버가 직접적으로 인터넷을 통해 통신은 가능합니다. 다만 이런 방식을 채택하면 클라이언트의 IP주소가 유출될 수 있다는 위험성이 존재합니다. (자세한 내용은 조금뒤에 설명드리겠습니다)&lt;/p&gt;
&lt;p&gt;이 외에도 다양한 이유로 프록시 서버를 통해 클라이언트와 서버가 프록시 서버를 통해 간접적으로 통신하는데, 이들을 계속 알아봅시다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;정리) 프록시 서버 : 클라이언트와 서버간의 통신을 중계해서 대리 수행해주는 서버. 클라이언트와 서버는 직접적으로 통신하는 일을 방지할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;forward-proxy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#forward-proxy&quot; aria-label=&quot;forward proxy permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Forward Proxy&lt;/h2&gt;
&lt;p&gt;프록시서버는 크게 Forward Proxy, Reverse Proxy 2종류로 나뉩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/a1b711d5-bb10-4855-9e85-035413bb5392/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;일반적으로 부르는 &quot;프록시 서버&quot; 는 Forward Proxy 를 의미합니다. 예를들어 아래처럼&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;프록시 서버를 설정하라!&lt;/li&gt;
&lt;li&gt;외국에서 접속한 것처럼 테스트하도록 프록시 설정을 해주자!&lt;/li&gt;
&lt;li&gt;개인정보를 빼돌린 해커 이모씨는 IP추적을 방지하도록 proxy 설정을 했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이런 표현들에서 사용하는 프록시라는 것이 Forward Proxy 입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;특징1-캐싱&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B9%EC%A7%951-%EC%BA%90%EC%8B%B1&quot; aria-label=&quot;특징1 캐싱 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;특징1: 캐싱&lt;/h3&gt;
&lt;p&gt;Forward Proxy 의 특징중 하나는 바로 캐싱입니다. 캐싱이란 &lt;strong&gt;자주 요청하는 결과 데이터를 캐시에 저장해놓고, 해당 데이터를 재요청하는 경우 서버에 재요청하지 않고 프록시 서버의 캐시에 저장해놓은 데이터를 리턴하는 방식&lt;/strong&gt; 을 의미합니다.&lt;/p&gt;
&lt;p&gt;예를들어 첫번째 클라이언트가 가요차트 1등 그룹이 누구인지 정보를 얻고싶어서, 서버에게 요청을 보냈다고 해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/023bfb30-a641-4296-904a-78d24a6c8619/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;여러개의 서버중 가요차트 정보를 제공해주는 담당 서버는 클라이언트가 원하는 정보를 아래처럼 제공해주겠죠? Forward Proxy 서버와 인터넷을 통해 정보를 주고받을 겁니다.&lt;/p&gt;
&lt;p&gt;그런데 이때 프록시 서버는 그냥 단순히 원하는 정보를 제공해주는 것이 아닙니다. 캐시에다가 가요차트 1등 그룹이 누구인지를 저장해놓는 것이죠.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/e7c5ce52-de65-4aeb-83f3-cb33a9d6c900/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그러고 추후 여러 클라이언트들이 가요차트 1등 그룹이 누군지를 알고 싶을때 프록시 서버는 캐시에 저장해놓았던 데이터를 제공해주는 것입니다. 즉, 서버까지 귀찮게 도달하지 않더라도 캐시에 저장해놓은 데이터를 프록시 서버가 대신 제공해주는 것이죠.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/860e3f65-a7b5-418d-b1e9-e4632cb026d9/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이러한 캐싱의 특징으로 얻을 수 있는 장점은 다음과 같습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;전송 시간절약&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;불필요한 외부 전송을 하지 않아도 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;외부 요청이 감소한다 : 네트워크 병목현상 감지&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;인터넷, 서버를 통하지 않기 떄문에 전송 시간이 절약됩니다. 또한 불필요한 외부 전송을 하지 않아도 되고 외부 요청이 감소됨으로써 네트워크 병목현상을 방지할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;특징2-익명성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B9%EC%A7%952-%EC%9D%B5%EB%AA%85%EC%84%B1&quot; aria-label=&quot;특징2 익명성 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;특징2: 익명성&lt;/h3&gt;
&lt;p&gt;다음으로는 익명성이라는 특징이 있습니다. 익명성을 지닌다는 것은 &lt;strong&gt;클라이언트가 보낸 요청을 감춘다&lt;/strong&gt;는 의미입니다.&lt;/p&gt;
&lt;p&gt;클라이언트가 서버로 직접 호출할때는 저희(클라이언트)의 IP주소, 장비 정보, OS정보등을 그대로 서버에게 전달해야합니다.&lt;/p&gt;
&lt;p&gt;만일 Forward Proxy 를 사용한다면 저희 클라이언트가 요청했지만 마치 Forward Proxy 가 요청을 한 것처럼 서버들에게 Forward Proxy 의 정보들을 전달해 줄 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/2b5ab61b-ad4f-480c-8a9b-563e0263c68f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;즉, 클라이언트의 IP주소 같은 정보를 서버에 직접 제공하지 않고, 대신 Forward Proxy 의 정보를 제공하는 것이죠.&lt;/strong&gt; 이게 익명성입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;서버는 응답받은 요청을 누가 보냈는지 알지 못하게 됩니다.&lt;/strong&gt;
예를들어 서버가 받은 요청 IP 는 곧 proxy IP 입니다. (서버가 받은 요청 IP = proxy IP)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;reverse-proxy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reverse-proxy&quot; aria-label=&quot;reverse proxy permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reverse Proxy&lt;/h2&gt;
&lt;p&gt;Reverse Proxy 는 Forward Proxy 과 거의 유사하나, 큰 차이점을 뽑자면 Forward Proxy 와 달리 인터넷-서버 사이에 위치하고 있다는 점입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/cf647573-851b-406a-8989-24f63dc3797d/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이들의 특징도 간단히 알아봅시다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;특징1--캐싱&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B9%EC%A7%951--%EC%BA%90%EC%8B%B1&quot; aria-label=&quot;특징1  캐싱 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;특징1 : 캐싱&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Forward Proxy 와 동일하게 캐싱이 가능합니다.&lt;/strong&gt; 그로 인해 얻어내는 장점도 동일하죠. 캐싱이 무엇인지는 앞서 살펴봤으니 자세한 설명은 생략하겠습니다!&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;특징2-보안&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B9%EC%A7%952-%EB%B3%B4%EC%95%88&quot; aria-label=&quot;특징2 보안 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;특징2: 보안&lt;/h3&gt;
&lt;p&gt;다음 특징은 보안적이다 라는 것입니다. &lt;strong&gt;서버의 정보를 클라이언트로부터 숨길 수 있다는 점&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4d2763b9-380e-4915-bb7d-0f9e93c90662/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;클라이언트는 요청을 할때 어떤 서버에 요청을 하는지, 또 해당 서버의 정보는 무엇인지 직접 알지 못합니다. 대신 클라이언트 입장에서 서버인 Reverse Proxy 에게 요청을 전달합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;클라이언트들은 Reverse Proxy 를 실제 서버라고 생각하고 요청을 합니다. 따라서 실제 서버들의 IP는 클라이언트로부터 안전하게 보호됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;정리해보자면 아래와 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트는 reverse proxy 를 실제 서버라고 생각하고 요청한다.&lt;/li&gt;
&lt;li&gt;실제 서버의 IP가 유출되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;특징3-로드밸런싱&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B9%EC%A7%953-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1&quot; aria-label=&quot;특징3 로드밸런싱 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;특징3: 로드밸런싱&lt;/h3&gt;
&lt;p&gt;다음으로 로드밸런싱이 가능하다는 점입니다. 로드밸런싱은 상황에 따라서 할지말지를 결정해야합니다. (하는 경우도 있고, 하지 않는 경우도 있습니다. 선택적임)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;로드밸런싱이란 무엇일까요? 이것도 무엇인지 알아봅시다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;로드밸런싱&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1&quot; aria-label=&quot;로드밸런싱 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로드밸런싱&lt;/h2&gt;
&lt;p&gt;로드밸런싱(Load Balancing) 이란 쉽게 말하면 부하분산입니다. &lt;strong&gt;해야할 작업 및 요청을 적절히 분배시켜서 서버의 부하를 분산시키는 것입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;로드밸런서란 여러대의 서버가 분산(나누어) 처리할 수 있도록 요청을 나누어주는 서비스다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;로드밸런싱의-등장배경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1%EC%9D%98-%EB%93%B1%EC%9E%A5%EB%B0%B0%EA%B2%BD&quot; aria-label=&quot;로드밸런싱의 등장배경 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로드밸런싱의 등장배경&lt;/h3&gt;
&lt;p&gt;저희는 왜 로드밸런싱을 써야하며? 이것은 왜 Reverse Proxy 에서 활용되는 것일까요?&lt;/p&gt;
&lt;p&gt;예를들어 최대 10명의 요청을 동시간대에 처리가능한 서버를 구축하고 배포했다고 가정해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/4992a4aa-f37d-44b6-b35e-e26422e238e2/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그런데 서비스 사용자가 많아져서 10명이 넘어가는 사람들이 요청을 보내는 경우가 발생한다면, 서버에 부여하는 부하가 커질겁니다. 과부하가 걸리는 것이죠.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이에따라 서버의 성능을 향상시켜서 문제는 임시적으로 해결 가능합니다. CPU 성능을 개선하고, 메모리도 넣어서 성능을 좋게 만들어서 해결하는 것이죠.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/5008a0dd-b93f-4a7a-96c5-e351478fcc9b/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;그러나 사용자가 100만명이 넘어가면 어떨까요? 그때는 메모리에 꽃을 수 있는 소켓도 한정적입니다. 이떄 바로 로드밸런서를 적용하여, 서버를 1대가 아닌 100대로 늘리고 클라이언트로 부터 오는 각 요청을 골구로 서버들에 분산시켜주는 것이죠.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/msung99/post/44fa8f5b-8790-43a4-a8be-3ef0cf80be5e/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;로드밸런싱의-특징과-장점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1%EC%9D%98-%ED%8A%B9%EC%A7%95%EA%B3%BC-%EC%9E%A5%EC%A0%90&quot; aria-label=&quot;로드밸런싱의 특징과 장점 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로드밸런싱의 특징과 장점&lt;/h2&gt;
&lt;p&gt;앞서 로드밸런싱을 알아봤는데, 특징을 정리하고 추가적인 장점을 나열해보자면 다음과 같습니다.&lt;/p&gt;
&lt;h3 id=&quot;-많은-트래픽을-어떻게-대처할까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-%EB%A7%8E%EC%9D%80-%ED%8A%B8%EB%9E%98%ED%94%BD%EC%9D%84-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8C%80%EC%B2%98%ED%95%A0%EA%B9%8C&quot; aria-label=&quot; 많은 트래픽을 어떻게 대처할까 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;🤔 많은 트래픽을 어떻게 대처할까?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Scale up: 기존 서버의 성능을 높인다 (비용도 같이 올라간다)&lt;/li&gt;
&lt;li&gt;Scale out: 여러 대의 서버를 두어 트래픽을 분산시킨다.(물리적 or 논리적)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;여기서 저는 비용적인 부분을 생각해 논리적인 Scale out 방식을 생각했습니다.
Scale out을 하기 위해서 무조건 해야 하는 일이 로드밸런싱입니다!&lt;/p&gt;
&lt;h3 id=&quot;트래픽을-감당하는-장점-이외의-장점--무중단-배포&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%8A%B8%EB%9E%98%ED%94%BD%EC%9D%84-%EA%B0%90%EB%8B%B9%ED%95%98%EB%8A%94-%EC%9E%A5%EC%A0%90-%EC%9D%B4%EC%99%B8%EC%9D%98-%EC%9E%A5%EC%A0%90--%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC&quot; aria-label=&quot;트래픽을 감당하는 장점 이외의 장점  무중단 배포 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;트래픽을 감당하는 장점 이외의 장점 : 무중단 배포&lt;/h3&gt;
&lt;p&gt;로드밸런싱을 통해 &lt;strong&gt;무중단 서비스와 배포&lt;/strong&gt;가 가능해집니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버는 갑자기 다운될 수도 있고, 배포를 하게되면 서버가 다운이됩니다. 인프라를 한번이라도 경험하신 분들은 다 아실겁니다.&lt;/li&gt;
&lt;li&gt;당연스럽게도 여러 대의 서버로 나누게 되면 하나의 서버가 다운되어도 실제 클라이언트는 알 수 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;로드밸런싱의-종류&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1%EC%9D%98-%EC%A2%85%EB%A5%98&quot; aria-label=&quot;로드밸런싱의 종류 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로드밸런싱의 종류&lt;/h2&gt;
&lt;h3 id=&quot;1-하드웨어에서의-로드밸런싱&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4%EC%97%90%EC%84%9C%EC%9D%98-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1&quot; aria-label=&quot;1 하드웨어에서의 로드밸런싱 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 하드웨어에서의 로드밸런싱&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;로드밸런서의 종류는 네트워크 계층의 7 Layer 를 기준으로 나누어져있다고 합니다. 저희는 이중에 L4, L7 정도만 알고 넘어갑시다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;h4&gt;L4 : Transport Layer, 그러니까 IP와 Port 레벨에서 로드 밸런싱하는 것&lt;/h4&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ex) 예를들어 http: //msung99.velog.io 에 접근시 서버 A와 B로 로드 밸런싱을 해주는것이다.(나눠줌)&lt;/p&gt;
&lt;p&gt;어떤 사이트로 들어갔을 때 우리가 80번 포트로 접근을 하게되죠. 이를 그냥 서버 A와 B로 요청을 고르게 나눠주는 것이다. 이게바로 L4 로드밸런서입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;h4&gt;L7 : 어플리케이션 레벨에서 로드밸런싱을 하는것이다.&lt;/h4&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ex) 예를들어 http: //msung99.velog.io 에 접근시 &quot;http: //msung99.velog.io&quot; URL의 뒤에다 무엇을 붙이는가에 따라서, 혹은 Query Param 에 따라서 그 애플리케이션을 요청하는 방법에 따라서 어떤 서버로 로드밸런싱을 할지 결정하는게 L7이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-소프트웨어에서의-로드밸런싱--reverse-proxy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4%EC%97%90%EC%84%9C%EC%9D%98-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1--reverse-proxy&quot; aria-label=&quot;2 소프트웨어에서의 로드밸런싱  reverse proxy permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 소프트웨어에서의 로드밸런싱 : Reverse Proxy&lt;/h3&gt;
&lt;p&gt;이 방식은 기본적으로 Reverse Proxy 를 기반으로 동작합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Reverse Proxy 는 로드밸런싱만을 위해 개발된 프로그램이 아니기 때문에 기본적인 로드밸런싱의 기능만이 있지만 그만큼 비용적으로 저렴하고 구축이 쉽다는 장점이 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Reverse Proxy 의 로드밸런싱은 Nginx, HAProxy 등으로 기능 구현이 가능합니다. 이들은 추후 포스팅으로 조만간 다루어볼까 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Gatsby Starter Haon 작가 소개]]></title><description><![CDATA[백엔드 개발자, 이민성입니다. LINE+ 서버 개발자로 재직중인 이민성입니다. MSA, Kafka, Apache Kylin, MySQL, Redis…]]></description><link>https://haon.site/default/about/</link><guid isPermaLink="false">https://haon.site/default/about/</guid><pubDate>Sat, 01 Jan 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;백엔드-개발자-이민성입니다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%9D%B4%EB%AF%BC%EC%84%B1%EC%9E%85%EB%8B%88%EB%8B%A4&quot; aria-label=&quot;백엔드 개발자 이민성입니다 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;백엔드 개발자, 이민성입니다.&lt;/h1&gt;
&lt;p&gt;LINE+ 서버 개발자로 재직중인 이민성입니다. MSA, Kafka, Apache Kylin, MySQL, Redis 에 관심이 많으며, 사용하는 기술의 내부 동작원리를 깊게 공부하고자 합니다.&lt;/p&gt;
&lt;p&gt;글쓰기를 좋아하며, 하루 평균 1,100명 &amp;#x26; MAU 1.8만명이 방문해주시는 기술 블로그를 꾸준히 운영하고 있습니다. 2022년부터 Velog에서 480개가 넘는 글을 작성하고 독자들과 소통하며, 실시간 트렌드 1위 인기 작가로 자주 선정되었습니다. 2024년부터는 현재 보고 계신 기술 블로그를 직접 React, Gatsby 로 개발하고 운영중에 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;-careers&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-careers&quot; aria-label=&quot; careers permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;🚀 Careers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LINE Plus - Server Developer (LINE Ads DSP Platform 서버 개발팀)&lt;/strong&gt; (25.08 ~ )&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kakao Tech BootCamp 1기 백엔드 과정&lt;/strong&gt; (24.07 ~ 24.12)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;멋쟁이사자처럼 인하대학교 10기 부원/11기 백엔드 운영진&lt;/strong&gt; (22.03 ~ 23.12)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;인하대학교 컴퓨터공학과&lt;/strong&gt; (19.03 ~ 25.08)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-skills&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-skills&quot; aria-label=&quot; skills permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;📸 Skills&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Java, Kotlin&lt;/li&gt;
&lt;li&gt;Spring Boot (JPA, Spring Batch/Cloud)&lt;/li&gt;
&lt;li&gt;Redis, Kafka&lt;/li&gt;
&lt;li&gt;MySQL, Apache Kylin (Hadoop)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;️-writing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EF%B8%8F-writing&quot; aria-label=&quot;️ writing permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✍️ Writing&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@msung99/%ED%9A%8C%EA%B3%A0-%EA%B8%80%EC%93%B0%EA%B8%B0%EA%B0%80-%EC%A0%95%EB%A7%90-%EB%82%98%EC%97%90%EA%B2%8C-%EB%8F%84%EC%9B%80%EC%9D%B4%EB%90%A0%EA%B9%8C&quot;&gt;글쓰기를 통한 메타인지 프로그래밍 학습법을 여러분에게 공유합니다&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://haon.blog/%ED%9A%8C%EA%B3%A0/growth-learning/&quot;&gt;프로그래밍을 공부하며 능동적인 성장을 위한 글쓰기 학습방법&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://haon.blog/%ED%9A%8C%EA%B3%A0/effective-study-way/&quot;&gt;카카오테크 부트캠프에서 찾아낸 나만의 효과적인 프로그래밍 글쓰기 학습법&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://haon.blog/%ED%9A%8C%EA%B3%A0/growing-up-together/&quot;&gt;카카오테크 팀 프로젝트에서 발견한 진정한 의미의 함께 성장하기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-knowledge-sharing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-knowledge-sharing&quot; aria-label=&quot; knowledge sharing permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;💡 Knowledge Sharing&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@msung99/Nginx-1995%EB%85%84-%EC%97%AD%EC%82%AC%EB%B6%80%ED%84%B0-%EB%9C%AF%EC%96%B4%EB%B3%B4%EB%8A%94-Nginx-%EB%93%B1%EC%9E%A5%EB%B0%B0%EA%B2%BD%EB%B6%80%ED%84%B0-%EB%82%B4%EB%B6%80-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98%EA%B9%8C%EC%A7%80&quot;&gt;1995년 역사부터 뜯어보는 Nginx : 등장배경부터 내부 메커니즘까지&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@msung99/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%84-%EC%95%84%EB%8A%94%EC%B2%99-%ED%95%98%EC%A7%80-%EB%A7%90%EC%9E%90-%EC%9A%B0%EB%A6%AC%EA%B0%80-%EC%98%A4%ED%95%B4%ED%95%98%EA%B3%A0-%EC%9E%88%EC%97%88%EB%8D%98-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC-%EA%B3%B5%EC%9C%A0%ED%95%A9%EB%8B%88%EB%8B%A4&quot;&gt;객체지향을 아는척 하지 말자 : 우리가 오해하고 있었던 객체지향에 관하여 공유합니다&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://haon.blog/database/replication-architecture/&quot;&gt;MySQL 8.0 레플리케이션의 내부 동작 원리와 아키텍처 구성 방식&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@msung99/Redis-%EB%B6%84%EC%82%B0-%EB%9D%BD%EC%9D%84-%EA%B5%AC%ED%98%84%ED%95%B4-race-condition-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0&quot;&gt;[Redis] 분산락(Distribution Lock) 을 구현해 다중화 서버에서 발생하는 동시성 문제 제어하기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-presentation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-presentation&quot; aria-label=&quot; presentation permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;📢 Presentation&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=tJEbhINVPA8&quot;&gt;[10분 카카오 Tech Talk] 하온의 데이터베이스 인덱스를 통한 쿼리 성능 최적화 기초&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-projects&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-projects&quot; aria-label=&quot; projects permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;🎸 Projects&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/kakaotech-25/moheng&quot;&gt;moheng(모행) - 무장애 여행지 추천 서비스 (2024.08 ~ 2024.12)&lt;/a&gt; : 카카오테크 부트캠프에서 개발한 여행지 추천 서비스입니다. MySQL Replication, FailOver, PIT 백업.복구 정책, 무중단 배포 전략등을 설계했으며, 98% 넘는 테스트 커버리지를 달성하는데 기여하였습니다. 또한 &lt;a href=&quot;https://github.com/kakaotech-25/harmony-tech-blog.git&quot;&gt;팀 기술 블로그&lt;/a&gt;를 운영하며 지식 공유 세미나를 운영했습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/msung99/Gatsby-Starter-Haon.git&quot;&gt;gatsby-starter-haon (2024 ~ )&lt;/a&gt; : 개발자가 글쓰기에 적합한 블로그를 직접 개발하고, 공유하고자 시작한 오픈소스 프로젝트입니다.  React, Gatsby 로 개발했으며, 현재 보고 계신 블로그도 본 프로젝트를 통해 빌드되었습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[소프티어 대비 계획]]></title><description><![CDATA[1. 알고리즘 준비해야할 리스트 1-1. 알고리즘 꾸준한 풀이 1-2. 시간복잡도 암기 OS + 컴구 학교 수업 열심히 듣기 추가적으로, 혼공 책 + 구글링으로 이해안되는 부분 확실히 개념 짚기 네트워크 인강 찾아보기. 3-way-handshaking…]]></description><link>https://haon.site/default/plan/cs/</link><guid isPermaLink="false">https://haon.site/default/plan/cs/</guid><pubDate>Sat, 01 Jan 2000 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;1-알고리즘&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98&quot; aria-label=&quot;1 알고리즘 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 알고리즘&lt;/h2&gt;
&lt;p&gt;준비해야할 리스트&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1-1. 알고리즘 꾸준한 풀이&lt;/li&gt;
&lt;li&gt;1-2. 시간복잡도 암기&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;os--컴구&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#os--%EC%BB%B4%EA%B5%AC&quot; aria-label=&quot;os  컴구 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OS + 컴구&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;학교 수업 열심히 듣기&lt;/li&gt;
&lt;li&gt;추가적으로, 혼공 책 + 구글링으로 이해안되는 부분 확실히 개념 짚기&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;네트워크&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC&quot; aria-label=&quot;네트워크 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;네트워크&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;인강 찾아보기. 3-way-handshaking 위주의 핵심 개념을 공부해야함.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;db&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#db&quot; aria-label=&quot;db permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DB&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;시험 응시 1~2주 전쯤에 (약 5월 초중순쯤 부터) SQLD 문제집 풀이&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;혹시 모르니 인덱스, 정규화, 트랜잭션 정도의 핵심 개념을 공부하자. 깃허브 CS 레포지토리를 통해 키워드를 참고할 것.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;자료구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;자료구조 permalink&quot; class=&quot;anchor-header before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자료구조&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;옛날 학교 수업 강노 다시 훑어보기. 블로깅 했던거 봐도 충분할 듯.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>