<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Studying IT with cobinding</title>
    <link>https://cobinding.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 10 Jun 2026 05:14:11 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>sebinChu</managingEditor>
    <image>
      <title>Studying IT with cobinding</title>
      <url>https://tistory1.daumcdn.net/tistory/5318694/attach/abe32a272a03421a864c9570076d7a09</url>
      <link>https://cobinding.tistory.com</link>
    </image>
    <item>
      <title>[Redis] 운영환경에서 ElastiCache(Valkey)를 사용할 때 알아야 할 지식: 기본 구조, ClusterMode, MultiAZ, 업그레이드 동작방식</title>
      <link>https://cobinding.tistory.com/327</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 Redis To Valkey 업그레이드 전환을 주도하면서 실제 작업을 통해 배운 운영 환경에서의 ElastiCache 사용방법에 대해 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. ElastiCache 기본 구조&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1. Shard와 Node&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ElastiCache를 생성할 때 &lt;b&gt;Cluster Mode&lt;/b&gt;로 생성하면, Shard, Node 구성을 선택해야 한다. &lt;b&gt;Shard와 Node(Replicas Per Shard)&lt;/b&gt;의 역할은 각각 다음과 같다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.1.1. Shard&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 여러 샤드에 나눠 저장하여 &lt;b&gt;CPU, 메모리 사용량을 분산&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;각 Shard는 데이터에 대한 Slot을 나눠서 가진다&lt;/b&gt;.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;key-value Store인 ElastiCache는 각 key에 대해 Hash Slot을 할당하고, Slot Number를 기준으로 key-value 데이터 쌍을 관리한다. (여러 키가 Slot을 공유한다.) 이렇게 하는 이유는, &lt;b&gt;Shard가 추가되고 줄어들 때, Slot 단위로 데이터를 움직이게 하기 위해서&lt;/b&gt;다. 또한, &lt;b&gt;get/set 데이터 라우팅의 기준&lt;/b&gt;이 되기도 한다. Client가 명령을 보낼 때마다 &lt;span style=&quot;color: #ef5369;&quot;&gt;CRC16 (key) mod 16384&lt;/span&gt;로 Slot을 계산하고, &lt;b&gt;그 slot을 소유한 Shard로 명령을 보낸다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;만약 Key-Shard가 직접적으로 매핑된다면 Shard가 바뀔 때마다 매핑이 흔들리게 된다. Shard가 바뀔 때마다 전체 데이터의 이동이 필요해지기 때문이다. 따라서 Slot 단위로 묶고 &lt;b&gt;리샤딩 때도, 데이터는 Slot 단위로 이동&lt;/b&gt;한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1aXSG/dJMcafz7i68/URJlQl3hiMnBKMfnbz9Gek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1aXSG/dJMcafz7i68/URJlQl3hiMnBKMfnbz9Gek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1aXSG/dJMcafz7i68/URJlQl3hiMnBKMfnbz9Gek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1aXSG%2FdJMcafz7i68%2FURJlQl3hiMnBKMfnbz9Gek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;352&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;556&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.1.2. Node&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드는 key-value 데이터가 실제 RAM에 사는 물리적 단위다. 하나의 Shard는 &lt;b&gt;Master Node 1개 + Replica 0~5개&lt;/b&gt;의 Node로 구성된다. [위 그림 참고]&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Master(Primary):&lt;/b&gt; 해당 Slot 범위의 &lt;b&gt;모든 Wirte를 처리&lt;/b&gt;한다. Read도 가능하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Replica:&lt;/b&gt; Master를 async로 복제한 사본이다. 1) Read 분산에 사용되고, 2) Master 장애 시, Failover 대상(HA)이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2. Replica의 활용 2가지&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.2.1. HA (자동 Failover)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Multi-AZ 활성화, Replica가 1대 이상이 조건이다.&lt;/li&gt;
&lt;li&gt;Master 장애가 감지되면 &lt;b&gt;가장 최신 Replica를 Master로 승격&lt;/b&gt;한다. 뒤에서 자세히 설명할 테지만 이때 Node DNS Topology가 변경되어 Client Connection은 끊긴 후에 재연결되며 애플리케이션과 ElastiCache 사이의 Topology 갱신이 필요하다. (클라이언트 설정으로 컨트롤 가능하다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.2.2. Read 분산&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Replica로 Read 트래픽을 분산한다. 노드당 Read 부하를 감소시킬 수 있다.&lt;/li&gt;
&lt;li&gt;ElastiCache는 Reader Endpoint를 제공한다. 일반적으로는 Client의 &lt;span style=&quot;color: #ef5369;&quot;&gt;READONLY&lt;/span&gt; 옵션 등을 통해 직접 분산시킨다.&lt;/li&gt;
&lt;li&gt;만약 강한 일관성이 필요한 Read라면 Master로 보내야한다. (예컨대, Wirte 직후 즉시 Read가 필요함)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.3. 현재 인프라 구성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 우리 인프라에서는 &lt;u&gt;Master 1 + Replica 1&lt;/u&gt; 구조를 갖는다. 이렇게 한 이유는 SRE팀 인프라 표준 AZ 정책을 기반으로, 각 Zone 설정 통합을 위해서다. EKS 워커 노드들도 A, C Zone에만 배치되도록 표준화되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로, 경험 상 &lt;b&gt;Replica를 2대 이상&lt;/b&gt; 둘 가치가 있는 상황은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Failover 진행 중에도 Read 가용성을 유지해야 할 때&lt;/li&gt;
&lt;li&gt;Read 트래픽이 압도적으로 많은 패턴의 애플리케이션&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;서비스 특성을 고려해서 필요하다면 Replica를 2대 이상 둘 수 있도록, 사내 플랫폼에서 Self-Service 기능을 제공하고 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;2. Cluster Mode와 MultiAZ&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;2.1. Cluster Mode&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;클러스터 모드는 Sharding을 하냐/하지 않냐에 따라 나뉜다. 세부적으로 달라지는 부분들이 있지만 크게 중요한 건 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Cluster Mode Enabled를 통해 데이터를 여러 샤드에 분산해서 CPU/메모리 사용량을 분산시킬 수 있다. 즉, &lt;b&gt;수평 확장&lt;/b&gt;이 가능하다.&lt;/li&gt;
&lt;li&gt;Cluster Mode Enabled 클러스터의&lt;b&gt; Parameter Group&lt;/b&gt; 설정은 반드시 &lt;span style=&quot;color: #ef5369;&quot;&gt;cluster_enabled=true&lt;/span&gt; 값을 설정해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2. MultiAZ(Availability Zone)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AZ는 &lt;u&gt;&lt;b&gt;한 리전 안에서 물리적으로 분리된 데이터센터&lt;/b&gt;&lt;/u&gt;를 의미한다. 예를 들어 ap-northeast-2a, ap-northeast-2b 요런식...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AZ를 분리하면 한 AZ가 통째로 죽어도, 다른 AZ는 멀쩡하도록 분리되어있다. 서비스 안정성을 위해 각 존별로 노드를 띄우는 전략을 취하기도 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;3. ElastiCache 노드 유형, 노드 버전 업그레이드 작동 방식&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1. ElastiCache 노드 유형&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택할 수 있는 노드 유형은 대표적으로 t, m, r 시리즈가 있다. 큰 차이는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;T: 가장 저렴하고 사양이 낮은 노드 유형이다. T 시리즈는 저렴하긴 하지만 개발 환경에서만 권하고 있다. t 시리즈는 &lt;b&gt;필요할 때만 순간적으로 성능을 높이는 &lt;u&gt;Burst Credit&lt;/u&gt; 기반의 인스턴스&lt;/b&gt;이기 때문이다. 실제로 적은 사용량에 메모리가 충분함에도 불구하고 메모리 스왑이 지속적으로 발생하는 경우도 있었다.&lt;/li&gt;
&lt;li&gt;M: General Purpose. 메모리 집약적인 연산이 없는 일반적인 캐시의 경우, M 시리즈 사용을 권장한다.&lt;/li&gt;
&lt;li&gt;R: 메모리 성능에 특화된 유형으로, vCPU 당 메모리가 가장 많은 유형이다. 같은 사이즈(&lt;span style=&quot;color: #ef5369;&quot;&gt;.large&lt;/span&gt;)로 비교하면, R이 M보다 메모리가 약 2배 정도다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2. ElastiCache 노드 유형/버전 업그레이드 동작방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드 유형 업그레이드와 노드 버전 업그레이드는 기본적인 메커니즘이 같다. 이번에 Redis To Valkey 전환 작업을 주도하면서 가장 많이 살펴봤던 것이 해당 부분이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2.1. in-place 업그레이드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ElastiCache 노드 업그레이드는&lt;b&gt; in-place&lt;/b&gt; 방식으로 진행된다. [&lt;a href=&quot;https://docs.aws.amazon.com/whitepapers/latest/overview-deployment-options/in-place-deployments.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;참고 문서&lt;/a&gt;] 새로운 클러스터를 만들어서 데이터를 옮기는 것(blue-green 등)이 아니라, &lt;b&gt;지금 쓰는 클러스터를 그대로 둔 채&lt;/b&gt;로 노드 버전을 올리는 것이다. 애플리케이션 입장에서는 접속 Endpoints DNS가 바뀌지 않는다. 덕분에 클라이언트 재배포 없이 업그레이드가 가능하지만, 대신 순단은 감수해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2.2. 실제 동작 순서&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ugapH/dJMcaaewkOv/CkkPc0Y9n5x6PvzcsLnCYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ugapH/dJMcaaewkOv/CkkPc0Y9n5x6PvzcsLnCYK/img.png&quot; data-alt=&quot;Redis Note&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ugapH/dJMcaaewkOv/CkkPc0Y9n5x6PvzcsLnCYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FugapH%2FdJMcaaewkOv%2FCkkPc0Y9n5x6PvzcsLnCYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;537&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;676&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Redis Note&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2.3. 업그레이드와 Topology 갱신&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;in-place 방식대로 업그레이드를 진행한다면 짧은 순단(Write 실패)이 발생한다. 동기화 단계에서는 기존 노드가 정상적으로 트래픽을 처리하기 때문에 영향이 없고, &lt;b&gt;전환 시점에 한해서 기존 Primary로의 connection이 끊긴다.&lt;/b&gt; 실제 전환 시에 샤드당 단절 수준은 N초 정도였지만, 에러율은 최대 5분까지 이어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 connection을 위해 클라이언트는, &lt;b&gt;&lt;u&gt;재연결 후 Cluster Topology를 갱신&lt;/u&gt;해서(&lt;span style=&quot;color: #ef5369;&quot;&gt;CLUSTER SLOTS&lt;/span&gt;/ &lt;span style=&quot;color: #ef5369;&quot;&gt;CLUSTER NODES&lt;/span&gt;) 새 노드를 찾아야 한다. &lt;/b&gt;Redis To Valkey로의 전환을 권장하기 위해 아래와 같이 언어별 가이드를 만들어서 배포했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1368&quot; data-origin-height=&quot;1690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnKhVv/dJMb997IyKn/8lX3vaVUY9FjbKb3oSJgPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnKhVv/dJMb997IyKn/8lX3vaVUY9FjbKb3oSJgPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnKhVv/dJMb997IyKn/8lX3vaVUY9FjbKb3oSJgPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnKhVv%2FdJMb997IyKn%2F8lX3vaVUY9FjbKb3oSJgPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;741&quot; data-origin-width=&quot;1368&quot; data-origin-height=&quot;1690&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;경험으로 배운 운영 지식&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토폴로지 갱신이 느리거나 &lt;span style=&quot;color: #ef5369;&quot;&gt;MOVED/ASK Redirect&lt;/span&gt;를 따라가지 못하느 클라이언트는 짧은 순단동안 에러를 낸다.&lt;/li&gt;
&lt;li&gt;각 클라이언트별로 Redis Version과 호환성을 잘 체크해야한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;예컨대, go-redis v8은 Redis 6.x와 호환된다. Valkey 7.x로의 업그레이드를 위해서는 go-redis를 v9로 업그레이드하는 선행 작업이 요구된다. 실제로 클라이언트 라이브러리 업그레이드를 놓치고 Valkey 전환을 진행했을 때 Valkey 연결에 지속적인 Panic이 발생하기도 했었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780842953594&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;level&quot;:&quot;panic&quot;,&quot;error&quot;:&quot;got 4 elements in cluster info address, expected 2 or 3&quot;,&quot;time&quot;:&quot;2026-06-01T09:27:37Z&quot;,&quot;message&quot;:&quot;redis.New(session) failed&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Write보다 Read 연산이 압도적으로 많은 작업을 하는 클러스터라면, 미리 Replica를 늘리고 전환을 시도하는 것이 순단 영향을 많이 줄여줄 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론은 Client별 재연결/Topology Refresh, Max Redirec 설정 등&lt;span&gt;&amp;nbsp;&lt;/span&gt;클라이언트 설정을 미리 점검해두는 것이 순단 영향을 줄이는 것의 핵심이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 경우는 사내 배포 플랫폼에 AWS API를 구조적으로 구성하여 전사 모든 클러스터의 전환 과정이 동일했다. AWS 콘솔에서 직접 진행하는 경우는 전환 이후의 Parameter Group 설정이나 cluster_enabled같은 값이 의도대로 잘 넘어왔는지 확인하는 것도 좋을 듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DB/In-Memory DB</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/327</guid>
      <comments>https://cobinding.tistory.com/327#entry327comment</comments>
      <pubDate>Sun, 7 Jun 2026 23:58:04 +0900</pubDate>
    </item>
    <item>
      <title>[BackEnd] Index를 통해 DB 조회 성능 1.2만배 개선하기</title>
      <link>https://cobinding.tistory.com/326</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어느 날부터&amp;nbsp; 리소스를 조회하는 GET 요청에 대해 &lt;u&gt;적게는 20초 많게는 120초&lt;/u&gt;까지 조회가 오래 걸리는 현상이 생겼다. 이를 해결하기 위해 했던 작업들과 개선 결과를 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 간단한 프로젝트 구조 설명&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사내 배포 플랫폼에 기여하며 인프라 &amp;amp; 개발 업무 경험을 쌓고 있다. 주업무 중 하나는 AWS 리소스 관련 CRUD 개발인데, 프레임워크는 FastAPI를 사용하고 대부분의 요청은 Celery Task를 통해 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 생성과 같은 요청은 본질적으로 느리고 실패할 수 있는 작업이다. S3 버킷, Redis 클러스터, EC2 인스턴스 등 생성에 수십 초~수 분 소요되며, 특히 Redis의 경우 여러 단계를 순서대로 실행해야 하는 복잡한 워크플로우를 따라야 한다. (ex. Subnet Group &amp;rarr; Replication Group &amp;rarr; Tagging) 그렇기 때문에&lt;b&gt; 대부분의 요청은 Celery Task를 통해 실행&lt;/b&gt;되며, &lt;b&gt;리소스 상태를 추적하기 위해 이 값을 DB에 저장&lt;/b&gt;하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 문제 파악을 위한 Datadog APM 설정&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 이 문제 파악을 위해서 API 요청 흐름을 정리하고, 이 과정에서 느린 쿼리가 있는지 분석해보았다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;어느 날부터&amp;nbsp; 리소스를 조회하는 GET 요청에 대해 적게는 20초 많게는 120초까지 조회가 오래 걸리는 현상이 생겼다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 될 만한 로직은 다음 부분이었는데,&lt;/p&gt;
&lt;pre id=&quot;code_1772881367227&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solve_xxx (self, resource: Resource):
    &quot;&quot;&quot;ResourceGroup 내의 Resource 목록을 조회할 때 사용하는 함수
    relation(lazy-loading) 이 설정된 모델 애트리뷰트를 직접 참조해서 데이터 형태를 완성한다.
    &quot;&quot;&quot;

    # XXX: 현재는 단순 참조 로직으로 데이터를 완성하지만
    #      n+1 문제를 유발하므로 성능에 문제가 있는 경우 튜닝해야 함.
    #      리소스는 2env(alpha, prod) * 4region(kr, jp, ca, gb)
    #      총 8개의 리소스이므로 크게 문제는 없을 듯.

    for event in resource.events:
        event.celery_task&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQLArchemy 동작 방식에 따르면 &lt;span style=&quot;color: #ef5369;&quot;&gt;resource.events&lt;/span&gt;, &lt;span style=&quot;color: #ef5369;&quot;&gt;event.celery_task&lt;/span&gt;와 같이 &lt;b&gt;relationship 속성에 접근&lt;/b&gt;하면 DB 쿼리를 수행하게 된다. &lt;b&gt;위 코드에서 &lt;span style=&quot;color: #ef5369;&quot;&gt;celery_task&lt;/span&gt; 조회는 event 개수만큼 실행된다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 아래 두 가지 데이터 개수를 확인 후, 느린 쿼리에 대해 판단해야 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;resource별 event 개수 (resource_events 테이블)&lt;/li&gt;
&lt;li&gt;event별&amp;nbsp;celery_task&amp;nbsp;연결&amp;nbsp;여부&amp;nbsp;(celery_tasks&amp;nbsp;테이블)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;646&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p85Tf/dJMcaiWKQnX/qbzqAhphIEKnWH9ndxIhek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p85Tf/dJMcaiWKQnX/qbzqAhphIEKnWH9ndxIhek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p85Tf/dJMcaiWKQnX/qbzqAhphIEKnWH9ndxIhek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp85Tf%2FdJMcaiWKQnX%2FqbzqAhphIEKnWH9ndxIhek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;344&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;646&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 실수?를 한 건, 이 resource 별 event 개수에만 집중해서&lt;span style=&quot;color: #ef5369;&quot;&gt; `for event in resource.events`&lt;/span&gt;에 대한 쿼리만 분석했다. 그 결과는 800개 수준이었고 당연히 느린 쿼리가 아닐 것이라고 판단했다. 그래서 좀 더 세밀한 분석을 위해 데이터독 도움을 받고자 APM을 설정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Datadog APM 설정 및 Latency 분석&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;APM 설정을 위해 필요한 작업은 &lt;a href=&quot;https://docs.datadoghq.com/ko/tracing/guide/tutorial-enable-python-containers/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ddog 가이드 문서&lt;/a&gt;를 참고했다. 새삼 우리 팀의 플랫폼 &amp;amp; 모니터링 인프라가 잘 되어있다는 걸 느낀 점은 Datadog Agent Pod 설정과 환경변수 주입은 아주 쉽게 해결할 수 있었다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 Datadog 가이드 문서를 따라 Trace 설정&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Python ddtrace 라이브러리 추가&lt;/li&gt;
&lt;li&gt;DockerFile에서 실행 부분(CMD)에 ddtrace 패키지 사용 명령어 `ddtrace-run` 추가&lt;/li&gt;
&lt;li&gt;Datadog 태그 환경 변수 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1374&quot; data-origin-height=&quot;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/depEEN/dJMcagLszLr/Gta4giigNxpqacxdgfaCWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/depEEN/dJMcagLszLr/Gta4giigNxpqacxdgfaCWK/img.png&quot; data-alt=&quot;세팅해야 할 환경변수들이 Pod에 잘 설정되어 있는 점&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/depEEN/dJMcagLszLr/Gta4giigNxpqacxdgfaCWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdepEEN%2FdJMcagLszLr%2FGta4giigNxpqacxdgfaCWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;72&quot; data-origin-width=&quot;1374&quot; data-origin-height=&quot;166&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;세팅해야 할 환경변수들이 Pod에 잘 설정되어 있는 점&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xogfF/dJMcahQ7apg/BGentjSMH58HFHxwHfO3Ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xogfF/dJMcahQ7apg/BGentjSMH58HFHxwHfO3Ak/img.png&quot; data-alt=&quot;ddtrace-run 추가!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xogfF/dJMcahQ7apg/BGentjSMH58HFHxwHfO3Ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxogfF%2FdJMcahQ7apg%2FBGentjSMH58HFHxwHfO3Ak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;233&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ddtrace-run 추가!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 APM 설정 완료&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 데이터독 대시보드에서 관련 지표를 확인할 수 있다. 모니터링 준비가 끝나서 바로 느린 요청에 대해 확인을 시작했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2496&quot; data-origin-height=&quot;1264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brYukN/dJMcagYXP0V/pKLvx7RUH68zkISZGif861/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brYukN/dJMcagYXP0V/pKLvx7RUH68zkISZGif861/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brYukN/dJMcagYXP0V/pKLvx7RUH68zkISZGif861/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrYukN%2FdJMcagYXP0V%2FpKLvx7RUH68zkISZGif861%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;354&quot; data-origin-width=&quot;2496&quot; data-origin-height=&quot;1264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET 요청에 대해 22s가 걸리는데, 그 중에서 특정 쿼리들이 느린 점을 trace를 통해 확인할 수 있었다. trace를 통해 느린 쿼리를 확인해보니, 처음에 의심했던 로직에 문제가 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2134&quot; data-origin-height=&quot;954&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B9iWu/dJMcadgSMhc/bQm42SDS4TjikGkMya09O0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B9iWu/dJMcadgSMhc/bQm42SDS4TjikGkMya09O0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B9iWu/dJMcadgSMhc/bQm42SDS4TjikGkMya09O0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB9iWu%2FdJMcadgSMhc%2FbQm42SDS4TjikGkMya09O0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;313&quot; data-origin-width=&quot;2134&quot; data-origin-height=&quot;954&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3 Full Scan, 느린 쿼리 확인&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2192&quot; data-origin-height=&quot;1314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eAC36Z/dJMcac3jPTP/bANiHlaRPW5V8Khjr4i6E1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eAC36Z/dJMcac3jPTP/bANiHlaRPW5V8Khjr4i6E1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eAC36Z/dJMcac3jPTP/bANiHlaRPW5V8Khjr4i6E1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeAC36Z%2FdJMcac3jPTP%2FbANiHlaRPW5V8Khjr4i6E1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;420&quot; data-origin-width=&quot;2192&quot; data-origin-height=&quot;1314&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터독을 통해 확인한 느린 쿼리와 관련해서 비슷한 쿼리를 만든 다음, EXPLAIN을 통해 어떻게 쿼리가 동작하는지 확인해보았다. 아래 두 가지 결과를 통해 느린 쿼리의 원인임을 판단했다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;type=all 즉, full-scan&lt;/b&gt;(테이블의 모든 행을 읽음)한다. &amp;rarr; 테이블의 모든 행을 스캔해야 하기에, 데이터가 많아질 수록 성능 부하&lt;/li&gt;
&lt;li&gt;&lt;b&gt;show index&lt;/b&gt;를 했을 때 &lt;b&gt;primary_index&lt;/b&gt;만 생성되어있다. &amp;rarr; PK에 대한 primary_index만 생성되어있었다. 문제가 되는 task_id에 대한 인덱스는 존재하지 않는 상황이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 이 내용을 조합해보면, &lt;b&gt;리소스를 한번 조회할 때, task_id에 대해서 6만 행에 대한 full-scan이 발생하고 있었다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Index 추가 (SQLAlchemy, MySQL)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인 분석 후, 해결 방법은 해당 컬럼에 대한 index를 생성하는 것이었다. index 생성이 이 문제를 해결해줄 것이라고 판단한 기준은 다음과 같다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 Index 추가로 해결 가능한 이유는?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 컬럼에 대해 `&lt;span style=&quot;color: #ef5369;&quot;&gt;SELECT`만 빈번히, `UPDATE` 및 `DELETE` 쿼리는 거의 일어나지 않는&lt;/span&gt; 패턴이다.&lt;/li&gt;
&lt;li&gt;카디널리티가 높다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;task_id는 uuid같은 값을 가지고 있다. 카디널리티 확인을 위해 &lt;span style=&quot;color: #ef5369;&quot;&gt;null, &quot;&quot;, `GROUP BY`&lt;/span&gt;를 통한 &lt;b&gt;중복값을 확인&lt;/b&gt;하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 SQLAlchemy로 Index 추가하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB에서 바로 Index를 추가할 수 있지만, 그러면 &lt;b data-stringify-type=&quot;bold&quot;&gt;코드 &amp;lt;&amp;gt; 마이그레이션 히스토리 &amp;lt;&amp;gt; DB 간 정합성이 깨진다. 그래서&lt;/b&gt;&amp;nbsp;&lt;b data-stringify-type=&quot;bold&quot;&gt;모델 수정 + Alembic 마이그레이션&lt;/b&gt;을 통한 index 생성 작업을 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법이 가능한 이유는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-border=&quot;0&quot; data-indent=&quot;0&quot; data-list-tree=&quot;true&quot; data-stringify-type=&quot;unordered-list&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-stringify-border=&quot;0&quot; data-stringify-indent=&quot;0&quot;&gt;MySQL(InnoDB)에서는 UNIQUE&amp;nbsp; 제약 조건이 붙은 열에 대해서, secondary index를 자동 생성한다.&lt;/li&gt;
&lt;li data-stringify-border=&quot;0&quot; data-stringify-indent=&quot;0&quot;&gt;코드 수정을 통한 SQLAlchemy &lt;b&gt;모델 변경&lt;/b&gt; &amp;rarr; Alembic을 통한 &lt;b&gt;DDL Script 실행&lt;/b&gt; &amp;rarr;MySQL이 index 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 반영하기 위해 아래와 같이 코드를 수정 후, DB 마이그레이션을 진행했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;102&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k72eG/dJMcaibrG8u/Di77jlHqTvqh0klJSihys0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k72eG/dJMcaibrG8u/Di77jlHqTvqh0klJSihys0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k72eG/dJMcaibrG8u/Di77jlHqTvqh0klJSihys0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk72eG%2FdJMcaibrG8u%2FDi77jlHqTvqh0klJSihys0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;48&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;102&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 결과!&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2118&quot; data-origin-height=&quot;538&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKita8/dJMcaaqV2C6/pl8V84H9GXg03T1OmmDnCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKita8/dJMcaaqV2C6/pl8V84H9GXg03T1OmmDnCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKita8/dJMcaaqV2C6/pl8V84H9GXg03T1OmmDnCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKita8%2FdJMcaaqV2C6%2Fpl8V84H9GXg03T1OmmDnCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;203&quot; data-origin-width=&quot;2118&quot; data-origin-height=&quot;538&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 GET 응답 Latency가 5s 미만을 유지하게 되었다! 그리고 특히 해당 쿼리는 &lt;u&gt;&lt;b&gt;21.6s &amp;rarr; 1.68ms&lt;/b&gt;&lt;/u&gt; (약 1.2만배) 정도의 개선 결과가 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/doQd6W/dJMcacvxh4q/09OEKzhR7NYKl4FuLrIBdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/doQd6W/dJMcacvxh4q/09OEKzhR7NYKl4FuLrIBdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/doQd6W/dJMcacvxh4q/09OEKzhR7NYKl4FuLrIBdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdoQd6W%2FdJMcacvxh4q%2F09OEKzhR7NYKl4FuLrIBdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;187&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;362&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Dev/Backend</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/326</guid>
      <comments>https://cobinding.tistory.com/326#entry326comment</comments>
      <pubDate>Sun, 8 Mar 2026 10:56:42 +0900</pubDate>
    </item>
    <item>
      <title>[Redis] Redis 운영 지식: redis-cli 명령어 정리</title>
      <link>https://cobinding.tistory.com/325</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 사용하는 redis-cli를 미리 정리해두고, 장애 대응이나 모니터링 상황 시에 활용하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. ElastiCache Endpoints&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 Connection Endpoints&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 엔드포인트의 역할을 잘 이해하고, 특히 클러스터 모드일 때의 접근 방법을 기억하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2218&quot; data-origin-height=&quot;962&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blLZPn/dJMcahwHdhb/XD2uEy8pvMAJnBIWoYktp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blLZPn/dJMcahwHdhb/XD2uEy8pvMAJnBIWoYktp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blLZPn/dJMcahwHdhb/XD2uEy8pvMAJnBIWoYktp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblLZPn%2FdJMcahwHdhb%2FXD2uEy8pvMAJnBIWoYktp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;304&quot; data-origin-width=&quot;2218&quot; data-origin-height=&quot;962&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자의 상황은 클러스터 모드를 활성화하고 샤드 2, 노드 2 구성을 가진 ElastiCache Engine을 하나 준비해두었다. 이와 같이 &lt;b&gt;클러스터 모드가 활성화&lt;/b&gt;된 엔진이라면, 위 이미지처럼&amp;nbsp;&lt;b&gt;Configuration Endpoint&lt;/b&gt;를 주로 활용한다. 참고로, &lt;b&gt;클러스터 모드를 비활성화&lt;/b&gt;한&amp;nbsp;경우는 &lt;b&gt;Primary Endpoint&lt;/b&gt;가 생성되고, 해당 엔드포인트를 활용한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2222&quot; data-origin-height=&quot;752&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ch6kFa/dJMcafMqfb4/rq2E69lBSEK6mtgv8CLfo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ch6kFa/dJMcafMqfb4/rq2E69lBSEK6mtgv8CLfo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ch6kFa/dJMcafMqfb4/rq2E69lBSEK6mtgv8CLfo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fch6kFa%2FdJMcafMqfb4%2Frq2E69lBSEK6mtgv8CLfo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;237&quot; data-origin-width=&quot;2222&quot; data-origin-height=&quot;752&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 Shards and nodes 섹션에서 각 노드들의 Endpoint 또한 확인할 수 있다. 보통은 첫 번째 이미지의 Configuration Endpoints를 활용하고, &lt;b&gt;특정 샤드/노드에 대한 특수한 확인이 필요할 때만 이 개별 Endpoint&lt;/b&gt;를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;# Endpoints 정리!&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Configuration Endpoints:&lt;/b&gt; 클러스터 모드일 때 일반적으로 커넥션을 맺는 주소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Primary Endpoints:&lt;/b&gt; 클러스터 모드가 아닐 때 일반적으로 커넥션을 맺는 주소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Shards and nodes 섹션의 개별 Node에 대한 Endpoints:&lt;/b&gt; 특정 샤드/노드에 대한 커넥션을 맺는 주소&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2 redis-cli를 통해 엔드포인트에 연결&lt;/h3&gt;
&lt;pre id=&quot;code_1771767859959&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;redis-cli -h ${cluster-endpoint} -p 6379 -c&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;-c: &lt;/b&gt;여러 노드가 생성된 클러스터 모드 엔진의 경우, key가 여러 샤드/노드에 분산되어있다. 이 옵션을 통해 redis-cli가 자동으로 해당 키가 있는 노드에 접근할 수 있도록 리다이렉트를 해주기에, 클러스터 모드일 경우 이 옵션이 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2584&quot; data-origin-height=&quot;740&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nBfQN/dJMcadAYFRb/2dbps9NizuoGgwlOz1RPf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nBfQN/dJMcadAYFRb/2dbps9NizuoGgwlOz1RPf0/img.png&quot; data-alt=&quot;간단한 list, info 명령어로 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nBfQN/dJMcadAYFRb/2dbps9NizuoGgwlOz1RPf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnBfQN%2FdJMcadAYFRb%2F2dbps9NizuoGgwlOz1RPf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;200&quot; data-origin-width=&quot;2584&quot; data-origin-height=&quot;740&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;간단한 list, info 명령어로 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;# tls로 접속하기&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1772254794879&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;redis-cli --tls -h &amp;lt;host&amp;gt; -p &amp;lt;port&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Redis 리소스 상태 확인&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;pre id=&quot;code_1772254865063&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 전체 정보
info all

# 섹션 별 조회
info memory
info stats
info commandstats
info clients
info replication
info keyspace
info server
info persistence&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 INFO memory&amp;nbsp;&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 193px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;지표&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;의미&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;메모&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;used_memory_human&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;실제 사용 메모리&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;dataset(실제 key&amp;amp;value)&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;+ overhead(내부 사용 메모리)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;used_memory_rss_human&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;OS가 Redis 프로세스에 할당한 메모리&lt;br /&gt;(Resident Set Size)&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;mem_fragmentation_ratio&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;RSS/ used_memory&lt;br /&gt;&lt;b&gt;&amp;rarr; 메모리 &lt;span style=&quot;color: #ef5369;&quot;&gt;단편화&lt;/span&gt; 비율!&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;- 1.0~1.2 수준이면 정상이고, 1.5가 넘어가면 위험할 수 있다. 1.0보다 낮으면 스왑 발생&lt;br /&gt;&lt;br /&gt;&amp;rarr; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;config set &lt;span style=&quot;color: #ef5369;&quot;&gt;activedefrag&lt;/span&gt; yes/&lt;/span&gt; failover/캐시 사용 패턴 개선&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;used_memory_peak_human&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;지금까지 사용한 &lt;b&gt;최대 메모리&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;&lt;b&gt;- 피크를 기준으로 메모리 용량 산정해야한다.&lt;/b&gt;&lt;br /&gt;- slowlog get 10&lt;br /&gt;- info commandstats&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;used_memory_peak_perc&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;&amp;nbsp;현재 사용량이 피크 대비 몇 %인가&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 55px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 55px;&quot;&gt;max_memory_policy&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 55px;&quot;&gt;사용 가능한 최대 메모리 한도&lt;br /&gt;&amp;rarr; &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;이 값을 초과하면 Policy 동작&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 55px;&quot;&gt;- config set maxmemory 12gb&lt;br /&gt;- 시스템 메모리의 60~75% 수준으로 설정 권장 &amp;rarr; 나머지는 os, 버퍼 등에 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;mem_allocator&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;현재 사용 중인 메모리 할당자 정보&lt;br /&gt;- &lt;b&gt;대부분 jemalloc-5.x.x이고,&amp;nbsp;&lt;/b&gt;&lt;br /&gt;- 간혹 lib, tcmalloc&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;jemalloc: 기본이자 권장 할당자. 단편화 관리가 우수하고, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;activedefrag&lt;/span&gt; 기능이 jemalloc에서만 동작한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 INFO clients&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;지표&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;메모리&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;메모&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;connected_clients&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;현재 연결된 클라이언트 수&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;maxclients와 비교한다. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;max에 도달하면 전부 연결이 거부된다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;blocked_clients&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;블로킹 명령(blpop, brpop, xread block)으로대기 중인 클라이언트 수&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;# 자세한 클라이언트 정보 &amp;rarr; client list&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1772455877006&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;redis-cli CLIENT LIST

id=123 addr=10.0.0.5:54321 fd=8 name=order-service age=3600 idle=10 
flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 obl=0 oll=0 omem=0 cmd=get&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;idle: 마지막 명령어 이후 지난 시간 &amp;rarr; 좀비 커넥션 확인(보통은 주기적인 헬스체크를 통해 수백초까지 가지 않음)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Redis 3.2부터는 TCP keepalive가 활성화되어 약 300초로 설정되어 있다. (&lt;a href=&quot;https://redis.io/docs/latest/develop/reference/clients/#tcp-keepalive&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;ElastiCache의 경우 Parameter Group에서 관리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;omem: 해당 클라이언트의 출력 버퍼에 쌓인 메모리. 이 값이 크면 큰 응답을 받고 있거나 느리게 응답을 소비 중..&lt;/li&gt;
&lt;li&gt;cmd: 마지막으로 사용한 명령어&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 성능/트래픽&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 INFO stats (성능 관련 지표)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;INFO stats의 필드는 두 종류가 섞여 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;# 순간값(instantaneous_xxx)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;instantaneous_ops_per_sec: 현재 초당 처리 명령 수&lt;/li&gt;
&lt;li&gt;instantaneous_input_kbps: 현재 클 &amp;rarr; 서 Network Bytes&lt;/li&gt;
&lt;li&gt;instantaneous_output_kbps: 현재 서 &amp;rarr; 클 Network Bytes&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;# 누적값(서버 기동 이후 계속 쌓임)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 값들은 Redis를 재시작하거나 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;config resetstat&lt;/span&gt;을 하지 않는 한 계속 올라가기만 한다.&lt;/b&gt; 그래서 요것만 보면 이게 지금 문제인지, 6개월에 한 번 있었던 건지 알기 어렵다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;total_commands_processed&lt;/li&gt;
&lt;li&gt;keyspace_hits / keyspace_misses&lt;/li&gt;
&lt;li&gt;evicted_keys / expired_keys&lt;/li&gt;
&lt;li&gt;rejected_connections&lt;/li&gt;
&lt;li&gt;sync_full / sync_partial_err&lt;/li&gt;
&lt;li&gt;그 외 대부분&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 info stats은 사실상 exporter, grafana에서 사용하기 유용한 값들이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 INFO commandstats&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 그라파나 상에서 latency나 ops/s가 치솟는 걸 확인했다면, &lt;b&gt;어떤 명령어가 영향을 주었는지&lt;/b&gt; &lt;span style=&quot;color: #ef5369;&quot;&gt;commandstats&lt;/span&gt;를 통해 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1772419417649&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;redis-cli INFO commandstats
```
```
# 출력 예시
cmdstat_get:calls=5823120,usec=2914560,usec_per_call=0.50,rejected_calls=0
cmdstat_set:calls=1203400,usec=721800,usec_per_call=0.60,rejected_calls=0
cmdstat_hgetall:calls=45200,usec=1582000,usec_per_call=35.00,rejected_calls=0
cmdstat_keys:calls=12,usec=8520000,usec_per_call=710000.00,rejected_calls=0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 봐야할 건 &lt;span style=&quot;color: #ef5369;&quot;&gt;usec_per_call&lt;/span&gt;인데, 위 예시에서 keys가 호출당 710ms를 발생한 걸 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;# usec_per_call 기준으로 정렬해서 느린 명령 찾기&lt;/h4&gt;
&lt;pre id=&quot;code_1772419499582&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;redis-cli INFO commandstats | sort -t, -k3 -rn | head -10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3 slowlog&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis는 특정치를 넘어선 명령어를 큐에 저장해서 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;slowlog get&lt;/b&gt;&lt;/span&gt;으로 보여준다.&lt;u&gt;&lt;b&gt; Redis 구조인 단일 스레드 특성 상, 한 명령어가 오랫동안 리소스를 독점하면 전체 성능에 영향이 있으므로, 이를 확인하고 개선하는 것이 중요하다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이때 측정 시간은 Network I/O, 응답 전송 시간을 &lt;b&gt;제외한 순수 명령어 실행 시간&lt;/b&gt;이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;# 주요 지표 의미&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;slowlog-log-slower-than&lt;/b&gt; (임계값, 마이크로초 &amp;mu;s) : parameter group에서 확인할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dSRtXM/dJMcagSanLM/sMrjKNUBjqEv2NjflUXEF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dSRtXM/dJMcagSanLM/sMrjKNUBjqEv2NjflUXEF0/img.png&quot; data-alt=&quot;현재 기본값이고 engine version에 따라 이 값을 aws에서 설정해준다. 나중에 테스트하고 추가해봐야지..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dSRtXM/dJMcagSanLM/sMrjKNUBjqEv2NjflUXEF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdSRtXM%2FdJMcagSanLM%2FsMrjKNUBjqEv2NjflUXEF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;261&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;현재 기본값이고 engine version에 따라 이 값을 aws에서 설정해준다. 나중에 테스트하고 추가해봐야지..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;slowlog-max-len: 최대 보관 개수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 고가용성&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 INFO replication&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 노드에 대한 replication 정보를 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnnzoo/dJMcacvtJFq/bepxVCCBO8O0alFOpJxIx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnnzoo/dJMcacvtJFq/bepxVCCBO8O0alFOpJxIx1/img.png&quot; data-alt=&quot;많은 정보들이 나오는데, 하나씩 살펴보자.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnnzoo/dJMcacvtJFq/bepxVCCBO8O0alFOpJxIx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbnnzoo%2FdJMcacvtJFq%2FbepxVCCBO8O0alFOpJxIx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;310&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;828&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;많은 정보들이 나오는데, 하나씩 살펴보자.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;master_host: 동기화 대상인 마스터의 주소&lt;/li&gt;
&lt;li&gt;master_link_status: `up`이면 정상 연결, `down`이면 끊긴 상태&lt;/li&gt;
&lt;li&gt;master_last_io_seconds_ago: master와 마지막으로 통신(I/O)한 지 몇 초가 지났는지 &amp;rarr; 보통 값이 작을 수록 정상이다. (0~수 초)&lt;/li&gt;
&lt;li&gt;master_sync_in_progress: master가 replica에 대해 sync를 수행 중인지 나타냄. 1이면 동기화, 0이면 아님&lt;/li&gt;
&lt;li&gt;slave_read_only: replica가 읽기 전용인지 나타낸다.&lt;b&gt; 1이면 read-only&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;master_failover_state: 현재 진행 중인 failover가 있/없는지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 여러 샤드가 있을 때 유용한 명령어&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.1 샤드별 데이터 저장 분포 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1773120336904&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;redis-cli --cluster call {endpoints} DBSIZE&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DB/In-Memory DB</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/325</guid>
      <comments>https://cobinding.tistory.com/325#entry325comment</comments>
      <pubDate>Mon, 2 Mar 2026 22:11:57 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] EC2 EBS와 Instance Storage, Root/Local/Ephemeral</title>
      <link>https://cobinding.tistory.com/323</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2 관련 작업을 도맡으면서 서버 운영을 위해서는 Storage 개념을 잘 이해하고 있어야 한다는 점을 알게되었다. 운영 관점에서 고려해보니 루트 볼륨, 이퍼머럴 스토리지, 부팅 디스크 등등 여러 용어도 확실히 알아야 겠다 싶어서 이번 포스팅을 통해 정리해보려 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. EC2 Storage: EBS와 Instance Store&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2에 연결할 수 있는 스토리지는 크게 다음과 같다. 이 포스팅에서는 &lt;b&gt;EBS, Instance Store&lt;/b&gt;를 중점적으로 알아본다.&lt;/p&gt;
&lt;pre id=&quot;code_1768720632685&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  EC2 스토리지
  ├── * EBS (Elastic Block Store)
  │   ├── 루트 볼륨 (부팅용)
  │   └── 추가 볼륨 (데이터용)
  │
  ├── * Instance Store (Ephemeral / Local Storage)
  │
  └── EFS (Elastic File System)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;EBS와 Instance Store&lt;/b&gt;를 직관적으로 이해할 수 있는 이미지는 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2296&quot; data-origin-height=&quot;998&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pY8dA/dJMcacPpopY/EQQBhnZw8K2lHSS1C9x1M1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pY8dA/dJMcacPpopY/EQQBhnZw8K2lHSS1C9x1M1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pY8dA/dJMcacPpopY/EQQBhnZw8K2lHSS1C9x1M1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpY8dA%2FdJMcacPpopY%2FEQQBhnZw8K2lHSS1C9x1M1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;348&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2296&quot; data-origin-height=&quot;998&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;text-align: center;&quot;&gt;EBS는 EC2와 &lt;b&gt;네트워크&lt;/b&gt;로 연결되고, Instance Store는 물리 서버에 같이 설치된다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. EBS(Elastic Block Store)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 Block Store&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1632&quot; data-origin-height=&quot;666&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/03vvb/dJMcabXf9kB/unRILtyeLz9GH5cyRKJ8Rk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/03vvb/dJMcabXf9kB/unRILtyeLz9GH5cyRKJ8Rk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/03vvb/dJMcabXf9kB/unRILtyeLz9GH5cyRKJ8Rk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F03vvb%2FdJMcabXf9kB%2FunRILtyeLz9GH5cyRKJ8Rk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;245&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1632&quot; data-origin-height=&quot;666&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;블록 단위&lt;/b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;로 데이터를 저장하는 스토리지 방식으로, SAN(Storage Area Network)나 클라우드 기반 스토리지 환경에서 사용되는 기술이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 &lt;b&gt;일정한 크기의 블록&lt;/b&gt;으로 나누고, 각 블록에는&amp;nbsp;&lt;b&gt;고유 주소&lt;/b&gt;가 부여된다.&lt;/li&gt;
&lt;li&gt;이 식별자를 통해서 데이터를 매우 빠르게 검색할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빠른 성능에 최적화&lt;/b&gt;되어있기에 다른 스토리지에 비해 &lt;b&gt;비쌈!&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;b&gt;* Block Storage 이외에는 File Storage(EFS), Object Storage(S3) 등이 있다. (이미지 참고)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 Elastic Block Store&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2에 연결하여 사용하는 블록 스토리지로,&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt; EC2 Instance와 독립적으로 데이터 유지가 가능&lt;/b&gt;&lt;/span&gt;하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EC2 Instance와 네트워크로 연결되어, 독립적으로 구성되므로 인스턴스 종료 후에도 데이터가 보존된다.&lt;/li&gt;
&lt;li&gt;하나의 EC2 Instance에 여러 용도의 EBS를 붙일 수 있다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;단, 속도를 위해 &lt;span style=&quot;color: #ef5369;&quot;&gt;동일한 가용영역 내의 EC2만 연결&lt;/span&gt;이 가능하다.&lt;/li&gt;
&lt;li&gt;거꾸로, &lt;span style=&quot;color: #ef5369;&quot;&gt;하나의 EBS를 여러 EC2에 장착 가능&lt;/span&gt;하다. (&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/ebs/latest/userguide/ebs-volumes-multi.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;EBS Multi Attach&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3 루트 볼륨/부팅 디스크&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;EC2 Instance를 생성하면 EBS가 루트 볼륨으로 설정된다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;루트 볼륨의 역할&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;부팅 순서를 보면 루트 볼륨의 중요도를 알 수 있다! 루트 볼륨(root device)은 EC2 인스턴스와 같은 서버가 부팅할 때 사용하는 스토리지로, 운영체제와 기본 파일 시스템(/)을 포함한다.이 스토리지를 통해 커널이 메모리에 올라가고, 일반적으로 서버에서 하려는 작업들이 가동 가능한 시스템을 구축하게 된다.&amp;nbsp;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OA7sP/dJMcafyDt23/MblomqbqcjlL3iZ6qCyNQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OA7sP/dJMcafyDt23/MblomqbqcjlL3iZ6qCyNQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OA7sP/dJMcafyDt23/MblomqbqcjlL3iZ6qCyNQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOA7sP%2FdJMcafyDt23%2FMblomqbqcjlL3iZ6qCyNQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;250&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;루트 볼륨: Instance Store(Ephemeral) Vs. EBS&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;EC2 Instance의 루트 볼륨은 EBS, Instance Store 모두 가능하지만 일반적으로는 EBS를 사용한다. &lt;/b&gt;(&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/ComponentsAMIs.html#storage-for-the-root-device&quot;&gt;관련 문서&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;1010&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBkjzk/dJMcahiStm4/ckTB8mAkaafsDhIamms5bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBkjzk/dJMcahiStm4/ckTB8mAkaafsDhIamms5bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBkjzk/dJMcahiStm4/ckTB8mAkaafsDhIamms5bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBkjzk%2FdJMcahiStm4%2FckTB8mAkaafsDhIamms5bk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;374&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;1010&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⚫️&amp;nbsp; Amazon EBS-backed AMI&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EBS를 Root Volume으로 사용하는 AMI다. 현재 AWS에서 생성되는 대부분의 EC2 인스턴스는 &lt;b&gt;EBS-backed AMI&lt;/b&gt;를 사용한다. &lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/RootDeviceStorage.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식문서&lt;/a&gt;에도 나와있듯, AWS에서는 EBS를 루트 볼륨으로 사용할 것을 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 뭘까?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인스턴스 서버는 언제든 실패할 수 있는데, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;EBS의 경우 인스턴스의 중단과 상관없이 데이터가 유지&lt;/span&gt;된다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;가령 스케일링, 운영 중 설정 변경, 하드웨어 장애 등으로 인해 &lt;b&gt;인스턴스의 중단이나 재생성은 피할 수 없는 이벤트&lt;/b&gt;인데, EBS를 루트 볼륨으로 사용하면 인스턴스의 중단 여부와 관계없이 루트 볼륨의 데이터는 그대로 유지된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;point-in-time&lt;/span&gt;스냅샷을&lt;b&gt;&amp;nbsp;여러 AZ에 복제된 형태로 저장&lt;/b&gt;하기 때문에, 필요 시 &lt;b&gt;완전한 볼륨 복구가 가능&lt;/b&gt;하다. (안정성 측면!) (&lt;a href=&quot;https://docs.aws.amazon.com/ebs/latest/userguide/ebs-snapshots.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;관련 문서&lt;/a&gt;)&lt;br /&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;point-in-time snapshot:&lt;/b&gt; 특정 시점(point)의 볼륨 상태를 그대로 보존한 복사본을 의미한다. 단순한 파일 복사가 아니라 파일 시스템, 블록 레벨 데이터, 메타데이터를 포함한 볼륨 전체 상태 등의 특정 시점 기준으로 freeze해서 저장한다. 그래서 스냅샷으로부터 새 볼륨을 만들면 &lt;b&gt;그 시점에 존재하던 특정 디스크 상태 그대로 복원&lt;/b&gt;된다.&amp;nbsp;&amp;nbsp;&lt;i&gt;&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의할 점 ⚠️ :&lt;/b&gt; 이러한 스냅샷은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;자동으로 백업되지 않기 때문에&lt;/span&gt; 정기적으로 EBS 스냅샷을 생성하거나, Amazon Data Lifecycle Manager, Backup 기능을 사용하여 자동 생성을 구축하는 등의 조치를 사용자가 취해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⚫️&amp;nbsp; Amazon S3-backed AMI&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Instance Store를 Root Volume으로 사용하는 AMI다. AMI 자체는 S3에 저장되고, 실제&amp;nbsp;&lt;b&gt;루트 볼륨은 인스턴스 내부의 로컬 스토리지(Instance Store)&lt;/b&gt; 에 생성되는 구조다.&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에 사용되다가 deprecated 되어가는 이유는 뭘까?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Instance Store는 인스턴스의 생명 주기와 완전히 결합된 스토리지&lt;/b&gt;다. 즉, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;인스턴스 장애가 OS 및 서버 상태 전체 유실&lt;/span&gt;로 이어진다.&lt;/li&gt;
&lt;li&gt;스토리지 계층에서의 &lt;b&gt;장애 복구 모델이 없다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 언제 사용할까?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;성능이 생존보다 중요한 상황&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;기본 설정이 모두 코드로 관리되는 경우 (AMI나 user-data Script를 통해 부팅 시 설치)&lt;/li&gt;
&lt;li&gt;데이터는 외부(S3, DB, Redis 등)에 존재&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.5 EBS 볼륨 유형 타입&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EBS 볼륨 타입은 일반 컴퓨터에서 SSD를 쓸지, HDD를 쓸지 고르는 것과 비슷한데, AWS는 용도별로 세분화해서 제공한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/ebs/latest/userguide/ebs-volume-types.html#vol-type-ssd&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SSD: gp3, gp2, io2, io1&lt;/a&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;gp: 웹서버, 개발환경, 소규모 DB에 사용하고 가격 대비 성능 균형이 좋아서 보편적으로 사용&lt;/li&gt;
&lt;li&gt;io: 대규모 DB가 필요할 때 사용하고 IOPS를 직접 지정 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/ebs/latest/userguide/ebs-volume-types.html#vol-type-hdd&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;HDD: st1, sc1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Instance Store&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 Instance Store&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Instance Store는 EBS처럼 붙였다 떼었다 하는 것이 아니라,&lt;b&gt; EC2 인스턴스가 실행되는 물리 호스트에 직접 연결된 스토리지다. &lt;/b&gt;Amazon에서는 인스턴스 유형으로 EC2에 Instance Store가 포함되었는지, 아닌지 구분한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;906&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L9UWH/dJMcacIDY21/0t7XVxb6ALoOQfcKVFCZwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L9UWH/dJMcacIDY21/0t7XVxb6ALoOQfcKVFCZwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L9UWH/dJMcacIDY21/0t7XVxb6ALoOQfcKVFCZwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL9UWH%2FdJMcacIDY21%2F0t7XVxb6ALoOQfcKVFCZwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;456&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;906&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EBS가 인스턴스와 &lt;b&gt;논리적으로 분리된 블록 스토리지&lt;/b&gt;라면, Instance Store는 &lt;b&gt;인스턴스 타입 자체에 물리적으로 포함된 스토리지&lt;/b&gt;라는 점에서 근본적인 차이가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때문에 &lt;b&gt;Instance Store는 다음과 같은 특성&lt;/b&gt;을 가진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;517&quot; data-start=&quot;435&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;453&quot; data-start=&quot;435&quot;&gt;인스턴스 생성 시 함께 제공됨&lt;/li&gt;
&lt;li data-end=&quot;478&quot; data-start=&quot;454&quot;&gt;&quot;attach / detach 개념이 없음&lt;/li&gt;
&lt;li data-end=&quot;496&quot; data-start=&quot;479&quot;&gt;다른 인스턴스에 재사용 불가&lt;/li&gt;
&lt;li data-end=&quot;517&quot; data-start=&quot;497&quot;&gt;인스턴스 생명주기와 완전히 결합됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;560&quot; data-start=&quot;519&quot; data-ke-size=&quot;size16&quot;&gt;AWS 공식 문서에서도 Instance Store를 다음과 같이 정의한다.(&lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/InstanceStorage.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;유저 가이드 문서&lt;/a&gt;)&lt;/p&gt;
&lt;blockquote data-end=&quot;718&quot; data-start=&quot;562&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;p data-end=&quot;718&quot; data-start=&quot;564&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Instance store provides temporary block-level storage for your instance.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;This storage is physically attached to the host computer for an EC2 instance.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;812&quot; data-start=&quot;720&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;812&quot; data-start=&quot;720&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;span style=&quot;background-color: #f6e199;&quot;&gt; &lt;b&gt;Instance Store는 &lt;/b&gt;&amp;lsquo;임시(temporary)&amp;rsquo; 스토리지&lt;/span&gt;이며, 인스턴스가 종료되거나 물리 호스트에 문제가 발생하면 데이터는 복구할 수 없다.&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;812&quot; data-start=&quot;720&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;812&quot; data-start=&quot;720&quot; data-ke-size=&quot;size23&quot;&gt;3.2 Instance Store와 NVMe&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Instance Store의 높은 성능은 &lt;b&gt;NVMe(Non-Volatile Memory Express)&lt;/b&gt; 인터페이스 덕분이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;NVMe란?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NVMe는 &lt;b&gt;SSD를 빠르게 사용하기 위해 설계된 스토리지 프로토콜&lt;/b&gt;이다. 기존 SATA 인터페이스는 HDD 시절에 만들어진 규격이라 SSD의 성능을 온전히 발휘하지 못했는데, NVMe는 &lt;b&gt;PCIe 레인을 통해 CPU와 거의 직통으로 연결&lt;/b&gt;되어 훨씬 빠른 I/O를 제공한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot; style=&quot;color: #383a42; text-align: left;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;SATA SSD:  CPU ──(SATA 컨트롤러)──&amp;rarr; SSD
NVMe SSD:  CPU ──(PCIe 직통)──────&amp;rarr; SSD&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말해, &lt;b&gt;같은 SSD라도 어떤 인터페이스로 연결하느냐에 따라 속도가 달라진다.&lt;/b&gt; NVMe는 SSD를 더 빠르게 전송하는 인터페이스!&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Instance Store가 빠른 이유&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Instance Store는 EC2가 실행되는 &lt;b&gt;물리 호스트에 장착된 NVMe SSD에 직접 연결&lt;/b&gt;된다. 네트워크를 경유하지 않고 PCIe로 바로 붙어있기 때문에 낮은 지연시간과 높은 IOPS를 제공한다. (&lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ssd-instance-store.html&quot;&gt;관련 문서&lt;/a&gt;)&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;less&quot; style=&quot;color: #383a42; text-align: left;&quot;&gt;&lt;code&gt;Instance Store:  EC2 ──(PCIe)──&amp;rarr; 물리 NVMe SSD (로컬)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그럼 EBS는?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nitro 기반 EC2에서는 &lt;b&gt;EBS도 NVMe 디바이스로 노출&lt;/b&gt;된다. 다만 실제로는 네트워크를 경유하는 원격 스토리지이고, OS에게 NVMe처럼 보이도록 에뮬레이션하는 것이다. NVMe 드라이버가 더 효율적이기 때문에 이런 방식을 사용한다. (&lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nvme-ebs-volumes.html&quot;&gt;관련 문서&lt;/a&gt;)&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;gcode&quot; style=&quot;color: #383a42; text-align: left;&quot;&gt;&lt;code&gt;EBS (Nitro):     EC2 ──(가상 NVMe)──&amp;rarr; 네트워크 ──&amp;rarr; EBS 볼륨 (원격)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 Nitro 인스턴스에서 lsblk를 치면 EBS 볼륨도 nvme로 표시된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #383a42; text-align: left;&quot;&gt;&lt;code&gt;$ lsblk
nvme0n1      259:0    0   8G  0 disk         # EBS 루트 볼륨
└─nvme0n1p1  259:1    0   8G  0 part /
nvme1n1      259:2    0 100G  0 disk /data   # EBS 추가 볼륨
nvme2n1      259:3    0 900G  0 disk /local  # Instance Store (진짜 로컬 NVMe)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;812&quot; data-start=&quot;720&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;812&quot; data-start=&quot;720&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;b&gt;* Instance Store는 상황에 따라 다른 이름으로 불리기도 한다.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Local Storage:&lt;/b&gt;&amp;nbsp;네트워크가 아닌 물리적 서버로 붙어있는 것&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Ephemeral Storage:&lt;/b&gt;&amp;nbsp;인스턴스가 종료될 때 사라지는 임시 스토리지 (&amp;harr; EBS는 인스턴스가 다운되어도 영구 저장됨.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;812&quot; data-start=&quot;720&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;3.4 Instance Storage 특성&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;로컬 스토리지는&amp;nbsp;&lt;b&gt;날아가도 되지만 속도가 중요한 데이터&lt;/b&gt;에 사용한다. (로컬 캐시와 같은...) 아래와 같은 활용 케이스가 대표적이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka Broker
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&amp;gt;메시지 쓰기/읽기가 초당 수십만건: 속도 필요&lt;/li&gt;
&lt;li&gt;카프카는 여러 복제본(3대)를 유지하기 때문에 1대가 죽어도 데이터는 그대로!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Spark/EMR
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;여러 Stage 간 데이터를 주고 받을 때, Shuffle 데이터를 Instance Store에 저장&lt;/li&gt;
&lt;li&gt;대략 이런 구조임: &amp;nbsp;&lt;i&gt;&amp;nbsp;[Stage&amp;nbsp;1]&amp;nbsp;&amp;rarr;&amp;nbsp;shuffle&amp;nbsp;데이터&amp;nbsp;&amp;rarr;&amp;nbsp;[Stage&amp;nbsp;2]&amp;nbsp;&amp;rarr;&amp;nbsp;shuffle&amp;nbsp;데이터&amp;nbsp;&amp;rarr;&amp;nbsp;[Stage&amp;nbsp;3]&lt;/i&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;캐싱! Redis&lt;/li&gt;
&lt;li&gt;로그 버퍼 (S3 전송 전 임시 저장)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;실제 장애 상황에서 &lt;span style=&quot;color: #ef5369;&quot;&gt;i3en.24xlarge&lt;/span&gt; 인스턴스를 여러 대 띄우고, Tantivy 라이브러리로 특정 필드가 존재하는 파일을 다운로드하는 케이스를 본 적 있다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;S3로 보내기 전에 잠깐 모아두는 용도이고 주기적으로 S3로 Flush하는 방식&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7; text-align: left;&quot;&gt;&lt;span style=&quot;color: #1d1c1d; text-align: left;&quot;&gt;ML 임시 데이터&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #006dd7; text-align: left;&quot;&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7; text-align: left;&quot;&gt;&lt;span style=&quot;color: #1d1c1d; text-align: left;&quot;&gt;S3 원본 데이터를 로컬에 복사해서 학습 중 빠른 I/O를 확보&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;812&quot; data-start=&quot;720&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;812&quot; data-start=&quot;720&quot; data-ke-size=&quot;size23&quot;&gt;3.5 Instance Store와 인스턴스 유형&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Amazon EC2에서는 모든 인스턴스 유형이 Instance Store를 제공하지 않는다. AWS는 &lt;b&gt;인스턴스 유형 자체로 Instance Store 포함 여부를 구분&lt;/b&gt;한다. 대표적으로 &lt;b&gt;d suffix&lt;/b&gt;가 붙은 인스턴스 유형은 Instance Store를 포함한다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;여기서 d는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;dense storage&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;를 의미하며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;로컬 NVMe SSD 기반&lt;/span&gt;의 Instance Store가 포함된 인스턴스를 나타낸다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1061&quot; data-start=&quot;1021&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1042&quot; data-start=&quot;1021&quot;&gt;m5d, c5d, r5d&lt;/li&gt;
&lt;li data-end=&quot;1061&quot; data-start=&quot;1043&quot;&gt;c6gd, r6gd 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1190&quot; data-start=&quot;1148&quot; data-ke-size=&quot;size16&quot;&gt;또한 다음과 같은 suffix들도 Instance Store와 관련이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1262&quot; data-start=&quot;1192&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1226&quot; data-start=&quot;1192&quot;&gt;i : I/O 최적화 (예: i3, i4i)&lt;/li&gt;
&lt;li data-end=&quot;1262&quot; data-start=&quot;1227&quot;&gt;h : HDD 기반 대용량 스토리지 (예: h1)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  실제 운영에서 알게된 Local Storage 관련 지식(Local Storage 제약 정책)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업무하면서 알게된 것. kubelet의&lt;b&gt; Disk Pressure&lt;/b&gt;는 &lt;b&gt;특정 컨테이너의 Instance Local Storage 점유&lt;/b&gt;로 인해서 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 특정 파드의 노드 로컬 스토리지 점유를 막기 위해서 아래와 같은 정책을 적용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1768729700714&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;resources:
  limits:
    ephemeral-storage: 10Gi
  requests:
    ephemeral-storage: &quot;0&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;해당 정책으로 죽은 Pod는 ContainerStatusUnknown 또는 Failed 상태를 보인다. (&lt;span style=&quot;color: #ef5369;&quot;&gt;Pod ephemeral local storage usage exceeds the total limit of containers&lt;/span&gt; 와 같은 메시지도 참고할 수 있다.)&lt;br /&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리!&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2 운영을 하려면 스토리지 선택 기준과 각 볼륨의 역할을 알아두는 게 중요한 것 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;EBS vs Instance Store&lt;/b&gt;: 네트워크로 연결되는 영구 스토리지 vs 물리 서버에 붙은 휘발성 로컬 스토리지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;루트 볼륨 vs 추가 볼륨&lt;/b&gt;: 부팅 디스크 vs 데이터 저장용 볼륨
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;부팅 디스크 역할! EC2가 시작될 때 OS를 로드하는 루트 볼륨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Ephemeral / Local Storage&lt;/b&gt;: Instance Store를 부르는 다른 이름들로, 빠르지만 인스턴스 종료 시 사라지는 스토리지
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;로컬 스토리지의 역할&lt;/b&gt;: 캐시, 임시 데이터, shuffle 데이터 등 빠른 I/O가 필요한 용도&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;NVMe&lt;/b&gt;: EBS와 Instance Store 모두에서 사용하는 스토리지 인터페이스 표준&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또, 스토리지 용도를 잘 알고 사용하는 것&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;848&quot; data-start=&quot;809&quot;&gt;영구 저장이 필요한 데이터를 Instance Store에 두거나&lt;/li&gt;
&lt;li data-end=&quot;886&quot; data-start=&quot;849&quot;&gt;반대로 빠른 I/O가 필요한 워크로드를 EBS에만 의존하거나&lt;/li&gt;
&lt;li data-end=&quot;923&quot; data-start=&quot;887&quot;&gt;루트 볼륨과 데이터 볼륨의 역할을 명확히 나누지 않은 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/AWS</category>
      <category>AWS</category>
      <category>DevOps</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/323</guid>
      <comments>https://cobinding.tistory.com/323#entry323comment</comments>
      <pubDate>Sun, 18 Jan 2026 19:13:57 +0900</pubDate>
    </item>
    <item>
      <title>[회고] 2025년 첫 커리어 1년간 한 일</title>
      <link>https://cobinding.tistory.com/321</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025년 첫 커리어를 시작하고 SRE로서 회사에 기여한 일들을 정리해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그대로 옮기니까 가독성이 좀 떨어지긴 한데..ㅋㅋ 작업 관련 스레드 링크를 모두 붙였다가 이걸 이미지와 글로 풀어내려니까 어려운 점..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 서비스 캐시 성능 향상 및 인프라 비용 절감을 위한 ElastiCache for Valkey 도입&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Problem&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;안정적인 서비스 운영과 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;성능/비용 최적화&lt;/span&gt;&lt;/b&gt;를 위해 Valkey로의 전환을 추진&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Task&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;사내 플랫폼 ElastiCache 기능 개발 및 출시&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사내 플랫폼 API 개발
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;boto3 API 검토 및 기존 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;Redis 워크플로우 기반 Valkey 생성 기능 구현&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;Engine Version 조회 API 리팩토링&lt;/b&gt;:&lt;/span&gt; Redis 5.x/6.x 지원 중단, Valkey 7.2/8.x 버전 추가&lt;/li&gt;
&lt;li&gt;Parameter Group &lt;b&gt;검증 로직 개선&lt;/b&gt; (Redis &amp;harr; Valkey 전환 시 engineType 버그 수정)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;u&gt;사내 플랫폼&amp;nbsp;UI 개발&lt;/u&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ElastiCache 생성 화면에 Redis/Valkey 엔진 선택&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구버전(Redis 5.x, 6.x) 생성 제한&lt;/b&gt; 및 Valkey 버전 목록 확장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;오픈 공지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1652&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kFxMd/dJMcaaRxlcZ/pHLOD1zmYdmxyLVPe6keH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kFxMd/dJMcaaRxlcZ/pHLOD1zmYdmxyLVPe6keH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kFxMd/dJMcaaRxlcZ/pHLOD1zmYdmxyLVPe6keH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkFxMd%2FdJMcaaRxlcZ%2FpHLOD1zmYdmxyLVPe6keH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;182&quot; data-origin-width=&quot;1652&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;Valkey 모니터링&lt;/b&gt;&lt;/span&gt;을 위해 &lt;b&gt;redis exporter&lt;/b&gt; v1.74.0 업그레이드&lt;/li&gt;
&lt;li&gt;&lt;b&gt;마이그레이션 검증 및 클라이언트 호환성 테스트&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Redis &amp;rarr; Valkey 전환 시,&lt;span style=&quot;background-color: #f6e199;&quot;&gt; 우려사항과 성능 비교&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;에 대한 글 작성 후 전사 발표를 통한 공유&lt;/span&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;성능 비교 테스트: &lt;a href=&quot;https://cobinding.tistory.com/309&quot;&gt;https://cobinding.tistory.com/309&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프로젝트 완성도&lt;/b&gt;를 위한 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;외부 미팅 주도&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS ElastiCache Specialist와의 미팅을 통해&lt;span style=&quot;background-color: #f6e199;&quot;&gt; &lt;b&gt;공식 가이드 및 베스트 프랙티스 검증&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;당근 내 ElastiCache를 적극적으로 사용하는 팀과의 미팅을 통해 &lt;b&gt;실사용 사례 및 운영 노하우 공유(/w Lani)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;관련 운영 지원
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엔진 업데이트 주의사항 공유&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;사내 플랫폼 Redis 마이그레이션 작업&lt;/li&gt;
&lt;li&gt;Redis 관련 운영 질문 &amp;amp; 업데이트 고려사항 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cd1gAr/dJMcagxrCPp/24CVkQBX1FAiZ2DyyNH6kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cd1gAr/dJMcagxrCPp/24CVkQBX1FAiZ2DyyNH6kk/img.png&quot; data-origin-width=&quot;1480&quot; data-origin-height=&quot;244&quot; data-is-animation=&quot;false&quot; width=&quot;600&quot; height=&quot;99&quot; style=&quot;width: 64.7916%; margin-right: 10px;&quot; data-widthpercent=&quot;65.55&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cd1gAr/dJMcagxrCPp/24CVkQBX1FAiZ2DyyNH6kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcd1gAr%2FdJMcagxrCPp%2F24CVkQBX1FAiZ2DyyNH6kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1480&quot; height=&quot;244&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d8tuqo/dJMcahC66tS/qV7Z9e694jFqOoQkmqYAQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d8tuqo/dJMcahC66tS/qV7Z9e694jFqOoQkmqYAQK/img.png&quot; data-origin-width=&quot;1498&quot; data-origin-height=&quot;470&quot; data-is-animation=&quot;false&quot; width=&quot;600&quot; height=&quot;188&quot; style=&quot;width: 34.0456%;&quot; data-widthpercent=&quot;34.45&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d8tuqo/dJMcahC66tS/qV7Z9e694jFqOoQkmqYAQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd8tuqo%2FdJMcahC66tS%2FqV7Z9e694jFqOoQkmqYAQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1498&quot; height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVHtVp/dJMcajnlTwj/lV3KxYHdkiUcABks72HNNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVHtVp/dJMcajnlTwj/lV3KxYHdkiUcABks72HNNk/img.png&quot; data-origin-width=&quot;936&quot; data-origin-height=&quot;386&quot; data-is-animation=&quot;false&quot; width=&quot;600&quot; height=&quot;247&quot; style=&quot;width: 37.3859%; margin-right: 10px; margin-top: 10px;&quot; data-widthpercent=&quot;38.28&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVHtVp/dJMcajnlTwj/lV3KxYHdkiUcABks72HNNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVHtVp%2FdJMcajnlTwj%2FlV3KxYHdkiUcABks72HNNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;936&quot; height=&quot;386&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lWQc6/dJMcai9OX1W/oRinTpus7iYWLhjGa0h6tK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lWQc6/dJMcai9OX1W/oRinTpus7iYWLhjGa0h6tK/img.png&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;746&quot; data-is-animation=&quot;false&quot; width=&quot;600&quot; height=&quot;504&quot; style=&quot;width: 18.3524%; margin-right: 10px; margin-top: 10px;&quot; data-widthpercent=&quot;18.79&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lWQc6/dJMcai9OX1W/oRinTpus7iYWLhjGa0h6tK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlWQc6%2FdJMcai9OX1W%2FoRinTpus7iYWLhjGa0h6tK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;888&quot; height=&quot;746&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brQEIj/dJMcaiPwBXT/Ja8xuyc44wm2yUBxlQzx31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brQEIj/dJMcaiPwBXT/Ja8xuyc44wm2yUBxlQzx31/img.png&quot; data-origin-width=&quot;1496&quot; data-origin-height=&quot;550&quot; data-is-animation=&quot;false&quot; style=&quot;width: 41.9361%; margin-top: 10px;&quot; data-widthpercent=&quot;42.93&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brQEIj/dJMcaiPwBXT/Ja8xuyc44wm2yUBxlQzx31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrQEIj%2FdJMcaiPwBXT%2FJa8xuyc44wm2yUBxlQzx31%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1496&quot; height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;사내 개발자들에게 Redis 엔진 업데이트 안내 및 대응 / 마이그레이션 업무 진행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;| Impact&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ElastiCache Redis &amp;rarr; Valkey 마이그레이션을 통해&lt;span style=&quot;color: #ef5369;&quot;&gt; &lt;b&gt;Alpha 21.9%, Prod 20.2%의 전환율 달성&lt;/b&gt;.&lt;/span&gt; Valkey는 Redis 대비 20% 저렴하여,&lt;span style=&quot;color: #ef5369; background-color: #f6e199;&quot;&gt; &lt;b&gt;현재 전체 ElastiCache 비용의 약 4% 절감 효과.&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;feature-platform 전환 사례&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;대략 일 $500, 월 $15,000 절감&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Valkey 마이그레이션의 &lt;b&gt;배경과 효과&lt;/b&gt;를 전사에 공유하고, &lt;b&gt;개발자 관점의 전환 가이드를 제공&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ItNws/dJMcacIBcZ4/E4M9SsGBTvxH5DkSdDM8u1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ItNws/dJMcacIBcZ4/E4M9SsGBTvxH5DkSdDM8u1/img.png&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;1354&quot; data-is-animation=&quot;false&quot; width=&quot;600&quot; height=&quot;570&quot; style=&quot;width: 36.8329%; margin-right: 10px;&quot; data-widthpercent=&quot;37.27&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ItNws/dJMcacIBcZ4/E4M9SsGBTvxH5DkSdDM8u1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FItNws%2FdJMcacIBcZ4%2FE4M9SsGBTvxH5DkSdDM8u1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1426&quot; height=&quot;1354&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DYF5r/dJMcad1J4Y1/pBVclMmEnAJy2ZS6KTgu7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DYF5r/dJMcad1J4Y1/pBVclMmEnAJy2ZS6KTgu7k/img.png&quot; data-origin-width=&quot;2670&quot; data-origin-height=&quot;1506&quot; data-is-animation=&quot;false&quot; width=&quot;600&quot; height=&quot;338&quot; style=&quot;width: 62.0043%;&quot; data-widthpercent=&quot;62.73&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DYF5r/dJMcad1J4Y1/pBVclMmEnAJy2ZS6KTgu7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDYF5r%2FdJMcad1J4Y1%2FpBVclMmEnAJy2ZS6KTgu7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2670&quot; height=&quot;1506&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;오른쪽 가장 위에 오프라인으로 참석해서 발표한 영상..ㅎㅎ 우리 회사에서는 매주 전사적으로 기술적 이야기를 공유하는데 그 자리를 빌려 발키 소개했다. 보통은 온라인으로 참여하고 원하는 사람이나 발표자는 오프라인으로 참여한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 인프라 E2E 장애 모니터링 가시성 확보 1: CloudFront Additional Metrics&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Problem&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CloudFront는 모든 트래픽이 통과하는 첫 진입점&lt;/b&gt;이지만, 해당 구간의 &lt;b&gt;모니터링 부재&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Task&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CloudFront 관련 모니터링 강화를 위한 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Additional Metrics 활성화&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Additional Metrics 예상 비용 확인 후 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1204&quot; data-origin-height=&quot;1452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bC7ipI/dJMcafFjDCl/ELL6RuHmEjWBWaYrROQHT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bC7ipI/dJMcafFjDCl/ELL6RuHmEjWBWaYrROQHT0/img.png&quot; data-alt=&quot;관련 미팅 문서를 만들었고 팀 내에 공유&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bC7ipI/dJMcafFjDCl/ELL6RuHmEjWBWaYrROQHT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbC7ipI%2FdJMcafFjDCl%2FELL6RuHmEjWBWaYrROQHT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;724&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1204&quot; data-origin-height=&quot;1452&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;관련 미팅 문서를 만들었고 팀 내에 공유&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Impact&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;지금까지 모니터링 되고있지 않은 영역들(Cache Hit, Origin Latency 등)에 대한 &lt;b&gt;메트릭 확보&lt;/b&gt;&lt;/span&gt; &amp;rarr; 확보된 지표로 &lt;b&gt;CloudFront 대시보드&lt;/b&gt; 제작 &amp;rarr; &lt;b&gt;CloudFront 모니터링 가시화&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;개발팀의 트래픽 확인 요청 해소&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2922&quot; data-origin-height=&quot;1510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MiUvL/dJMcajgAZT1/1s2JKYHdbZ3ekl2ZKoaWek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MiUvL/dJMcajgAZT1/1s2JKYHdbZ3ekl2ZKoaWek/img.png&quot; data-alt=&quot;처음 PromQL &amp;amp;amp; Grafana를 활용해서 작업하고, 개발팀에게 공유해본 경험&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MiUvL/dJMcajgAZT1/1s2JKYHdbZ3ekl2ZKoaWek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMiUvL%2FdJMcajgAZT1%2F1s2JKYHdbZ3ekl2ZKoaWek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;310&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2922&quot; data-origin-height=&quot;1510&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;처음 PromQL &amp;amp; Grafana를 활용해서 작업하고, 개발팀에게 공유해본 경험&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 인프라 E2E 장애 모니터링 가시성 확보 2: CloudFront Alert 시스템 구축&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Problem&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CloudFront 레거시 자원 점검을 계기로 &lt;b&gt;인프라 최앞단인 CloudFront 모니터링을 강화&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;Anomaly Request&lt;/b&gt;와 &lt;b&gt;403 Error Rate&lt;/b&gt; 얼럿을 통해 트래픽 이상 감지 체계&lt;/span&gt;를 구축.&lt;/li&gt;
&lt;li&gt;이후 false positive를 지속적으로 개선하며, 인프라 전반의 장애 대응 역량 강화를 목표로 진행중&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Task&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Datadog False Alert 평가 및 정리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CF additional metrics&lt;/b&gt; &lt;b&gt;활성화 후 false alert 급증&lt;/b&gt; &amp;rarr; 레거시 자원 목록 추출 및 정리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DDog Alert&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;제거 논의&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dz9QQZ/dJMcabXdvm2/eU4ADKKzDnPiTfCMZRyZzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dz9QQZ/dJMcabXdvm2/eU4ADKKzDnPiTfCMZRyZzk/img.png&quot; data-origin-width=&quot;1508&quot; data-origin-height=&quot;816&quot; data-is-animation=&quot;false&quot; width=&quot;600&quot; height=&quot;325&quot; style=&quot;width: 50.2632%; margin-right: 10px;&quot; data-widthpercent=&quot;50.85&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dz9QQZ/dJMcabXdvm2/eU4ADKKzDnPiTfCMZRyZzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdz9QQZ%2FdJMcabXdvm2%2FeU4ADKKzDnPiTfCMZRyZzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1508&quot; height=&quot;816&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xhjzp/dJMcabbPZt3/uCCwi82xxcGJh44ZfiTjx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xhjzp/dJMcabbPZt3/uCCwi82xxcGJh44ZfiTjx1/img.png&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;654&quot; data-is-animation=&quot;false&quot; width=&quot;400&quot; height=&quot;224&quot; data-widthpercent=&quot;49.15&quot; style=&quot;width: 48.574%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xhjzp/dJMcabbPZt3/uCCwi82xxcGJh44ZfiTjx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxhjzp%2FdJMcabbPZt3%2FuCCwi82xxcGJh44ZfiTjx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1168&quot; height=&quot;654&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;CF에 대한 Add Metrics을 활성화한 이후 Ddog 얼럿에 문제가 생겼고 이를 grafana alert rule로 풀어보는 작업으로 확장되었다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;(위 결과에 따른) Grafana Alert Rule 설계 및 구현&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Request Anomaly&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;Z-Score 기반 &lt;b&gt;단기(3h)/장기(2주) 추세 반영 쿼리 직접 구현&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;테스트 결과를 분석하며 단기 추세 계산을 24h &amp;rarr; 8h &amp;rarr; 3h로 최종 적용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;3h가 이상치 얼럿에 대한 최선의 판단이라고 결론&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장기 &amp;amp; 단기 추세 알고리즘 문서 (링크 첨부)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트래픽 이상치 유효성 검증 결과&lt;/b&gt; 분석 문서 &lt;b&gt;(링크 첨부)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Alert 계산 효율을 위해 특정 규모의 트래픽이 있는 Dist에 대해서만 수행하도록 &lt;b&gt;하한선 설정&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;하한선 측정 방법에 대한 문서:&lt;/b&gt; 위 알고리즘 문서와 동일&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOJHmH/dJMcagjUAc5/yWkUlN4biGZjBYDHkYTnQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOJHmH/dJMcagjUAc5/yWkUlN4biGZjBYDHkYTnQk/img.png&quot; data-origin-width=&quot;1176&quot; data-origin-height=&quot;978&quot; data-is-animation=&quot;false&quot; style=&quot;width: 40.4347%; margin-right: 10px;&quot; data-widthpercent=&quot;41.4&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOJHmH/dJMcagjUAc5/yWkUlN4biGZjBYDHkYTnQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOJHmH%2FdJMcagjUAc5%2FyWkUlN4biGZjBYDHkYTnQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1176&quot; height=&quot;978&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7m0ET/dJMcabXdvqv/bP0qR2t8tEhfEzA7wfK2FK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7m0ET/dJMcabXdvqv/bP0qR2t8tEhfEzA7wfK2FK/img.png&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;1102&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.796%; margin-right: 10px;&quot; data-widthpercent=&quot;32.55&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7m0ET/dJMcabXdvqv/bP0qR2t8tEhfEzA7wfK2FK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7m0ET%2FdJMcabXdvqv%2FbP0qR2t8tEhfEzA7wfK2FK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1042&quot; height=&quot;1102&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCDlxP/dJMcai228P2/tFulNRzqhm5UrQIlsNHvjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCDlxP/dJMcai228P2/tFulNRzqhm5UrQIlsNHvjK/img.png&quot; data-origin-width=&quot;1138&quot; data-origin-height=&quot;1504&quot; data-is-animation=&quot;false&quot; style=&quot;width: 25.4437%;&quot; data-widthpercent=&quot;26.05&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCDlxP/dJMcai228P2/tFulNRzqhm5UrQIlsNHvjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCDlxP%2FdJMcai228P2%2FtFulNRzqhm5UrQIlsNHvjK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1138&quot; height=&quot;1504&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;요렇게... PromQL로 알고리즘을 구현하고 그 얼럿 추이를 지켜본 뒤, 유효성을 검증했다. 지금은 Prod 환경에서 얼럿이 동작하고 있고, 계속해서 개선을 하다보니 쿼리가 많이 길어져서 레코딩룰로 전환하는 작업으로 이어졌다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;403 Error Rate
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DDoS, Origin 권한 문제 감지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;지속적인 False Positive 개선&lt;/b&gt; (/w Hun, Aidan)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;false positive 최소화를 위한 개선 작업&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Alert 가독성&lt;/b&gt;을 위한 메시지 포맷 개선 (project_name, Distribution ID 등 정보 추가)&lt;/li&gt;
&lt;li&gt;특정 프로젝트 자원 예외처리 및 사이드이펙트 대응&lt;/li&gt;
&lt;li&gt;Alert Delivery 적용하여 서비스팀 전달 체계 구축&lt;/li&gt;
&lt;li&gt;HAProxy, big-picture 등 Edge Case 예외처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Impact&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199; color: #ef5369;&quot;&gt;DDoS 감지&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjn3XA/dJMcaiPwCBs/r3xKXtYiTIxohTUdpKn5X0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjn3XA/dJMcaiPwCBs/r3xKXtYiTIxohTUdpKn5X0/img.png&quot; data-alt=&quot;실제로 DDoS 감지에 활용되고 있고 Q보다 빠르게 잡은 성과가 있었다.   DDoS 패턴이 다양해지고 있는데 여러 건 얼럿을 받아봐서 후속조치 등에 대한 고민을 하고있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjn3XA/dJMcaiPwCBs/r3xKXtYiTIxohTUdpKn5X0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcjn3XA%2FdJMcaiPwCBs%2Fr3xKXtYiTIxohTUdpKn5X0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;365&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실제로 DDoS 감지에 활용되고 있고 Q보다 빠르게 잡은 성과가 있었다.   DDoS 패턴이 다양해지고 있는데 여러 건 얼럿을 받아봐서 후속조치 등에 대한 고민을 하고있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로모션 이벤트 감지
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로모션 이벤트가 생기면 사용자가 몰리는데, 이에 대한 이상치도 감지되고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;전사 Traffic Anomaly Detection 활용 기대
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지속적인 False Positive 개선을 통해 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;인프라 앞단(CloudFront)의 트래픽 이상 감지 체계 고도화&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;향후 다른 레이어(istio 등)&lt;/b&gt;의 Anomaly Detection&lt;/span&gt;에도 해당 알고리즘 적용을 통한 모니터링 기대&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) &lt;b&gt;멀티 리전 환경에서 EC2 SSM 접근 로깅&amp;middot;암호화 구조 표준화&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;| Problem&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리전별로 EC2 접근 로깅 및 암호화 설정이 서로 달라서,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;리전마다 보안 설정을 확인해야 하는 비용이 발생할 수 있는 구조&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;주요 인스턴스 서버에 대한 접근 이력이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;일관된 기준으로 관리되고 있지 않음&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Task&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제 발견 및 작업 직접 제안
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EC2 기능 사내 플랫폼 오픈 준비 중 &lt;b&gt;SSM KMS 설정과 세션 로그 S3 버킷이 KR 리전에만 존재하는 것 발견&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;모든 환경/리전별 SSM 로킹 &amp;amp; 암호화 설정 &lt;b&gt;표준화 필요성 직접 제안&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;752&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnkDPL/dJMcahXrT4Q/WyvyKrQqUkgnq9kwHV2fqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnkDPL/dJMcahXrT4Q/WyvyKrQqUkgnq9kwHV2fqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnkDPL/dJMcahXrT4Q/WyvyKrQqUkgnq9kwHV2fqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnkDPL%2FdJMcahXrT4Q%2FWyvyKrQqUkgnq9kwHV2fqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;365&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;752&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;글로벌 리전 SSM 인프라 구축
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Alpha/Prod 환경의 글로벌 리전에 &lt;b&gt;세션 로그 S3 버킷 생성&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리전별 KMS Key&lt;/b&gt; 생성&lt;/li&gt;
&lt;li&gt;SSM Session에 대해 KMS 암호화 설정 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Impact&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;SSM 접근 로깅과 암호화 설정&lt;/b&gt;을 적용 &amp;rarr; &lt;span style=&quot;background-color: #f6e199; color: #ef5369;&quot;&gt;&lt;b&gt;환경/리전 관계 없이 접근 기록과 암호화 적용 여부가 보장되도록&lt;/b&gt; &lt;b&gt;인프라 구조 표준화&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;세션 로그는 개인정보&amp;middot;인증 정보가 포함된 &lt;b&gt;보안 핵심 로그&lt;/b&gt;이기에, &lt;b&gt;보안팀 일부 인원만 접근 가능&lt;/b&gt;하도록 설정&lt;/li&gt;
&lt;li&gt;해당 작업을 통해 &lt;b&gt;인프라 보안, SSM Logging 역량 강화&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5) 전사 EC2 SSM 접근 관리를 위한 공용 IAM Policy 적용 및 인프라 표준화&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Problem&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SSM 로깅&amp;middot;암호화 설정 후
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CA 리전 인스턴스(i-056...)에서 &lt;b&gt;SSM 접속 불가 문제&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;EKS 클러스터 &lt;b&gt;노드 접근 불가 문제&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;프로덕션 &lt;b&gt;worker Role 권한&lt;/b&gt; 문제&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Task&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;미적용 Role 추출 및 문서화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;환경/리전별로 SSM 접근 Policy 미할당 Role 추출&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;1056&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqpKqg/dJMcafefzWx/PxmgMqNOw9VG6hbKxNTDp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqpKqg/dJMcafefzWx/PxmgMqNOw9VG6hbKxNTDp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqpKqg/dJMcafefzWx/PxmgMqNOw9VG6hbKxNTDp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqpKqg%2FdJMcafefzWx%2FPxmgMqNOw9VG6hbKxNTDp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;548&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;1056&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;공용 Policy 정의 및 일괄 적용&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공통 Policy 에 최소 권한 원칙 적용 (KMS, S3 PutObject 등)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Alpha/Prod 환경의 모든 리전 Instance 관련 Role&lt;/b&gt;에 스크립트로 일괄 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;적용 검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;환경/리전별 haproxy 머신에 &lt;b&gt;SSM 명령어로 접근 가능 여부 검증&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;1086&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CK0QH/dJMcaaDZY8H/QKVk2bIoOVB9KJtsqQ1aAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CK0QH/dJMcaaDZY8H/QKVk2bIoOVB9KJtsqQ1aAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CK0QH/dJMcaaDZY8H/QKVk2bIoOVB9KJtsqQ1aAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCK0QH%2FdJMcaaDZY8H%2FQKVk2bIoOVB9KJtsqQ1aAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;516&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Impact&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;전사 인프라 &lt;b&gt;모든 EC2 인스턴스에 일관된 SSM 접근 정책 적용&lt;/b&gt; 완료&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;신규 인스턴스 생성 시에도 동일한 Policy가 자동 적용되는 &lt;b&gt;표준화된 구조&lt;/b&gt; 확보&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인프라실 위클리 발표&lt;/b&gt;를 통해 네트워크팀, DB팀 등 &lt;b&gt;SSM 사용 부서에 변경사항 공유&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6) 플랫폼 기반 인프라 프로비저닝&lt;b&gt;&amp;middot;운영 자동화: EC2 Instance 기능 도입&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Problem&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(SRE) 인스턴스 타입, 스토리지, AMI, 보안 그룹, IAM Role 설정과 SSM 접근 방법을&lt;span style=&quot;background-color: #f6e199;&quot;&gt; &lt;b&gt;매번 수동으로 처리&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 스펙을 위한 &lt;b&gt;커뮤니케이션 비용&lt;/b&gt; 증가&lt;/li&gt;
&lt;li&gt;수동으로 설정하다보니 &lt;b&gt;인프라 비표준화, 보안 유의사항을 누락&lt;/b&gt;하기 쉬운 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;(개발팀) 테스트용 서버가 필요할 때마다 SRE 요청 후 대기해야 하므로 &lt;b&gt;PoC 속도 저하 및 자율성 제한&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Task&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Tech Spec&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;922&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2GsnM/dJMcadgn1b4/kX7IQvueAkuJqIOVU7PHFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2GsnM/dJMcadgn1b4/kX7IQvueAkuJqIOVU7PHFk/img.png&quot; data-alt=&quot;테크 스펙을 작성하고 논의했다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2GsnM/dJMcadgn1b4/kX7IQvueAkuJqIOVU7PHFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2GsnM%2FdJMcadgn1b4%2FkX7IQvueAkuJqIOVU7PHFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;518&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;922&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테크 스펙을 작성하고 논의했다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EC2 Instance &lt;b&gt;서버 보안 강화&lt;/b&gt;를 위한 설정들 (w/보안팀)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;모든 Instance에&lt;/b&gt;user-data Script를 통한 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;CrowdStrike&lt;/b&gt;&lt;/span&gt; 강제화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모든 Instance에 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;IMDSv2&lt;/span&gt;&lt;/b&gt; 설정 강제화&lt;/li&gt;
&lt;li&gt;Security Group &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Inbound&lt;/span&gt; 제한&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보안상 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;최소 권한 부여&lt;/span&gt;&lt;/b&gt;를 위한 논의 스레드:&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;사내 플랫폼 EC2 생성&amp;middot;수정&amp;middot;삭제 작업&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;830&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cY6KJV/dJMcacaKvfw/tTlHGGqDFGr2WS7OK9GorK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cY6KJV/dJMcacaKvfw/tTlHGGqDFGr2WS7OK9GorK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cY6KJV/dJMcacaKvfw/tTlHGGqDFGr2WS7OK9GorK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcY6KJV%2FdJMcacaKvfw%2FtTlHGGqDFGr2WS7OK9GorK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;830&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Impact&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Test용 서버를 플랫폼으로 쉽고 빠르게 생성&lt;/li&gt;
&lt;li&gt;앞으로 생성되는 EC2 Instance는 &lt;b&gt;CrowdStrike&lt;/b&gt;, &lt;b&gt;IMDSv2, SSM 접근 등 보안 사항 준수&lt;/b&gt; &amp;rarr; 인프라 표준화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7) CloudFront SaaS Manager 도입&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Problem&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;FE 배포된 프론트엔드 사이트의 도메인 변경 건&lt;/b&gt;이 필요할 때마다 개발팀 &amp;rarr; SRE 요청
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SRE는 사내 플랫폼 API를 통해 도메인/ACM 인증서를 직접 수정하고, 사내 플랫폼 DB 값도 별도로 수정해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;전사의 프론트엔드 배포 건수가 많아지면서 CloudFront, 서브도메인 관리 부담도 함께 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Task&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;사내 플랫폼&amp;nbsp;API 구현 및 PoC 진행&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;boto3 API로 multi-tenant &lt;b&gt;생성 기능 검증 및 테스트&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;개발팀 미팅 및 요구사항 협의&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1차 (7/14): SaaS Manager 기능 소개&lt;b&gt;&amp;nbsp;도입 가능성&lt;/b&gt; 논의&lt;/li&gt;
&lt;li&gt;2차 (10/23): &lt;b&gt;API 스펙&lt;/b&gt; 및 multi-tenant 구조 논의&lt;/li&gt;
&lt;li&gt;결정 사항: FE 배포 전용 multi-tenant 1개 구성, 사내 플랫폼 즉시 도입은 보류 &lt;b&gt;SRE팀 내부 검토 및 결정&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구조 설계 재검토 및 선행 작업&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기 구조 설계가 복잡해 동료와&lt;b&gt;&amp;nbsp;협업하여 워크플로우 재검토&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;SaaS Manager 도입을 위한 선행 작업으로 사내&lt;b&gt;&amp;nbsp;플랫폼 도메인 스코프 정리&lt;/b&gt; 진행&lt;/li&gt;
&lt;li&gt;도메인 스코프 정리 완료 후 CF multi-tenant 사내 플랫폼 API 스펙 재정의&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Impact&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사내 플랫폼 StaticWebsite &lt;b&gt;도메인 스코프 정리 및 개선 완료&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;PoC 기술 검토 완료,&lt;b&gt; FE 배포 전체 방향성 논의로 확장&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8) AWS 비용 이상치 탐지 시스템 구축을 통한 비용 관리&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Problem&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MAU 2000만, 서비스가 점점 커지고 다양한 시도를 하면서 &lt;b&gt;인프라 비용 꾸준히 증가&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;GCP는 비용 이상치 감지 시스템이 구축되어있지만&lt;b&gt;, AWS는 수동 확인에 의존&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이상 비용은 운영 이슈로 이어질 수 있고, 인프라 비용 관리 관점에서 중요도가 높음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Task&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;출시 공지(기능, 사용법, 실제 탐지 내용)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1340&quot; data-origin-height=&quot;898&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cD7zfp/dJMcab3YMNz/D2DR5lF7iGDVGf5tFe2GI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cD7zfp/dJMcab3YMNz/D2DR5lF7iGDVGf5tFe2GI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cD7zfp/dJMcab3YMNz/D2DR5lF7iGDVGf5tFe2GI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcD7zfp%2FdJMcab3YMNz%2FD2DR5lF7iGDVGf5tFe2GI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;402&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1340&quot; data-origin-height=&quot;898&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트 단계
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;중간 과정 및 의사결정&lt;/b&gt;에 대한 정리 문서 (문서 링크)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Slack 채널 자동 알림&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Impact&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;결과물: threshold=$1,000이 넘는 비용 이상치에 대한 모니터링&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모니터링 과정을 거쳐 threshold 값 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;1476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUhL4j/dJMcaacV4zj/TzPvWhnBJSXq8yG9DAhtV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUhL4j/dJMcaacV4zj/TzPvWhnBJSXq8yG9DAhtV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUhL4j/dJMcaacV4zj/TzPvWhnBJSXq8yG9DAhtV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUhL4j%2FdJMcaacV4zj%2FTzPvWhnBJSXq8yG9DAhtV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;747&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;1476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예시 얼럿 유형:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예시1(MSK 스케일업)&lt;/li&gt;
&lt;li&gt;예시2(prod---x 관련 DB 스케일업)&lt;/li&gt;
&lt;li&gt;예시3(ppxx-platform $2,000 증가)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;얼럿 유효성이 검증되어 &lt;b&gt;개발팀 온콜 멘션 그룹 활성화 계획(2026 1분기)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9) StaticWebsite SLI 정의 및 사내 플랫폼 대시보드 구성&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Problem&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;StaticWebiste(R53, CloudFront, S3) 장애 감지 및 대응 체계 강화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현황: CloudFront 관련 장애 발생 시 에러 조기 감지율 부족&lt;/li&gt;
&lt;li&gt;목표: 에러 캐치율 개선을 통한 장애 대응 시간 단축&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Task&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CloudFront SLI 정의를 위한 &lt;b&gt;아이데이션&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CloudFront SLI Info, success_request&lt;/b&gt; 관련 작업&amp;nbsp;&lt;/li&gt;
&lt;li&gt;CloudFront 메트릭을 위한 &lt;b&gt;레거시 Tag 제거&lt;/b&gt; 작업&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;R53 SLI 개선&lt;/b&gt; 작업&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사내 플랫폼 SLI 가시화&lt;/b&gt; 작업&lt;/li&gt;
&lt;li&gt;클라우드파트 SLI 문서 정리 작업&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| Impact&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사내 플랫폼 StaticWebsite 모니터링 가시화&lt;/li&gt;
&lt;li&gt;R53 Exporter &lt;b&gt;DNS Lookup 대상 추가&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;R53 Exporter 메트릭 수집 성능 개선&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1508&quot; data-origin-height=&quot;1460&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfiSuB/dJMcabQrlKv/istAkl0LLnPfFEDgRoImR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfiSuB/dJMcabQrlKv/istAkl0LLnPfFEDgRoImR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfiSuB/dJMcabQrlKv/istAkl0LLnPfFEDgRoImR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfiSuB%2FdJMcabQrlKv%2FistAkl0LLnPfFEDgRoImR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;581&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1508&quot; data-origin-height=&quot;1460&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;운영 업무&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;| 장애 대응&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;| Pay Oncall&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SRE 클라우드 파트 &lt;b&gt;Pay 업무 온콜 담당&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적: 온콜 운영 경험을 통한 &lt;b&gt;Pay 업무 이해도 향상 및 파트 내 지식 공유&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pay 업무 사흘 간 느낀 점 회고&lt;/li&gt;
&lt;li&gt;&lt;b&gt;온콜 일지&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;| Market Oncall&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;| 기타 운영&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;멀티 리전 Prod 환경의 &lt;b&gt;KMS 삭제 및 비활성화 권한 Deny&lt;/b&gt; (/w Lu)&lt;/li&gt;
&lt;li&gt;AWS GuardDuty 비용 증가 원인 파악을 위해 비용 모니터링 Budgets 설정&lt;/li&gt;
&lt;li&gt;StaticWebsite 관련 &lt;b&gt;도메인 변경&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;발표 및 공유&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[25.03.07 | 인프라 위클리] 신규입사자가 느낀 개선점과 방향성 발표&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[25.05.16 | 인프라 위클리] Kost Anomaly Detection 기능 소개&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[25.07.18 | 인프라 위클리] CloudFront Saas Manager 기능 소개 및 협업 과정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[25.08.28 | Tech Allhands] Kontrol ElastiCache for Valkey 출시와 관련 내용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[25.12.05 | 인프라 위클리] Kontrol EC2 도입 과정 중 인프라 &amp;amp; 보안 개선점 소개&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기타&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[25.09.03] Builder's Camp 해커톤 시뮬레이션 참여&lt;/p&gt;</description>
      <category>회고 &amp;amp; 후기/회고 &amp;amp; 후기</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/321</guid>
      <comments>https://cobinding.tistory.com/321#entry321comment</comments>
      <pubDate>Sun, 11 Jan 2026 14:52:34 +0900</pubDate>
    </item>
    <item>
      <title>[DevOps] SSH 없이 안전하게 EC2 운영하기: SSM 기반 접근 제어와 전역 정책 설계</title>
      <link>https://cobinding.tistory.com/320</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스가 점점 커지면서 개발자의 다양한 PoC 니즈를 충족하기 위해, EC2 Instance Self-Service 기능을 준비했다. 프라이빗 환경에서 서버가 뜰 테지만, 아무래도 서버를 오픈하는 건 여러 위험성이 있기에 작업 전에 테크 스펙을 작성해서 &lt;b&gt;여러가지 인프라/보안 이슈&lt;/b&gt;에 대해 검토했다. 그 중에서도 가장 레슨런이 많았던 &lt;b&gt;Session Manager와 SSM Logging&lt;/b&gt;에 대해 정리해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. EC2 Instance 접근 방식&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2 Instance에 접근하는 다양한 방식이 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfSXPa/dJMcab3L02q/40OsfZsFZxXD9kglMHhJX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfSXPa/dJMcab3L02q/40OsfZsFZxXD9kglMHhJX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfSXPa/dJMcab3L02q/40OsfZsFZxXD9kglMHhJX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfSXPa%2FdJMcab3L02q%2F40OsfZsFZxXD9kglMHhJX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;225&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;502&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 EC2 Instance Connect (&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/ec2-instance-connect-methods.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt;)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 기반 SSH 연결 방식으로, &lt;b&gt;AWS가 일시적인 SSH 퍼블릭 키를 자동 생성&lt;/b&gt;하여 60초간 인스턴스 메타데이터에 푸시한다. SSH Client와 다른 점은 &lt;b&gt;직접 .pem과 같은 Key를 사용하는 것이 아니라, &lt;span style=&quot;color: #ef5369;&quot;&gt;사용자는 IAM Role을 통해&lt;/span&gt; SSH에 접근 권한을 평가 받는다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;콘솔 연결 시, Public IP 주소가 필요&lt;/li&gt;
&lt;li&gt;인바운드 22포트 허용 필요&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;`ec2-instance-connect:SendSSHPublicKey` &lt;span style=&quot;color: #000000;&quot;&gt;권한 필요&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;CLI 명령어로는 다음과 같이 접근 가능&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1765019602927&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;aws ec2-instance-connect ssh --instance-id i-1234567890example&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인스턴스 커넥트를 사용할 때는 endpoint가 필요한데..! 이거 작업 추기로 해보기&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2 Session Manager (&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/systems-manager/latest/userguide/session-manager.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt;)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS SSM의 기능으로, &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;SSH 키 없이 IAM 기반&lt;/b&gt;&lt;/span&gt;으로 인스턴스에 접근하는 방식이다. &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;HTTPS(443)&lt;/span&gt;&lt;/b&gt; 프로토콜을 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인바운드 22 포트 개방 X&lt;/li&gt;
&lt;li&gt;Public IP 불필요&lt;/li&gt;
&lt;li&gt;SSM Agent 설치 필수&lt;/li&gt;
&lt;li&gt;IAM 역할 필수&lt;/li&gt;
&lt;li&gt;CloudWatch, S3로 세션 로깅 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;1.3 SSH Client (&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/connect-linux-inst-ssh.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt;)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;SSH 프로토콜을 사용해서 로컬 터미널에서 직접 인스턴스에 연결하는 방식이다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;키 페어 파일(.pem) 필수 -&amp;gt; &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;사용자가 키 직접 관리&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;인바운드 22 포트 필요&lt;/li&gt;
&lt;li&gt;Public IP or Pirvate 경로 필요&lt;/li&gt;
&lt;li&gt;로컬 SSH 클라이언트 사용(OpenSSH, PuTTY 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.4 EC2 Serial Console (&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/ec2-serial-console.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt;)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스턴스의 직렬 포트(serial port)에 직접 접근하는 방식이다. 특이한 점은 &lt;b&gt;네트워크 연결 없이 인스턴스 접근이 가능&lt;/b&gt;해서, 네트워크 장애 시 유용한 접근 방법으로 통한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;계정 수준에서 명시적 활성화 필요&lt;/li&gt;
&lt;li&gt;인스턴스당 1개의 동시 세션만 허용&lt;/li&gt;
&lt;li&gt;인스턴스가 반드시 running 상태여야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;border: 1px solid #dddddd; padding: 8px; background-color: #f2f2f2; width: 11.9767%;&quot;&gt;항목&lt;/th&gt;
&lt;th style=&quot;border: 1px solid #dddddd; padding: 8px; background-color: #f2f2f2; width: 23.3721%;&quot;&gt;Instance Connect&lt;/th&gt;
&lt;th style=&quot;border: 1px solid #dddddd; padding: 8px; background-color: #f2f2f2; width: 25.3488%;&quot;&gt;⭐️ Session Manager&lt;/th&gt;
&lt;th style=&quot;border: 1px solid #dddddd; padding: 8px; background-color: #f2f2f2; width: 15.1163%;&quot;&gt;SSH Client&lt;/th&gt;
&lt;th style=&quot;border: 1px solid #dddddd; padding: 8px; background-color: #f2f2f2; width: 24.0698%;&quot;&gt;Serial Console&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 11.9767%;&quot;&gt;네트워크 필요&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 23.3721%;&quot;&gt;필수 (퍼블릭 IP)&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 25.3488%;&quot;&gt;필수 (Private 가능)&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 15.1163%;&quot;&gt;필수&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 24.0698%;&quot;&gt;불필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 11.9767%;&quot;&gt;키 관리&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 23.3721%;&quot;&gt;자동 (60초 임시)&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 25.3488%;&quot;&gt;불필요&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 15.1163%;&quot;&gt;수동 (.pem)&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 24.0698%;&quot;&gt;SSH 키 또는 패스워드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 11.9767%;&quot;&gt;인바운드 포트&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 23.3721%;&quot;&gt;22 필요&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 25.3488%;&quot;&gt;불필요&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 15.1163%;&quot;&gt;22 필요&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 24.0698%;&quot;&gt;불필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 11.9767%;&quot;&gt;주 용도&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 23.3721%;&quot;&gt;간편 SSH 접속&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 25.3488%;&quot;&gt;대규모 관리&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 15.1163%;&quot;&gt;전통적 접속&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 24.0698%;&quot;&gt;부팅/네트워크 장애 해결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 11.9767%;&quot;&gt;IAM 인증&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 23.3721%;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 25.3488%;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 15.1163%;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #dddddd; padding: 8px; width: 24.0698%;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Session Manager 적용과 그 이유&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 Session Manager가 필요한 이유&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️ 기존 SSH 접근 방식의 한계&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 SSH 기반 EC2 접근 방식은 다음과 같은 문제점이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Private Subnet&lt;/b&gt; 환경에 있는 인스턴스에 접근하기 위해서는 따로 &lt;b&gt;전용 서버 운영&lt;/b&gt;이 필요하다.&lt;/li&gt;
&lt;li&gt;EC2 인스턴스마다 &lt;b&gt;Key Pair 생성 및 안전한 보관&lt;/b&gt;이 필요하다. (&lt;u&gt;만약 키가 노출된다면...!  )&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;Key 사용을 위한 Inbound Port 22 오픈이 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;접속 로그&lt;/b&gt;나 &lt;b&gt;실행 명령어를 추적&lt;/b&gt;할 방법이 부족하다.&lt;/li&gt;
&lt;li&gt;해당 인스턴스 사용자가 증가할 수록 &lt;b&gt;키/권한 관리가 복잡&lt;/b&gt;하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Inbound 22 Port 오픈이 왜 위험할까?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;22번 포트를 여는 것은 &lt;b&gt;외부에서 SSH로 해당 서버에 접속할 수 있도록 허용하는 것을 의미&lt;/b&gt;하기 때문이다.&lt;/li&gt;
&lt;li&gt;SSH(Secure Shell) 서비스의 표준 포트로, IANA(Internet Assigned Numbers Authority)가 공식 할당한 포트 번호다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️&lt;span&gt; SSM Session Manager 이점&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인바운드 포트를 열 필요가 없고, HTTPS(443) 프로토콜을 사용하여 추가 포트 개방이 불필요하다.&lt;/li&gt;
&lt;li&gt;EC2 인스턴스의 Public IP 주소도 필요없다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;IAM 기반&lt;/b&gt;&lt;/span&gt;으로 조직 내에 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;어떤 구성원이 세션을 시작&lt;/b&gt;&lt;/span&gt;할 수 있는지, &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;어떤 노드에 접근&lt;/b&gt;&lt;/span&gt;할 수 있는지 제어가 가능하다.&lt;/li&gt;
&lt;li&gt;CloudWatch Logs, S3 버킷을 통한 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;모든 세션 활동을 기록&lt;/b&gt;&lt;/span&gt;할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 이유로 SSM Session Manager를 활용하여 Session 을 시작한 모든 &lt;u&gt;&lt;b&gt;① 사내 유저 Id&lt;/b&gt;&lt;/u&gt;와 &lt;u&gt;&lt;b&gt;② 해당 로그&lt;/b&gt;&lt;/u&gt;를 기록하도록 설정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;2.2 Session Manager 동작 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Session Manager 동작 방식을 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWmHot/dJMcah3Zdpr/slvYyZq76BgNDXOjkxlor0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWmHot/dJMcah3Zdpr/slvYyZq76BgNDXOjkxlor0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWmHot/dJMcah3Zdpr/slvYyZq76BgNDXOjkxlor0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWmHot%2FdJMcah3Zdpr%2FslvYyZq76BgNDXOjkxlor0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;350&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;756&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유저가 SSM으로 인스턴스에 접근하기 위해 가장 처음하는 것이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/systems-manager/latest/APIReference/API_StartSession.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;StartSession API&lt;/a&gt; 호출&lt;/b&gt;이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Session API를 호출하면, Session Manger가 Stream URL과 토큰을 return한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;유저는 이 값으로&amp;nbsp; Session Manager에 Websocket 연결을 하고, Session Manager를 프록시로 사용하며 인스턴스와 소통한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1765107323119&quot; class=&quot;json&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;{
   &quot;SessionId&quot;: &quot;string&quot;,
   &quot;StreamUrl&quot;: &quot;string&quot;,
   &quot;TokenValue&quot;: &quot;string&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EC2 Instance에 연결된 IAM Role을 통해 &lt;b&gt;Session Manager와 통신하는 데 필요한 권한 &amp;amp; 세션 로깅 권한&lt;/b&gt;을 설정한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;SSM Agent가 인스턴스를 시작할 때, Instance Profile에 연결된 IAM Role을 assume&lt;/b&gt;&lt;/span&gt;한다. 이 권한으로 Session Manager 서비스에 아웃바운드 연결하고, WebSocket 채널을 유지할 수 있게 된다. (&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#ec2-instance-profile&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Instance Profile에 대한 글!&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;세션 로깅이 활성화&lt;/b&gt;된 경우에는 버킷에 접근할 권한이 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;세션 데이터 암호화를 활성화&lt;/b&gt;했다면, 세션 데이터 암복호화 권한이 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Session Manager를 통한 Session 로깅 &amp;amp; 모니터링 설정&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 AWS System Manager의 Session Manager 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;System Manager는 계정・리전 단위로 설정&lt;/b&gt;된다. 필자가 속한 회사의 경우 Alpha, Prod 두 환경을 운영하고 있기에 각각의 계정과 KR, JP, CA, GB 리전에 대해 설정해주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;1386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HWcuM/dJMcab3L02r/pfufvABFkJrMvvNcNuSi9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HWcuM/dJMcab3L02r/pfufvABFkJrMvvNcNuSi9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HWcuM/dJMcab3L02r/pfufvABFkJrMvvNcNuSi9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHWcuM%2FdJMcab3L02r%2FpfufvABFkJrMvvNcNuSi9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;532&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;1386&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자의 경우 &lt;b&gt;General Preferences&lt;/b&gt;,&lt;b&gt; S3 logging&lt;/b&gt; 부분을 활성화 해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️ General Preferences&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1660&quot; data-origin-height=&quot;1092&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bum25O/dJMcab3L02s/KUM3CWfadzSIVRkpRSArOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bum25O/dJMcab3L02s/KUM3CWfadzSIVRkpRSArOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bum25O/dJMcab3L02s/KUM3CWfadzSIVRkpRSArOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbum25O%2FdJMcab3L02s%2FKUM3CWfadzSIVRkpRSArOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;460&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1660&quot; data-origin-height=&quot;1092&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 첫 번째 옵션은 세션 데이터를 KMS 키로 암호화하는 것에 대한 설정이다. 이 설정을 하지 않으면 세션 데이터가 평문으로 전송/저장되어, 보안을 위해 활성화하였다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;사용자와 EC2 인스턴스 간 주고받는 모든 명령어와 출력이 암호화&lt;/b&gt;된다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;SSM 접근 및 통신을 위해 KMS 암복호화 관련 권한이 추가로 요구된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;S3에 저장되는 세션 로그가 암호화되어 저장된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;⚫️&lt;span&gt; S3 Logging&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;SessionLog에 대한 정보를 볼 수 있다. &lt;b&gt;누가, 언제, 어떻게 접근&lt;/b&gt;하였고 어떤 명령어를 썼는지 모두 확인 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buQXID/dJMcab3L02v/FhgH62MvV62KyI8F3GOrB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buQXID/dJMcab3L02v/FhgH62MvV62KyI8F3GOrB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buQXID/dJMcab3L02v/FhgH62MvV62KyI8F3GOrB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuQXID%2FdJMcab3L02v%2FFhgH62MvV62KyI8F3GOrB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;477&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1678&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 이미지가 그 예시다. Session Manager의 S3 Logging 활성화를 통해서 위와 같이 &lt;b&gt;session을 시작한 사내 이메일과 접속 시각, 로그를 확인&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;여기서 주의할 점&lt;/b&gt;&lt;/span&gt;은 이러한 Session 데이터를 남겨서 보안 이슈에 대응할 수 있지만, &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;그 반대로 Session 데이터가 노출되면 보안 위험에 처할 수 있다.&lt;/b&gt;&lt;/span&gt; 따라서 &lt;b&gt;해당 버킷 접근 권한은 root 계정과 보안팀 일부 구성원들로 한정했다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 마주한 문제&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Session 암호화 &amp;amp; Logging을 전역 설정하다보니&amp;hellip; 글로벌 리전에 대한 SSM 접근이 실패하는 현상을 겪었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 트러블 슈팅을 위해 SSM으로 접근했는데 실패&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PezHL/dJMcah3Zdpo/KtxWzlzbV5jmROgb2PINs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PezHL/dJMcah3Zdpo/KtxWzlzbV5jmROgb2PINs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PezHL/dJMcah3Zdpo/KtxWzlzbV5jmROgb2PINs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPezHL%2FdJMcah3Zdpo%2FKtxWzlzbV5jmROgb2PINs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;157&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1&quot; data-origin-height=&quot;1&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biwZ8Z/dJMcah3Zdpn/O38MhrMqCdfCyPGoLoaR5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biwZ8Z/dJMcah3Zdpn/O38MhrMqCdfCyPGoLoaR5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biwZ8Z/dJMcah3Zdpn/O38MhrMqCdfCyPGoLoaR5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiwZ8Z%2FdJMcah3Zdpn%2FO38MhrMqCdfCyPGoLoaR5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1&quot; height=&quot;1&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1&quot; data-origin-height=&quot;1&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 HAProxy Ansible 실패&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Nlhuo/dJMcagKMkaw/jE0I3okV0Bf2GssLa9sYv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Nlhuo/dJMcagKMkaw/jE0I3okV0Bf2GssLa9sYv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Nlhuo/dJMcagKMkaw/jE0I3okV0Bf2GssLa9sYv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNlhuo%2FdJMcagKMkaw%2FjE0I3okV0Bf2GssLa9sYv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;39&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;118&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.3&lt;span style=&quot;color: #ef5369;&quot;&gt; [해결법]&lt;/span&gt; SSM 접근을 위한 전역 공통 IAM Policy 정의 및 등록&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 여러 계정/리전을 함께 운영하면서 많이 헷갈려왔던 것인데..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 AWS System Manager는 &lt;b&gt;각 리전/환경별로 설정되는 값&lt;/b&gt;이기 때문에 &lt;b&gt;모든 환경/리전에 대한 관련 권한을 등록해&lt;/b&gt;야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IAM Role&lt;/b&gt;의 경우 &lt;b&gt;글로벌&lt;/b&gt; 서비스여서... &lt;b&gt;SSM 접근을 위한 공용 Policy를 설정&lt;/b&gt;하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) ⭐️ &lt;span style=&quot;color: #ef5369;&quot;&gt;최소 권한&lt;/span&gt;만 갖도록 Policy 정책을 재정의&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 인스턴스가 물고있는 Role에 부여할 Policy이므로,&lt;b&gt; 보수적으로 접근하여 허용할 권한(JSON)을 작성&lt;/b&gt;하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허용한 목록은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AmazonSSMManagedInstanceCore&lt;/li&gt;
&lt;li&gt;KMSEncryptionForSessionManager&lt;/li&gt;
&lt;li&gt;S3BucketAccessForSessionManager&lt;/li&gt;
&lt;li&gt;S3EncryptionForSessionManager&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) &lt;span style=&quot;color: #ef5369;&quot;&gt;환경/리전별&lt;/span&gt;로 &lt;span style=&quot;color: #ef5369;&quot;&gt;공용 Policy&lt;/span&gt;가 바인딩 되지 않은 Role을 추출 및 적용&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;SSM 접근이 필요한 인스턴스가 물고있는 Role에 대해서만 적용&lt;/b&gt;하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;1378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WdqE4/dJMcah3Zdpq/27piEgkfbNksY6eY0OzC1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WdqE4/dJMcah3Zdpq/27piEgkfbNksY6eY0OzC1K/img.png&quot; data-alt=&quot;기존 Policy에 부족한 권한을 추가하는 일이라 장애는 없을 테지만, Sciprt 대상 목록을 추출해서 공유&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WdqE4/dJMcah3Zdpq/27piEgkfbNksY6eY0OzC1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWdqE4%2FdJMcah3Zdpq%2F27piEgkfbNksY6eY0OzC1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;494&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;1378&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기존 Policy에 부족한 권한을 추가하는 일이라 장애는 없을 테지만, Sciprt 대상 목록을 추출해서 공유&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1730&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBblwV/dJMcaajut6M/GGxFxkOl8VONNOPoQNip30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBblwV/dJMcaajut6M/GGxFxkOl8VONNOPoQNip30/img.png&quot; data-alt=&quot;그리고 해당 목록을 공유하고, 공지 후에 작업 진행!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBblwV/dJMcaajut6M/GGxFxkOl8VONNOPoQNip30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBblwV%2FdJMcaajut6M%2FGGxFxkOl8VONNOPoQNip30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;206&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1730&quot; data-origin-height=&quot;510&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그리고 해당 목록을 공유하고, 공지 후에 작업 진행!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3) 접근 권한 부여 완료...!&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1260&quot; data-origin-height=&quot;247&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VeCzz/dJMcadHfaml/caIL6n6bAn7TYFORccPHU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VeCzz/dJMcadHfaml/caIL6n6bAn7TYFORccPHU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VeCzz/dJMcadHfaml/caIL6n6bAn7TYFORccPHU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVeCzz%2FdJMcadHfaml%2FcaIL6n6bAn7TYFORccPHU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;137&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1260&quot; data-origin-height=&quot;247&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 레슨런&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이번 일을 하면서 인프라의 표준화와 정책의 중요성을 알게되었다. 하나의 작업을 할 때에, 다양한 관점에서 고민하고 일관된 정책을 적용해본 특별한 경험이었다. 옆의 팀원들이 정책을 잘 설계하는 것과 기술 부채를 고민하는 모습들을 종종 보았는데 왜 그러한 고민들을 하는지 온전히 이해할 수 있게 되었다.&lt;/li&gt;
&lt;li&gt;특히 보안 관점에서는 기존의 나라면 SG이나 Inbound 정도 고민했었을 텐데, 다양한 보안 고려사항을 알 수 있게 되었다. AMI, CroudStrike, 접근 방식, 표준화된 인프라 등등...&amp;nbsp;&lt;/li&gt;
&lt;li&gt;그리고 SSM 동작 방식에 대해 깊이 있게 살펴본 점도 도움되었다. 원래는 그냥 SSM을 사용했다면 이제는 왜 SSH가 위험한지, SSM은 어떠한 문제가 있는지 고민할 수 있게 되었다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;그래서 좀 더 구체적으로 고민이 드는 것은 SSM 접근은 ssm-agent가 정상 상태일 때만 가능하기 때문에 대책/가이드 필요하다는 것이다. 요고는 조금 더 신경써서 공부하고 챙겨보려고 한다.&lt;/li&gt;
&lt;li&gt;(추가 작성) 당장 경험으로는 일단 4가지 접근 방식 중 4번이 간단하긴 한데, 너무 느린 점. 1번의 경우에는 커넥션을 위한 엔드포인트가 필요하다. 이것도 공용으로 미리 만들어 두는 게 좋을지..?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/DevOps</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/320</guid>
      <comments>https://cobinding.tistory.com/320#entry320comment</comments>
      <pubDate>Sun, 7 Dec 2025 21:46:09 +0900</pubDate>
    </item>
    <item>
      <title>[BackEnd] 비동기 테스크 큐와 DI Container (feat. Celery)</title>
      <link>https://cobinding.tistory.com/319</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 학습 흐름이 아래와 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;IoC &amp;amp; DIP &amp;amp; DI&amp;nbsp;&lt;/li&gt;
&lt;li&gt;FrameWork의 DI Container로 여러 의존성 관리&lt;/li&gt;
&lt;li&gt;싱글톤 패턴 &amp;amp; DI Container 직접 만들어서 사용하기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 흐름에 따라서, 이번에는 DI Container를 직접 구현해서 사용할 때, 비동기 테스크 큐들이 어떤 이점을 누릴 수 있는지 살펴보자.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;먼저 &lt;u&gt;비동기 테스크 큐&lt;/u&gt;와 파이썬 생태계의 대표적 라이브러리인 &lt;u&gt;Celery&lt;/u&gt;를 알아보고,&lt;/li&gt;
&lt;li&gt;규모가 큰 &lt;u&gt;분산 환경 프로젝트&lt;/u&gt;에서 Celery와 같은 비동기 테스크 큐를&lt;/li&gt;
&lt;li&gt;&lt;u&gt;DI Container에 등록하는 방법과 이점&lt;/u&gt;을 살펴볼 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 비동기 테스크 큐&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 비동기 테스크 큐란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말해서 &lt;b&gt;지금 당장 처리하지 않아도 되는 일을, 나중에 따로 처리하게 만드는 시스템&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1802&quot; data-origin-height=&quot;1308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by7doL/dJMcachapTW/GuXAb7R03OE4kk8KKmKVq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by7doL/dJMcachapTW/GuXAb7R03OE4kk8KKmKVq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by7doL/dJMcachapTW/GuXAb7R03OE4kk8KKmKVq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fby7doL%2FdJMcachapTW%2FGuXAb7R03OE4kk8KKmKVq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;508&quot; data-origin-width=&quot;1802&quot; data-origin-height=&quot;1308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;백엔드 서버는 보통 아래와 같은 구조다.&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;클라이언트가 HTTP 요청 보냄&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;서버가 요청 처리&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;응답 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;⚫️&lt;span&gt; &amp;nbsp;&lt;/span&gt;그런데 어떤 작업들은...&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-start=&quot;227&quot; data-end=&quot;270&quot;&gt;오래 걸림 (예: 이미지 리사이즈, 영상 인코딩, 대용량 CSV 처리)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-start=&quot;271&quot; data-end=&quot;311&quot;&gt;외부 API를 여러 번 호출 (결제 정산, 써드파티 연동)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-start=&quot;312&quot; data-end=&quot;346&quot;&gt;꼭 실시간일 필요 없음 (알림 발송, 이메일 발송)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;⚫️&lt;span&gt; &amp;nbsp;&lt;/span&gt;이들을 기존 구조로 처리하면 :&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;사용자는 쓸데없이 오래 기다림&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;웹 서버 스레드를 금방 소진해서 전체 서비스가 느려짐&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;=&amp;gt; 따라서 위와 같은 작업들은 큐에 넣어두고 나중에 따로 처리하게 한다!&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;1.2%20%EB%8F%99%EC%9E%91%20%EA%B5%AC%EC%A1%B0(%EC%95%84%EC%A3%BC%20%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9D%B8)-1&quot; style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;1.2 동작 구조(아주 기본적인)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;웹 서버(Producer)
&lt;ul style=&quot;list-style-type: disc; color: #353638;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;사용자의 요청을 받음&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;해당 작업은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;할일 목록(큐)&lt;/b&gt;에 넣어두고, 바로 응답을 돌려줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;큐(메시지 브로커)
&lt;ul style=&quot;list-style-type: disc; color: #353638;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;할일 목록&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;Redis, RabbitMQ, SQS 등&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;작업 하나하나가 메시지처럼 저장됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;워커(Consumer)
&lt;ul style=&quot;list-style-type: disc; color: #353638;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;메시지 브로커 큐에서 할일을 하나씩 처리함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;1.3%20%EB%B9%84%EB%8F%99%EA%B8%B0%20%ED%85%8C%EC%8A%A4%ED%81%AC%20%ED%81%90%EB%A5%BC%20%EB%8F%84%EC%9E%85%ED%95%98%EB%A9%B4%20%EC%9E%A5%EC%A0%90-1&quot; style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;1.3&amp;nbsp; 장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;오래 걸리는 작업은 워커 서버에 위임 하고, 사용자에게는 빠른 응답을 제공해서 서비스 품질을 높인다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;ex. 메일 전송, 알림, 로그 등의 작업&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;요청 처리 시간이 큐에 넣는 시간 정도로 짧아진다.&lt;/li&gt;
&lt;li&gt;트래픽이 많아질 때는 워커 수만 스케일 아웃하면 되므로, 적은 비용으로 많은 효과를 누릴 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 파이썬 생태계의 작업 큐, Celery&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Celery는 Python 언어로 작성된, &lt;b&gt;분산시스템&lt;/b&gt;에서 복잡한 작업들을 효과적으로 정의하고 처리할 수 있도록 도와주는&lt;b&gt; 비동기 작업 큐&lt;/b&gt; 라이브러리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 간단한 코드 예시&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;celery task 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763216387924&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from celery import Celery

app = Celery(
    &quot;my_app&quot;,
    broker=&quot;redis://localhost:6379/0&quot;,        # 브로커 (예: Redis)
    backend=&quot;redis://localhost:6379/1&quot;,       # 결과 저장용 (옵션)
)

# celery 환경의 테스크임을 표현하는 데코레이터
@app.task
def resolve_domain(domain: str): 
	# dnspython으로 dns 조회 로직...
    # redis 저장... 
    return {&quot;domain&quot;: domain, &quot;ip&quot;: &quot;1.2.3.4&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP API에서 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763276853851&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI
from app import resolve_domain

app = FastAPI()

@app.post(&quot;/lookup&quot;)
def lookup(domain: str):
	# 비동기로 테스크 큐에 넣기
    task = resolve_domain.delay(domain)
    return {&quot;task_id&quot;: task.id}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;`/lookup`&lt;/span&gt; 호출은 금방 응답&lt;/b&gt;하고, 실제 DNS 조회는 Celery 워커가 &lt;b&gt;백그라운드에서 처리&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 Celery 개념들&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️ Task&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 비동기로 처리하려는 작업 그자체(메소드)다. 함수 단위로 정의되는 작업들을 Celery의 라이프사이클로 싣기 위해서 데코레이터 표현으로 정의할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1763178856677&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from celery import Celery

app = Celery(__name__)

# celery 데코레이터 표현
@app.task
def simple_task():
	return hello&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️ Task Signature&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;`.apply()`&lt;/span&gt;, &lt;span style=&quot;color: #ef5369;&quot;&gt;`.delay()`&lt;/span&gt; 등의 표현으로 메시지 브로커에 작업을 보내는 코드를 작성하게 되는데, 이러한 코드는 내부적으로 다음과 같은 처리 과정을 거친다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;`add.delay(1, 2)`&lt;/span&gt; 호출&lt;/li&gt;
&lt;li&gt;내부에서 &lt;span style=&quot;color: #ef5369;&quot;&gt;`add.s(1, 2)`&lt;/span&gt;같은 Signature 객체 만듦(또는 재사용)&lt;/li&gt;
&lt;li&gt;그 시그니처를 기반으로 메시지를 직렬화(보통 JSON)해서&amp;nbsp;&lt;/li&gt;
&lt;li&gt;브로커에 발행&lt;/li&gt;
&lt;li&gt;워커가 그 메시지를 가져와서 역직렬화 + 해당 함수 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; &lt;b&gt;Celery Signature 프로토콜&lt;/b&gt;에 맞게 작업 요청 메시지를 작성해서 브로커로 보내면, &lt;b&gt;이 언어가 무엇이든 Celery 워커가 받아서&lt;/b&gt; 작업을 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️ Kombu&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Celery는 &lt;b&gt;이벤트 메시지 포맷, 워커 로직에만 집중&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 브로커와의 통신은 &lt;b&gt;kombu의 trasport 시스템&lt;/b&gt;이 맡아서, 다양한 브로커를 &lt;u&gt;&lt;b&gt;URL만 바꿔 끼우면 되는 수준으로 추상화&lt;/b&gt;&lt;/u&gt; 해준다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://github.com/celery/kombu?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;kombu: &lt;/a&gt;&lt;/b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;여러 브로커(RabbitMQ, Redis, SQS...)를 같은 방식으로 다룰 수 있게 해주는 플러그블 메시징 레이어&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1763279074184&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - celery/kombu: Messaging library for Python.&quot; data-og-description=&quot;Messaging library for Python. Contribute to celery/kombu development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/celery/kombu?utm_source=chatgpt.com&quot; data-og-url=&quot;https://github.com/celery/kombu&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/boPLmk/hyZNQwa7Zo/vBZ7iLgy34WAw63kdkThM1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/1cdWS/hyZNRolcVE/gqmkK20enVsNUN1CLkGdJK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/celery/kombu?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/celery/kombu?utm_source=chatgpt.com&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/boPLmk/hyZNQwa7Zo/vBZ7iLgy34WAw63kdkThM1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/1cdWS/hyZNRolcVE/gqmkK20enVsNUN1CLkGdJK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - celery/kombu: Messaging library for Python.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Messaging library for Python. Contribute to celery/kombu development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️ 작업 컨트롤&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Rate Limiting:&lt;/b&gt; 워커가 특정 테스크를 수행하는 것에 대한 속도를 조절할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Schedular[ETA 방식, Countdown 방식]:&lt;/b&gt; 원하는 시간에 작업이 시작될 수 있도록 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Retry:&lt;/b&gt; 작업이 실패하면 적절한 Backoff 메커니즘을 가지고 재시도를 수행한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Expiration:&lt;/b&gt; 오래 쌓여있던 작업들은 처리하지 않고 만료시킨다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Task Routing:&lt;/b&gt; 작업들을 격리하거나 우선순위를 위한 큐를 정의하고, 작업들을 라우팅한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Canvas:&amp;nbsp;&lt;/b&gt;Backend를 활용해 작업 결과들을 파이프라이닝해서 처리할 수 있는 메커니즘을 제공해 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. DI Container와 비동기 테스크 큐&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 필자는 회사에서 클라우드 자원 관리 서비스에 기여하고 있는데, 비즈니스 특성 상 클라우드 자원 관리 작업이 복잡하고 오래 걸리는 경우가 많다. 그래서 클라우드 리소스들을 프로비저닝하는 작업은 Celery로 관리되는데... 이 예시로 해당 주제를 풀어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 웹 애플리케이션과 워커의 프로세스&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1612&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2mwGN/dJMcabigRT1/PvwGBKu2uYdrBMCa8JGMa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2mwGN/dJMcabigRT1/PvwGBKu2uYdrBMCa8JGMa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2mwGN/dJMcabigRT1/PvwGBKu2uYdrBMCa8JGMa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2mwGN%2FdJMcabigRT1%2FPvwGBKu2uYdrBMCa8JGMa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;276&quot; data-origin-width=&quot;1612&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI App과 Celery Worker가 처리하는 것은 완전 다른 별개의 프로세스다. &lt;u&gt;Worker 프로세스는 FastAPI 프로세스의 메모리에 접근할 수 없으므로, 각자 초기화해야 한다.&lt;/u&gt;&amp;nbsp;하지만 프로젝트 전체를 관통하는&amp;nbsp;&lt;b&gt;CoreDIContainer&lt;/b&gt; 클래스와 &lt;b&gt;같은 설정을 사용해야 하므로&lt;/b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;&amp;nbsp;코드 레벨에서 통일성을 보장&lt;/b&gt;&lt;/span&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2&amp;nbsp; 웹 App과 워커가 같은 설정을 사용해야 하는 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 사용자 요청을 받아서 &lt;b&gt;&quot;무엇을 할지&quot;&lt;/b&gt; 기록하고, Celery Worker는 실제로 클라우드에서 &lt;b&gt;&quot;그 작업을 수행&quot;&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 사용자가 &quot;alpha 환경에 S3 버킷을 만들어주세요&quot;라고 요청하면, FastAPI는 이 요청을 DB에 기록한다. 그 후 Celery Worker가 이 기록을 보고 AWS에 실제 버킷을 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MIkLf/dJMcacIfxS7/2SyXHio7kFZnsKKK11S240/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MIkLf/dJMcacIfxS7/2SyXHio7kFZnsKKK11S240/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MIkLf/dJMcacIfxS7/2SyXHio7kFZnsKKK11S240/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMIkLf%2FdJMcacIfxS7%2F2SyXHio7kFZnsKKK11S240%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;228&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;690&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이때, 애플리케이션과 Celery Worker의 설정이 다르면 DB에는 Alpha 환경에 버킷 생성이라고 되어있지만, 실제로는 Prod 환경에 버킷이 만들어진다.&lt;b&gt; 시스템이 알고있는 정보와 클라우드 상태가 달라지는 것이다. 비용 청구도 잘못되고, 보안 문제도 발생할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 FastAPI와 Celery Worker가 다른 프로세스에서 실행되더라도, &lt;u&gt;반드시 같은 Settings를 기반으로 DIContainer를 초기화&lt;/u&gt;해야 한다. &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;같은 AWS 계정에 접근하고, 같은 DB를 사용하며, 같은 환경 정책을 적용해야 전체 시스템이 일관되게 동작한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1) Broker Mode일 때의 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;DI Container 주입:&lt;/span&gt; 초기화 클래스 정의&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트가 실행될 때,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;가장 먼저&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;공용 커스텀 DI Container를 초기화&lt;/b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;하고, 이 객체가&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Celery App 생성에 사용되도록&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;전달한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763294252333&quot; class=&quot;pf&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;@asynccontextmanager
async def lifespan(app: FastAPI):
    core = KrpCore(app.state.settings)                        # 1. DI Container 초기화
    celery = create_celery_app(app.state.settings, core=core) # 2. Celery에 core 전달
    core.load_services(celery)                      # 3. 서비스들 초기화
    app.state.core = core                           # 4. FastAPI state에 저장
    yield&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 구조에 따라서 Celery로 처리되는 프로비저닝 작업들은 AWS Client Map이나 DB 세션 메이커 등 필요한 의존성 항목들이 기대한 대로 준비된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763295098286&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class LoadKrpCoreStep(bootsteps.StartStopStep):
    &quot;&quot;&quot;Celery Worker가 시작될 때 공용 DI Container를 초기화 한다.

    - 공용 컨테이너 초기화,
    - celery app state에 KrpCore(공용 컨테이너) 셋업
    - Fast API 구동과 동일한 접근성을 보장하기 위함.
      - fastapi.state.aws
    &quot;&quot;&quot;

    requires = {&quot;celery.worker.components:Pool&quot;}

    def create(self, parent, **kwargs):
        logger.info(&quot;Prepare KrpCore&quot;)
        app.state.core = KrpCore(settings)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;2) Eager Mode에서의 DI Container 주입: 초기화&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Eager 모드에서는 Worker 프로세스가 없으므로, Worker에서처럼 공용 컨테이너 초기화 로직이 실행되지 않는다. 따라서 FastAPI가 전달한 core를 통해 의존성을 처리하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트가 실행될 때, &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;가장 먼저&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;공용 커스텀 DI Container를 초기화&lt;/b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;하고, 이 객체가&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Celery App 생성에 사용되도록&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;전달한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763292819369&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@asynccontextmanager
async def lifespan(app: FastAPI):
    core = KrpCore(app.state.settings)                        # 1. DI Container 초기화
    celery = create_celery_app(app.state.settings, core=core) # 2. Celery에 core 전달
    core.load_services(celery)                      # 3. 서비스들 초기화
    app.state.core = core                           # 4. FastAPI state에 저장
    yield&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Celery App을 초기화할 때, 전달받은 공용 DI Container 객체를 사용한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;Worker 프로세스들이 FastAPI App과 동일한 접근성을 보장하기 위함이다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763292913749&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def create_app(settings: KrpSettings, core: KrpCore | None = None):
    app = Celery()
    app.state = State()
    app.state.core = core  # FastAPI가 준 core를 celery.state에 저장!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 비동기 큐와 인프라 리소스 프로비저닝 &amp;amp; 비동기 큐 활용 및 프로젝트 관리법을 알아보았다..! 하나하나 따라가느라 시간도 많이 오래걸리고 어려웠지만 앞으로 작업에 많은 도움이 될 학습이었다.&lt;/p&gt;</description>
      <category>Dev/Backend</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/319</guid>
      <comments>https://cobinding.tistory.com/319#entry319comment</comments>
      <pubDate>Sun, 16 Nov 2025 21:17:05 +0900</pubDate>
    </item>
    <item>
      <title>[BackEnd] 싱글톤 패턴 &amp;amp; DI Container 직접 만들어서 사용하기</title>
      <link>https://cobinding.tistory.com/317</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #353638;&quot;&gt;IoC, DI, DIP 알아보기&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cobinding.tistory.com/316&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #353638;&quot;&gt;DI Container 구현하고 Framework 이해하기&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #353638;&quot;&gt;^ 위 글에 이어 DI Container를 직접 구현해서 여러 객체 간의 의존성을 관리하는 것에 대해 더 깊이 이해해보고자 한다. 요즘 가장 많이 쓰는 프레임워크가 FastAPI이기에, 글도 이를 중점적으로 작성해 나갈 것이다.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #353638;&quot;&gt;1. Framework가 여러 의존성을 관리하는 방법 : Depends&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI의 의존성 주입은 &lt;b&gt;함수 시그니처를 통한 선언적 의존성 관리와 &lt;/b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Depends()&lt;/b&gt;&lt;/span&gt;가 핵심이다. &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Depends()&lt;/b&gt;&lt;/span&gt;는 한마디로 쉽게 &lt;i&gt;&lt;b&gt;&quot;이 함수를 실행해서 결과를 여기에 넣어줘&quot;&lt;/b&gt;&lt;/i&gt;라고 프레임워크에게 알려주는 도구다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이 &lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;b&gt;Dependency Injection의 핵심 기능인&lt;/b&gt;&lt;/span&gt;&lt;b&gt; &lt;/b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Depends()&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;를 사용하지 않고 의존 관계를 구현했을 때의 현상은 &lt;/span&gt;&lt;a href=&quot;https://cobinding.tistory.com/316&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이 글&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;에 작성이 되어있으니 참고&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 Depends()의 동작으로 이해해보는 의존성 관리&lt;/h3&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from fastapi import Depends, FastAPI

# 의존성 함수
def get_database():
	db = Database()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;	yield db
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;finally:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;	db.close()

# API Endpoint에서 사용
@app.get(&quot;/items/&quot;)
def get_items(db = Depends(get_database)):
	return db.query(&quot;SELECT * FROM items&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;위 코드를 실행하면 다음 과정이 실행된다.&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;API 실행 전에 FastAPI가 Depends()를 스캔한다.&lt;/li&gt;
&lt;li&gt;get_database 함수를 의존성으로 등록한다.&lt;/li&gt;
&lt;li&gt;내부적으로 get_database 함수를 호출하고 Database() 객체를 반환해서 엔드포인트에 주입한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;# FastAPI가 자동으로 처리:
&amp;nbsp;&amp;nbsp;get_items(db=db)&amp;nbsp;&amp;nbsp;# yield된 db 객체를 파라미터로 전달&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;마지막으로 API 응답 생성 후, 프레임워크가 db.close() 등의 정리 작업을 실행하고, 200 OK와 같은 응답을 반환한다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 Depends()만 사용할 때의 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 의존성 주입은 &lt;b&gt;대규모 애플리케이션에서 몇 가지 한계&lt;/b&gt;가 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️ 무거운 객체의 반복 생성&lt;/h4&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def get_aws_client(settings = Depends(get_settings)):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# boto3 클라이언트는 생성
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return boto3.client('s3', region_name=settings.aws_region)

@app.get(&quot;/buckets&quot;)
def list_buckets(aws = Depends(get_aws_client)):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return aws.list_buckets()

@app.post(&quot;/buckets&quot;)
def create_bucket(aws = Depends(get_aws_client)):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return aws.create_bucket(...)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;get_aws_client&lt;span style=&quot;color: #000000;&quot;&gt; 메소드의 활용, 생명주기 관리는 Depends를 통해 프레임워크가 도맡아서 할 수 있지만&amp;nbsp;&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;요청마다 관련 클라이언트 새로 생성(boto3 클라이언트는 생성 비용이 큼)&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;연결 풀을 효과적으로 사용할 수 없음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;초기화 비용이 큰 객체일 수록 성능 저하&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;* 참고로 Depends()의 캐싱은 &quot;같은 요청 내&quot;에서만 동작한다. 다음 요청이 오면 다시 생성된다.&lt;br /&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/reference/dependencies/?utm_source=chatgpt.com#fastapi.Depends&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;FastAPI의 use_cache(&lt;/span&gt;&lt;/a&gt;request-scoped cache): &lt;u&gt;같은 요청 안에서&lt;/u&gt; dependenc가 여러 번 선언되면, 캐싱된 내용을 사용한다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;⚫️&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;API Parameter 복잡도&lt;/span&gt;&lt;/h4&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;@app.post(&quot;/resources&quot;)
def create_resource(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;aws_client = Depends(get_aws_client),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;db = Depends(get_db),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;redis = Depends(get_redis),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;slack = Depends(get_slack),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;prometheus = Depends(get_prometheus),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;katalog = Depends(get_katalog),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# ... 
):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pass&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가독성 저하&lt;/li&gt;
&lt;li&gt;테스트 시 모든 파라미터를 Mock으로 준비해야 함&lt;/li&gt;
&lt;li&gt;새로운 의존성 추가 시 모든 엔드포인트 수정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. DI Container의 등장 배경 : 싱글톤 패턴&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 싱글톤 패턴 등장 배경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Depends()와 같은 프레임워크가 제공하는 DI를 사용하면 많은 이점이 있지만, 애플리케이션 규모가 커질 수록 불편함 또한 생긴다. 클라이언트가 요청을 보낼 때마다 새로운 객체를 만들어서 반환한다. &lt;b&gt;가령 고객의 트래픽이 초당 100만큼 발생하면 초당 100개의 객체가 생성 - 소멸 과정을 겪는다.&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️ 무거운 초기화를 매 요청마다 수행하면&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;응답시간 증가&lt;/li&gt;
&lt;li&gt;리소스 낭비: CPU, 메모리, 네트워크 연결 반복 생성&lt;/li&gt;
&lt;li&gt;외부 서비스 부하: AWS API, Redis 서버에 불필요한 연결 요청&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️ 문제 사례&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS Client Boto3 세션 생성, credential 로딩, region별 endpoint 연결&lt;/li&gt;
&lt;li&gt;Redis TCP 연결 생성, 인증, connection pool 초기화&lt;/li&gt;
&lt;li&gt;DB 커넥션 풀 생성, 스키마 메타데이터 로딩&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 싱글톤 패턴으로 해결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이를 해결하기 위해서 객체를 하나만 만들어놓고, 클라이언트가 동일한 객체를 요청하면 이 객체를 반환해서, 모든 클라이언트(요청)가 하나의 객체를 공유하게끔한다. 이것이 바로 싱글톤 패턴이다.&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;# 1. 앱 시작 시 한 번만 생성 (lifespan)
&amp;nbsp;&amp;nbsp;@asynccontextmanager
&amp;nbsp;&amp;nbsp;async def lifespan(app: FastAPI):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;core = KrpCore(settings)&amp;nbsp;&amp;nbsp;# 딱 한 번만 실행
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;app.state.core = core&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 앱 전역에 저장
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;yield

&amp;nbsp;&amp;nbsp;# 2. Depends로 접근 (매번 실행되지만 같은 객체 반환)
&amp;nbsp;&amp;nbsp;def with_krp_core(request: Request) -&amp;gt; KrpCore:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return request.app.state.core&amp;nbsp;&amp;nbsp;# 저장된 싱글톤 반환

&amp;nbsp;&amp;nbsp;# 3. 엔드포인트에서 사용
&amp;nbsp;&amp;nbsp;@app.get(&quot;/s3&quot;)
&amp;nbsp;&amp;nbsp;def list_s3(core: KrpCore = Depends(with_krp_core)):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;주로 클라우드 서비스를 셀프 서비스하는 프로젝트를 운영/개발하는 우리 팀에서는 AWS Client, Redis, GCP Client와 같이, 여러 객체에서 반복 사용되며 무거운 초기화를 가진 클라이언트들에 대해 앱 시작 시 초기화한다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Framework가 제공하는 DI Cotainer&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 FastAPI&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;FastAPI는 DI Container&lt;/b&gt; 객체를 노출하진 않지만, Depends + 내부 dependency resolver가 사실상 DI Container의 역할을 한다. 이는 &lt;a title=&quot;FastAPI Dependency Injections&quot; href=&quot;https://fastapi.tiangolo.com/tutorial/dependencies/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;FastAPI Dependency Injection 문서&lt;/span&gt;&lt;/a&gt;에 잘 나와있는데, 핵심을 정리하면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;별도의 컨테이너 객체를 직접 다루지 않더라도 대부분의 웹 애플리케이션에서 충분히 쓸 수 있는 의존성 주입 기능을 제공한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;함수 시그니처에 의존성을 선언하면&lt;/li&gt;
&lt;li&gt;프레임워크가 의존성 그래프를 만들고&lt;/li&gt;
&lt;li&gt;요청 단위로 생성/캐싱/정리까지 자동으로 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 Spring IoC Container &amp;amp; Bean&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spring DI Cotainer&lt;/b&gt;는 FastAPI와 다르게 Spring IoC Container가 직접 Bean 객체를 관리하며 IoC를 제공한다. 이또한 &lt;a href=&quot;https://docs.spring.io/spring-framework/reference/core/beans/introduction.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Spring IoC &amp;amp; Bean 문서&lt;/span&gt;&lt;/a&gt;에 잘 나와있다!&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;`org.springframework.context.ApplicationContext`&lt;/span&gt; 인터페이스가 &lt;b&gt;Spring IoC 컨테이너&lt;/b&gt;를 대표하며, 컨테이너는 애플리케이션에서 사용할 &lt;b&gt;bean들을 인스턴스화, 설정, 조립&lt;/b&gt;하는 책임을 가진다. (&lt;a href=&quot;https://docs.spring.io/spring-framework/reference/core/beans/basics.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;문서 바로가기&lt;/span&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;IoC 컨테이너는 &lt;b&gt;XML, 애너테이션, Java 코드&lt;/b&gt; 같은 &lt;b&gt;configuration metadata&lt;/b&gt;를 읽어서 &amp;ldquo;어떤 객체를 생성&amp;middot;구성&amp;middot;연결할지&amp;rdquo;에 대한 지시를 받고, 그에 따라 빈과 빈 사이의 의존성을 설정한다. (&lt;a href=&quot;https://docs.spring.io/spring-framework/reference/core/beans/basics.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;문서 바로가기&lt;/span&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cY47pM/dJMcaf5ZPmg/TTQwojUlagBKRe0mNKxeL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cY47pM/dJMcaf5ZPmg/TTQwojUlagBKRe0mNKxeL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cY47pM/dJMcaf5ZPmg/TTQwojUlagBKRe0mNKxeL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcY47pM%2FdJMcaf5ZPmg%2FTTQwojUlagBKRe0mNKxeL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;294&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Configuration
class AppConfig {

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Bean
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AwsClient awsClient(AppSettings settings) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return new AwsClient(settings.getRegion());
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Bean
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;UserService userService(AwsClient awsClient) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return new UserService(awsClient);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}

public class Main {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public static void main(String[] args) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ApplicationContext ctx =
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new AnnotationConfigApplicationContext(AppConfig.class);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;UserService userService = ctx.getBean(UserService.class);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;userService.process();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;-&amp;gt; 이건 커스텀 컨테이너를 설정했다기 보다 Spring이 제공하는 DI 컨테이너(ApplicationContext)에 빈 설정을 등록해서 쓰는 것&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Custom DI Container&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 살펴본 것처럼, 무거운 클라이언트(Boto3, Redis, DB 커넥션 풀 등)를 요청마다 새로 만드는 문제는 FastAPI의 `Depends`만으로 해결하기 어렵다. 그래서&lt;b&gt; lifespan과 `app.state`를 이용해 애플리케이션 시작 시점에 한 번만 생성하는&amp;nbsp;싱글톤 패턴&lt;/b&gt;을 도입해 성능 문제는 어느 정도 해결할 수 있었다.&lt;br /&gt;&lt;br /&gt;하지만 애플리케이션 규모가 더 커지면&lt;b&gt;, 단순히 &amp;ldquo;전역 싱글톤 몇 개&amp;rdquo;만 두는 방식으로는 다음과&amp;nbsp;같은&amp;nbsp;설계상의&amp;nbsp;문제가&amp;nbsp;남는다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 의존성을 직접 주입하면 API 엔드포인트 파라미터 목록이 길어지고, 핵심 도메인 로직이 의존성 목록에 묻힌다.&lt;/li&gt;
&lt;li&gt;서비스 간 의존 관계가 엔드포인트/의존성 함수 곳곳에 흩어져 있어서&lt;b&gt;, 시스템 전체 의존성 그래프 파악이 어렵다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;도메인 서비스가 FastAPI의 &lt;span style=&quot;color: #ef5369;&quot;&gt;`Depends`&lt;/span&gt;에 직접 의존하게 되어, &lt;b&gt;HTTP 이외의 진입점(배치, Celery, CLI, 테스트 등)에서 재사용하기가 불편&lt;/b&gt;하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 &lt;span style=&quot;color: #ef5369;&quot;&gt;프레임워크 DI와 전역 싱글톤&lt;/span&gt;만으로는 부족한 의존성 관리를 &lt;span style=&quot;color: #ef5369;&quot;&gt;커스텀화해서 해결!&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 설계상의 문제를 해결하기 위해, 우리 팀에서는 애플리케이션 전용 DI 컨테이너인 &lt;span style=&quot;color: #ef5369;&quot;&gt;`KrpCore`&lt;/span&gt;를 두고 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️ Custom DI Container 정의하기&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플리케이션 공통 클라이언트 초기화&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;class KrpCore:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;&quot;&quot;KRP 애플리케이션의 의존성 컨테이너&quot;&quot;&quot;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def __init__(self, settings: KrpSettings):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self._settings = settings
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self._aws_client_map = load_aws_client_map(settings)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self._gcp_client_map = load_gcp_client_map(settings)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self._prometheus_client = PrometheusClient(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;api_url=settings.prometheus_api_url,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;api_token=settings.prometheus_api_token,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
 
@property
	def aws_client_map(self) -&amp;gt; AwsEnvironmentRegionClientMap: ...
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
@property
	def sessionmaker(self): ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️ 공용 컨테이너는 앱 시작 시 한 번만 만들고, 이후 Depends로 사용&lt;/h4&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;app = FastAPI(lifespan=lifespan)

@asynccontextmanager
async def lifespan(app: FastAPI):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;core = KrpCore(settings)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # 앱 시작 시 한 번만 생성
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;core.load_services(celery)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # 서비스들도 한 번만 초기화
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;app.state.core = core&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 전역 싱글톤처럼 보관
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;yield&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 앱 종료 시 정리 로직 실행 가능

def get_core(request: Request) -&amp;gt; KrpCore:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return request.app.state.core&amp;nbsp;&amp;nbsp;# 항상 같은 core 인스턴스 반환

@app.post(&quot;/s3&quot;)
def create_s3(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;payload: s3Create,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;core: KrpCore = Depends(get_core),
):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;service = core.services[&quot;s3&quot;]
	...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 프레임워크 제공 DI Container 사용 VS 커스텀 장단점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 길지만 중요한 내용..!&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 409px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;관점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;프레임워크 제공 DI&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;커스텀 DI Container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 56px;&quot;&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;&lt;b&gt;학습 비용 / 생산성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;이미 프레임워크와 깊게 통합되어 있어서 팀원이 문서만 보고 바로 쓰기 쉽다.&lt;/td&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;컨테이너 개념, 등록/해결 규칙, 라이프사이클 정책 등 팀이 새로 익혀야 할 추상화가 하나 더 생긴다. 초기 설계와 가이드 정리가 필요하다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 56px;&quot;&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;&lt;b&gt;성능 / 라이프사이클&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;기본이 요청 스코프(request-scoped)라 요청마다 새 인스턴스를 만들기 쉽고, 싱글톤/글로벌 스코프는 별도 패턴(lifespan, app.state 등)을 직접 도입해야 한다.&lt;/td&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;Boto3, Redis, DB 커넥션 풀 등 무거운 객체를 컨테이너 내부에서 명시적으로 singleton처럼 관리해 여러 요청에서 재사용하기 쉽다. 라이프사이클 정책을 컨테이너 기준으로 설계 가능.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 56px;&quot;&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;&lt;b&gt;엔드포인트 시그니처 / 가독성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;Depends를 많이 쓰면 엔드포인트 파라미터가 길어지고, 도메인 로직보다 &amp;ldquo;의존성 목록&amp;rdquo;이 먼저 보일 수 있다.&lt;/td&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;엔드포인트는 core: KrpCore나 UserService 같은 &amp;ldquo;굵은 서비스 단위&amp;rdquo;만 받도록 정리할 수 있어서, 시그니처가 더 단순해지고 도메인에 집중하기 쉽다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 56px;&quot;&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;&lt;b&gt;의존 관계 가시성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;의존성이 각 엔드포인트/의존 함수에 흩어져 있어서, 전체 시스템의 의존성 그래프를 한눈에 보기 어렵다.&lt;/td&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;컨테이너 초기화/등록 코드(KrpCore, load_services 등)에 의존 관계가 모이므로, &amp;ldquo;어떤 서비스가 무엇에 의존하는지&amp;rdquo;를 한 곳에서 파악하기 좋다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 56px;&quot;&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;&lt;b&gt;테스트 / 의존성 교체&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;FastAPI의 dependency_overrides 등으로 일부 의존성을 쉽게 바꿀 수 있지만, 깊고 복잡한 서비스 그래프를 통째로 갈아끼우기에는 다소 불편할 수 있다.&lt;/td&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;컨테이너에 등록된 구현만 바꾸면(Fake 서비스, In-memory Repo 등) 테스트 환경 전체 의존성을 한 번에 바꿀 수 있다. 특정 토큰 이름(예: &quot;s3&quot;, &quot;redis&quot;)만 기준으로 교체 가능.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 56px;&quot;&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;&lt;b&gt;프레임워크 독립성 / 재사용성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;서비스/로직이 프레임워크의 DI 메커니즘(Depends, 애너테이션 등)에 더 밀접하게 묶이기 쉽다. HTTP 이외 진입점에서 재사용하려면 별도 어댑터가 필요할 때가 많다.&lt;/td&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;컨테이너를 중심으로 의존성을 주입하면, 서비스는 &amp;ldquo;컨테이너에서 주어지는 것&amp;rdquo;만 의존한다. 같은 서비스 코드를 Celery, 배치, CLI, 테스트 등 다양한 환경에서 재사용하기 쉽다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 56px;&quot;&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;&lt;b&gt;복잡도 / 운영 비용&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;&amp;ldquo;프레임워크가 해주는 대로&amp;rdquo; 쓰면 되기 때문에 구조가 단순하고 디버깅 포인트도 비교적 적다. 대신 프로젝트가 커질수록 점진적으로 난잡해질 수 있다.&lt;/td&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;설계가 잘 되면 구조가 정돈되지만, 컨테이너가 또 하나의 인프라 코드가 되어 관리 포인트가 늘어난다. 설계가 어설프면 &amp;ldquo;의존성이 어디서 만들어지는지 더 헷갈리는&amp;rdquo; 역효과도 가능.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Dev/Backend</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/317</guid>
      <comments>https://cobinding.tistory.com/317#entry317comment</comments>
      <pubDate>Sun, 9 Nov 2025 17:12:38 +0900</pubDate>
    </item>
    <item>
      <title>[BackEnd] Framework 이해하기 - DI Container로 여러 의존성 관리</title>
      <link>https://cobinding.tistory.com/316</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;이전 글&quot; href=&quot;https://cobinding.tistory.com/315&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이전 글&lt;/a&gt;에서 IoC, DI, DIP를 알아보았다. 이제 여기서 확장해서 DI Container와 생명 주기 관리, Web Framework의 원리를 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Framework&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프레임워크 본질 중 하나가 IoC다. 일반적으로는 개발자가 직접 코드 흐름을 제어하지만, 프레임워크는 이 흐름을 뒤집는다. (제어의 역전)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프레임워크가 애플리케이션 코드를 호출 및 흐름을 제어하고, 개발자는 의존 관계만 정의하는 방식이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;framework라고 하면 DI 패턴을 통해서 여러 자원을 관리할 수 있는데, 이게 바로 IoC를 실현하는 구체적인 방법이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FastAPI: 함수 인자에 Depends()를 명시한다. FastAPI가 호출 시점에 자동으로 의존성을 주입한다.&lt;/li&gt;
&lt;li&gt;Spring: @Component, @Service, @Repository Spring IoC 컨테이너가 의존를 생성하고 주입한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. DI Container로 의존성 관리하기&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 DI Container가 없는 상황 (문제 코드)&lt;/h3&gt;
&lt;pre id=&quot;code_1762004673414&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# DI Container가 없는 상황
from abc import ABC, abstractmethod

class Teacher:
    def __init__(self, name: str):
        self.name = name
    
    def teach(self):
        return f&quot;{self.name} 선생님이 수업합니다.&quot;

class Student:
    def __init__(self, name: str):
        self.name = name
    
    def study(self):
        return f&quot;{self.name} 학생이 공부 중이에요.&quot;

class ClassRoom:
    def __init__(self, teacher: Teacher, student: Student):
        self.teacher = teacher
        self.student = student

    def conduct_session(self):
        print(&quot;== 수업 시작 ==&quot;)
        print(self.teacher.teach())
        print(self.student.study())
        print(&quot;== 수업 끝 ==&quot;)

if __name__ == &quot;__main__&quot;:
    # file: classroom.py
    teacher1 = Teacher(&quot;김선생&quot;)
    student1 = Student(&quot;cobinding&quot;)
    classroom1 = ClassRoom(teacher1, student1)
    classroom1.conduct_session()

    # file: exam.py
    teacher2 = Teacher(&quot;이선생&quot;)
    student2 = Student(&quot;cobinding&quot;) # 다른 객체를 또 생성하게 됨
    classroom2 = ClassRoom(teacher2, student2)
    classroom2.conduct_session()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 실제 웹 프레임 워크 상황에 빗대면...&lt;/h3&gt;
&lt;pre id=&quot;code_1762004944912&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 실제 웹 프레임 워크 상황이 되면...
@app.get(f&quot;/class/{student_id}&quot;)
def start_class(student_id: int):
    # 각 api 경로에서 아래와 같은 객체들을 매번 생성해야 함.
    db = Database(&quot;localhost:5432&quot;)
    cache = Redis(&quot;localhost:6379&quot;)
    logger = Logger(&quot;app.log&quot;)

    teacher = Teacher(&quot;김선생&quot;)
    student = db.get_student(student_id)
    classroom = ClassRoom(teacher, student)
    result = classroom.conduct_session()

    db.close()

    return result&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 API에서 db, redis, logger 등 필요한 코드를 반복하게 되어, 어떤 비즈니스 로직이 담겨있는지 보다 무엇을 만들어야 하는지에 대한 코드가 대부분이 된다.&lt;/li&gt;
&lt;li&gt;객체의 생성/소멸&lt;span style=&quot;color: #ef5369;&quot;&gt; 라이프사이클을 개발자가 직접 제어&lt;/span&gt;하게 된다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;테스트 시에도 진짜 DB/Redis 인스턴스가 계속 만들어진다.&lt;/li&gt;
&lt;li&gt;ClassRoom은 &lt;span style=&quot;color: #ef5369;&quot;&gt;구체 클래스(Teacher, Student)에 직접 의존하&lt;/span&gt;게 된다. (DIP 실패)&lt;/li&gt;
&lt;li&gt;의존성이 늘수록 생성 코드가 폭발..&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 프레임워크가 없다면 이러한 어려움을 겪게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 문제를 한번 DI Container를 생성하고 웹 프레임워크의 핵심 메커니즘을 이해해보자!&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. DIContainer로 여러 의존성 관리&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 DIContainer 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성 주입 컨테이너를 만들어서 위 문제를 해결해보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;앞선 코드에는 다음과 같은 한계가 있다.&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Student(&quot;cobinding&quot;)을 여러 번 생성하는 문제&lt;/li&gt;
&lt;li&gt;여러 파일/모듈 간에 동일 객체를 공유하기 어려운 구조&lt;/li&gt;
&lt;li&gt;전역 변수를 사용하지 않고는 의존성을 중앙에서 관리하기 어려움&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 &lt;b&gt;DI(Dependency Injection) Container를 직접 구현&lt;/b&gt;해보며, FastAPI의 Depends()와 같은 프레임워크가 &lt;b&gt;내부적으로 어떻게 의존성을 주입하는지 이해해보자.&lt;/b&gt; 이를 통해 웹 프레임워크가 의존성을 생성/관리/주입하는 원리를 간단한 예제로 살펴본다.&lt;/p&gt;
&lt;pre id=&quot;code_1762132847021&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;&quot;&quot;
DI Container: 의존성 주입 컨테이너

main.py의 문제 해결:
1. Student(&quot;cobinding&quot;)을 여러 번 생성하는 문제
2. 여러 파일/모듈에서 같은 객체 공유 어려움
3. 전역 변수 없이 중앙에서 관리

&quot;&quot;&quot;

class CoreDIContainer:
    &quot;&quot;&quot; 의존성 주입 컨테이너 &quot;&quot;&quot;

    def __init__(self):
        self._services = {} # 싱글톤 객체 저장소

    def register(self, name: str, instance):
        self._services[name] = instance
        print(f&quot; `{name} 등록됨. ID: {id(instance)}&quot;)

    def get(self, name: str):
        &quot;&quot;&quot; 의존성 가져오기 &quot;&quot;&quot;
        if name not in self._services:
            raise ValueError(f&quot;{name}이 등록되어 있지 않습니다.&quot;)
        return self._services[name]
    
    def has(self, name: str) -&amp;gt; bool:
        &quot;&quot;&quot; 의존성 존재 여부 확인 &quot;&quot;&quot;
        return name in self._services                                                       

    def clear(self):
        &quot;&quot;&quot; 모든 의존성 제거 &quot;&quot;&quot;
        self._services.clear()
        print(&quot;모든 의존성 제거 완료&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 DIContainer 사용 예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 실제로 사용하는 구현은 다음과 같다. 이전 코드에서 발생했던 문제를 DI Container를 통해 어떻게 해결할 수 있는지가 담겨있다.&lt;/p&gt;
&lt;pre id=&quot;code_1762133554751&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if __name__ == &quot;__main__&quot;:
    from main import Teacher, Student, ClassRoom

    print(&quot; == DI Container 사용 예시 == &quot;)

    # 1. 컨테이너 생성
    container = CoreDIContainer()

    # 2. 의존성 등록 (한번만)
    container.register(&quot;teacher_kim&quot;, Teacher(&quot;김선생&quot;))
    container.register(&quot;teacher_lee&quot;, Teacher(&quot;이선생&quot;))
    container.register(&quot;student_co&quot;, Student(&quot;cobinding&quot;))

    # 3. file: classroom.py
    print(&quot;[classroom.py] 첫 번째 수업&quot;)
    teacher = container.get(&quot;teacher_kim&quot;)
    student = container.get(&quot;student_co&quot;)
    print(f&quot;student ID: {id(student)}&quot;)

    classroom1 = ClassRoom(teacher, student)
    classroom1.conduct_session()

    # 4. file: exam.py =&amp;gt; 같은 객체 재사용
    print(&quot;[exam.py] 두 번째 시험&quot;)
    teacher = container.get(&quot;teacher_lee&quot;)
    student = container.get(&quot;student_co&quot;) 
    print(f&quot;student ID: {id(student)}&quot;)

    classroom2 = ClassRoom(teacher, student)
    classroom2.conduct_session()

    print()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 컨테이너 생성&lt;/b&gt;&lt;br /&gt;CoreDIContainer()를 한 번만 생성한다. 이 컨테이너는 모든 의존성을 중앙에서 관리하는 저장소 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 의존성 등록&lt;/b&gt;&lt;br /&gt;Teacher, Student 같은 객체를 register() 메서드로 한 번만 등록한다.&lt;br /&gt;이 단계에서 객체는 &amp;ldquo;싱글톤처럼&amp;rdquo; 관리되며, 이후 어디서든 동일한 인스턴스를 꺼내 쓸 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. classroom.py 실행&lt;/b&gt;&lt;br /&gt;container.get(&quot;teacher_kim&quot;)과 container.get(&quot;student_co&quot;)를 호출해 필요한 의존성을 주입받는다.&lt;br /&gt;직접 Teacher()나 Student()를 생성하지 않아도 되고, 객체의 생성 위치가 코드 전역에 흩어지지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. exam.py 실행&lt;/b&gt;&lt;br /&gt;같은 방식으로 container.get()을 호출하면 &lt;b&gt;동일한 객체 인스턴스&lt;/b&gt;가 재사용된다.&lt;br /&gt;출력된 student ID가 동일한 값으로 찍히는 것을 보면,&lt;br /&gt;&amp;ldquo;Student(&quot;cobinding&quot;)이 여러 번 생성되는 문제&amp;rdquo;가 해결된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;알게된 점&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그동안은 웹 프레임워크를 사용할 때 내부 동작보다는 제공되는 편의성에만 의존해왔다. 하지만 이번에 DI Container와 IoC 원리를 직접 구현해보며, 프레임워크가 어떻게 의존성을 관리하고 주입하는지를 구체적으로 이해할 수 있었다. 회사에서 자주 보는 프로젝트에서도 KRPCore라는 중앙 의존성 컨테이너가 있는데, 이제는 왜 그렇게 설계했는지, 어떤 장점을 가지는지 명확히 이해할 수 있게 되었다! FastAPI Depends()의 원리나 내부 구조, 프레임워크의 장단점 같은 걸 더 공부해봐야겠다고 생각들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Dev/Backend</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/316</guid>
      <comments>https://cobinding.tistory.com/316#entry316comment</comments>
      <pubDate>Mon, 3 Nov 2025 10:47:39 +0900</pubDate>
    </item>
    <item>
      <title>[BackEnd] IoC &amp;amp; DIP  &amp;amp; DI</title>
      <link>https://cobinding.tistory.com/315</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Inversion of Control, Dependency Inversion Principle, Dependency Injection&lt;/b&gt;에 대해 학습한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DIP를 위해 &amp;rarr; IoC 개념으로 &amp;rarr; DI를 사용&lt;/b&gt;하는 큰 흐름을 가지고 각 내용을 살펴본다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. IoC (Inversion of Control) : 제어(관리)의 역전&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램의 제어 흐름을 개발자가 아닌 프레임워크/컨테이너가 담당하는 설계 원칙이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 제어(관리)의 두 가지 의미&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체 생명 주기 제어(생성, 소멸)&lt;/li&gt;
&lt;li&gt;프로그램 실행 흐름 제어(언제, 어떤 순서로)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2 전통적 방식 vs IoC&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IoC 적용 전&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1760923988975&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Teacher:
	def teach(self): 
		student = Student() # 내가 직접 생성
		result = student.study() # 내가 직접 호출
		return result

# main에서 내가 모든 걸 제어함
def main():
	teacher = Teacher() # 내가 생성
	result = teacher.teach() # 내가 호출&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IoC 적용 후&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1760924022475&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Learner(ABC):
	@abstractmethod
	def study(self) -&amp;gt; str:
		pass

class Student(Learner):
	def study(self) -&amp;gt; str:
		return &quot;학생이 공부 중이에요.&quot;
		
class CollegeStudent(Learner):
	def study(self) -&amp;gt; str:
		return &quot;대학생이 공부 중이에요.&quot;
		
class Teacher:
	# DI로 구현하는 것이 가장 일반적이다.
	def __init__(self, learner: Learner):
		self.learner = learner # 외부에서 생성 받아옴 =&amp;gt; 제어권 역전
	
	def teach(self):
		return self.learner.study()

# main, continer의 제어
def main():
    # 외부에서 제어 (IoC, DI)
    student = Student()
    teacher1 = Teacher(student)  # DI

    college = CollegeStudent()
    teacher2 = Teacher(college)  # DI

    print(teacher1.teach())  # 학생이 공부 중이에요.
    print(teacher2.teach())  # 대학생이 공부 중이에요.&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Teacher는 교육 로직에만 집중한다. (SRP 준수)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;SRP(Single Responsibility Principle) : 단일 책임 원칙&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;한 클래스는 오직 하나의 책임(역할)만 가져야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;외부에서 다른 Student를 Teacher에 주입 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. DIP(Dependency Inversion Principle)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DIP는 IoC 철학을 &amp;ldquo;무엇에 의존할 것인가&amp;rdquo;로 구체화한 설계 원칙이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'상위 레벨의 모듈은 하위 레벨의 모듈에 의존해서는 안된다. 둘 모두 추상화에 의존해야한다. 추상화는 세부 사항에 의존해서는 안된다.&amp;rsquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체적인 것이 아니라 추상적인 것에 의존한다. 의존 방향이 &lt;b&gt;상위 &amp;rarr; 추상 &amp;larr; 하위&lt;/b&gt; 로 둘다 추상에 의존한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 DIP 적용 전후 의존 관계&lt;/h3&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;# DIP 적용 전
Teacher --&amp;gt; Student (고수준 모듈이 저수준 모듈(구체 class)에 바로 의존)

# DIP 적용 후
Teacher --&amp;gt; Learner &amp;lt;-- Student
            (추상화)      (하위 구체 클래스)
            ^ 의존 방향 역전
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Teacher : 고수준 모듈 (비즈니스 로직)&lt;/li&gt;
&lt;li&gt;Learner : 추상화 (인터페이스/추상 클래스)&lt;/li&gt;
&lt;li&gt;Student : 저수준 모듈 (구체적 구현) &amp;lt;/aside&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1760924067470&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from abc import ABC, abstractmethod

# 1. 추상화 정의
class Learner(ABC):  # 추상 클래스 (인터페이스)
    @abstractmethod
    def study(self) -&amp;gt; str:
        &quot;&quot;&quot;공부하는 행위&quot;&quot;&quot;
        pass

# 2. 구체 클래스들이 추상화를 구현
class Student(Learner):
    def study(self) -&amp;gt; str:
        return &quot;학생이 공부 중이에요.&quot;

class CollegeStudent(Learner):
    def study(self) -&amp;gt; str:
        return &quot;대학생이 공부 중이에요.&quot;

## 새로운 학습자를 추가하더라도 ... Learner를 상속 받는 구체 클래스만 추가하면 됨

# 3. Teacher는 추상화에 의존 : Learner 인터페이스만 알고, 구체 구현은 몰라도 됨
class Teacher:
    def __init__(self, learner: Learner):  # 추상 클래스에 의존해야함! (DI)
        self.learner = learner

    def teach(self) -&amp;gt; str:
        return self.learner.study()

# 4. 사용 (DI + DIP)
if __name__ == &quot;__main__&quot;:
    # 이제 모든 Learner 구현체 사용 가능!
    student = Student()
    college = CollegeStudent()

    teacher1 = Teacher(student)
    teacher2 = Teacher(college)
    
    print(teacher1.teach())  # 학생이 공부 중이에요.
    print(teacher2.teach())  # 대학생이 공부 중이에요.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 왜 &amp;ldquo;역전(Inversion)&amp;rdquo;인가?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전통적 의존 방향
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Applicatiion Layer &amp;rarr; Business Logic &amp;rarr; DB Layer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;DIP 적용 후
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Application Layer &amp;rarr; Interface &amp;larr; DB Impl&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Dependency Injection&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DI는 런타임 시점의 의존 관계 결정을 코드 외부로 위임함으로써, 컴파일 타임 의존성과 런타임 의존성을 분리하는 설계 기법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;: 컴파일 단계에서는 소스 코드 자체(타입/인터페이스)만 확인하고, 런타임에 실제로 어떤 구체 객체를 사용할지 동적으로 결정할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 객체가 &lt;b&gt;자신의 협력 객체를 직접 생성(new)하지 않고, 외부에서 주입 받음&lt;/b&gt;으로써 결합도는 낮추고 응집도는 높이는 것이다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;# Case 1 : DI 적용 X
class Teacher:
	def teach(self):
		student = Student() # 소스코드에 Student가 명시됨 -&amp;gt; 컴파일 타임 의존성
		return student.study()

# Case 2 : DI 적용 O
class Teacher:
    def __init__(self, student: Student):  # 타입 힌트만 있음
        self.student = student

student = Student()
student = CollegeStudent() # 다른 객체도 가능
teacher = Teacher(student) # 여기서 의존 관계 결정!
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 의존성&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 코드가 다른 코드에 기대고 있는 것. 즉,변경 전파 가능성을 의미한다.&lt;/li&gt;
&lt;li&gt;A가 B에게 의존한다 &amp;rarr; B의 변경이 A에게 전파될 수 있다.
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;class Teacher:
	def teach(self):
		student = Studnet() # 구체 클래스에 직접 의존
		return student.study()
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Teacher는 Student라는 구체 클래스에 소스코드 레벨에서 결합됨&lt;/li&gt;
&lt;li&gt;Teacher가 협력 객체의 생성과 사용이라는 두 가지 책임을 가진다. (SRP 위반)&lt;/li&gt;
&lt;li&gt;새로운 학습자 타입을 추가할 때 Teacher 코드는 수정이 필요하다. (OPC 위반)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;OCP(Open-Closed Principle)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;새로운 기능 추가는 가능하되, 기존 코드 수정은 하지 않아야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;class Student:
    def study(self):
        return &quot;공부 중이에요.&quot;

class Teacher:
    def teach(self):
        student = Student()
        return student.study()
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Teacher 클래스가 Student 클래스의 메소드를 직접 사용하면서 의존하고 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 나중에 Student 코드가 변경되면 Teacher 코드도 변경되어야함. &lt;span style=&quot;color: #ef5369;&quot;&gt;강한 의존&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 강한 의존을 피하기 위한 외부 의존성 주입&lt;/h3&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;class Student:
    def study(self):
        return &quot;공부 중이에요.&quot;

class Teacher:
    def __init__(self, student: Student):
        self.student = student  # 외부에서 주입받음

    def teach(self):
        return self.student.study()

student = Student()
teacher = Teacher(student) # 외부에서 만들어서, 객체 의존성 주입
print(teacher.teach())  # &quot;공부 중이에요.&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Teacher는 클래스 내부에서 직접적으로 Student 객체에 의존하지 않고, 외부를 통해 의존한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; Student 내부 구현이 바뀌어도 상관없음 &amp;rarr; 유지보수, 테스트, 확장이 쉬워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. DI 방식 3가지&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성자 주입&lt;/li&gt;
&lt;li&gt;메서드 주입&lt;/li&gt;
&lt;li&gt;필드 주입&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 생성자 주입 &amp;rarr; 권장. KRP Core.&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체를 만들 때 필요한 것을 받아서, 변동 없이 그것만 사용한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체를 __init__으로 받아서 붙여줌 &amp;rarr; 의존성이 명확하다. __init__ 보면 바로 알 수 있음&lt;/li&gt;
&lt;li&gt;한번 주입되면 변경 불가능 &amp;rarr; 불변성 보장&lt;/li&gt;
&lt;li&gt;클래스 전체에서 일관된 의존성 사용 &amp;harr; 변경하려면 새 객체 생성&lt;/li&gt;
&lt;li&gt;매번 파라미터 전달 안해도됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1760924235715&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Student: 
	# 1. 생성자 정의
	def __init__(self, name=&quot;sb&quot;):
		self.name = name 
		
	# 2. 출력 형식(문자열) 정의
	def __str__(self):
		return self.name
		
	def study(self):
		return &quot;학생이 공부 중이에요.&quot;
		
class Teacher:

	# 주입받을 의존성을 private 필드로 선언
	_student: Student  
	
	# 외부에서 객체를 파라미터로 받아서 생성자 주입 
	def __init__(self, student: Student):
		self._student = student 
	
	def teach(self):
		return self._student.study()
		
	# 주입받은 Student 객체를 Teacher 클래스의 모든 메서드에서 자유롭게 활용 가능하다.
	def check_homework(self):
		return f&quot;{self._student.study()} 숙제를 확인합니다.&quot;
		
	def give_feedback(self):
		return f&quot;{self._student} 에 대한 피드백&quot;

if __name__ == &quot;__main__&quot;: 
	student = Student()
	teacher = Teacher(student) # 외부에서 student를 생성하고 주입
	print(teacher.teach()) # 학생이 공부중이에요.
	print(&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 Method Injection &amp;rarr; 일반적으로 사용하지 않는다.&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성자가 아닌 일반 메서드의 파라미터로 의존성을 주입 받는다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메서드마다 다른 의존성 주입 가능하다.&lt;/li&gt;
&lt;li&gt;필요한 메서드만 의존받을 수 있다.&lt;/li&gt;
&lt;li&gt;매번 파라미터로 전달해야 하는 번거로움과 명확하지 않은 의존성(함수마다 달라지기 때문)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1760924261348&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Student:
	def __init__(self, name=&quot;sb&quot;):
		self.name = name

	def __str__(self):
		return self.name
	
	def study(self):
		return &quot;학생이 공부 중이에요.&quot;
	
class Teacher:
	# 생성자에서는 의존성을 받지 않음
	def __init__(self):
		pass
	
	# 메서드 파라미터로 의존성을 주입 받음
	def teach(self, student: Student):
		return student.study()
	
	def check_homework(self, student: Student):
		return f&quot;{student.study()} 숙제를 확인합니다.&quot;
	
	def give_feedback(self, student: Student):
		return f&quot;{student}에 대한 피드백&quot;
	
if __name__ == &quot;__main__&quot;:
    student = Student()
    teacher = Teacher()  # 의존성 없이 생성

    # 메서드 호출 시마다 의존성을 주입
    print(teacher.teach(student))              # 학생이 공부 중이에요.
    print(teacher.check_homework(student))     # 학생이 공부 중이에요. 숙제를 확인합니다.
    print(teacher.give_feedback(student))      # sb 에 대한 피드백&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.3 Field Injection &amp;rarr; @Autowired&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체 생성 후, 필드에 직접 할당한다. teacher.student = student
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;약간 구현부에서 setter 선언하는 것처럼 사용하는 느낌&lt;/li&gt;
&lt;li&gt;객체 생성과 의존성 주입 분리가 가능하다.&lt;/li&gt;
&lt;li&gt;불변성 보장 안됨(언제든 변경 가능)&lt;/li&gt;
&lt;li&gt;테스트 하기 어려움&lt;/li&gt;
&lt;li&gt;@Autowired 이 방식인데 deprecated 추세&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1760924278881&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Student:
	def __init__(self, name=&quot;sb&quot;):
		self.name = name
	
	def __str__(self):
		return self.name
	
	def study(self):
		return &quot;학생이 공부 중이에요.&quot;

class Teacher:
	def __init__(self):
		# 필드만 선언해두고 생성자에서 의존성 받지 않음
		self.student = None
	
	def teach(self): 
		return self.student.study()
	
	def check_homework(self):
		return f&quot;{self.student.study()} 숙제를 확인합니다.&quot;
	
	def give_feedback(self):
		return f&quot;{self.student}에 대한 피드백&quot;
	
if __name__ == &quot;__main__&quot;:
    student = Student()
    teacher = Teacher()  # 의존성 없이 생성

    # 필드에 직접 의존성을 주입
    teacher.student = student

    # 메서드 호출 시 파라미터 불필요
    print(teacher.teach())              # 학생이 공부 중이에요.
    print(teacher.check_homework())     # 학생이 공부 중이에요. 숙제를 확인합니다.
    print(teacher.give_feedback())      # sb 대한 피드백&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Dev/Backend</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/315</guid>
      <comments>https://cobinding.tistory.com/315#entry315comment</comments>
      <pubDate>Mon, 20 Oct 2025 10:42:29 +0900</pubDate>
    </item>
    <item>
      <title>[K8S] 자주 사용하는 Kubernetes CLI 정리</title>
      <link>https://cobinding.tistory.com/314</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업무를 하면서 점점 파드에 접근해서 정보를 확인할 일이 늘어나고 있다. 온콜이나 장애 상황 시에 노션에 붙여둔 명령어를 일일이 찾아보는게 허들이라 느껴져서 아예 블로그에 정리해보기로 했다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Kubectl&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes API를 사용해서 쿠버네티스 클러스터의 컨트롤 플레인과 통신하기 위한 커맨드 라인 툴이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;리소스 관리:&lt;/b&gt; Pod, Service, Deployment, ConfigMap 등 k8s 리소스를 생성하고 관리할 수 있다. YAML 파일을 통해 선언적으로 리소스를 정의하거나 명령형 커맨드로 직접 다룰 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클러스터 상태 확인:&lt;/b&gt; 클러스터에서 실행 중인 애플리케이션의 상태, 노드 정보, 이벤트 로그 등을 조회할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;애플리케이션 배포 및 업데이트:&lt;/b&gt; 컨테이너 이미지를 배포하고, 롤링 업데이트를 수행하며, 필요 시 롤백할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;0. aws-vault 로그인&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 경우 &lt;span style=&quot;color: #ef5369;&quot;&gt;aws-vault&lt;/span&gt;를 사용하고 있기에, &lt;b&gt;클러스터 접근을 위한 특정 프로필을 사용하여 로그인&lt;/b&gt;을 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1759028676406&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;aws-vault exec daangn/prod -- zsh
aws-vault exec daangn/alpha -- zsh
aws-vault exec daangn/data -- zsh&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;~/.aws/config&lt;/span&gt; 하위에 정의된 자격 증명에 관련한 프로필로 인증한 뒤, aws에 접근할 수 있다.&lt;/li&gt;
&lt;li&gt;&amp;mdash;zsh : aws-vault 실행 후 zsh 셸을 시작한다는 뜻!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. cluster contexts 확인 및 등록&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 cluster contexts란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨텍스트에는 다음과 같은 정보가 담겨있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Cluster: 연결할 k8s 클러스터의 API 서버 주소와 인증서 정보&lt;/li&gt;
&lt;li&gt;User: 해당 클러스터에 접속할 때 사용할 사용자 인증 정보(토큰, 인증서, AWS IAM 등)&lt;/li&gt;
&lt;li&gt;Namespace: 기본으로 작업할 네임스페이스&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업을 하기 전에 항상 이 컨텍스트를 확인해야 한다. 그래서 &lt;b&gt;나의 경우는 kube-ps1을 통해 터미널 가장 앞 쪽에 현재 접속중인 context가 표시 되도록 해두었다!&lt;/b&gt; 컨텍스트 실수로 인한 프로덕션 장애가 발생하는 경우가 실제로도 종종 있다. 예를 들어서, 개발 서버를 살제하려다가 실수로 프로덕션에서 실행하는.. &amp;nbsp; 실수는 누구나 할 수 있으니까 이 컨텍스트의 중요성을 알고, kube-ps1과 같은 도구를 활용하자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2 cluster contexts 정보 확인하기&lt;/h3&gt;
&lt;pre id=&quot;code_1759028741488&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 로컬 kube config 정보를 통해 로컬 환경에 등록되어 있는 컨텍스트 확인
kubectl config get-contexts

# 콘솔 eks 클러스터 확인
aws eks list-clusters --region ap-northeast-2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 &amp;harr; 콘솔에 등록된 클러스터 컨텍스트를 비교하고, 작업이 필요한 클러스터가 있다면 context를 등록해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.3 로컬 환경에 contexts 등록 및 사용하기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️ eks update-kubeconfig로 로컬 config 업데이트&lt;/h4&gt;
&lt;pre id=&quot;code_1759028806105&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;aws eks update-kubeconfig --name ${cluster} --region ap-northeast-2 --alias ${cluster}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 경우, 콘솔에 없는 kmkr 클러스터 컨텍스트를 위 명령어를 통해 추가해주었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 kubeconfig는 &lt;b&gt;개인 설정 파일(~/.kube/config)&lt;/b&gt;에 나의 kubectl이 클러스터에 연결하는 방법을 정의하는 명령어다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️ config use-context로 사용할 컨텍스트 지정&lt;/h4&gt;
&lt;pre id=&quot;code_1759028836806&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl config use-context ${cluster}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0hRy2/btsQSiCSQtq/bAGOqgKGdcNE1dDgADHaOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0hRy2/btsQSiCSQtq/bAGOqgKGdcNE1dDgADHaOk/img.png&quot; data-alt=&quot;사용할 컨텍스트 지정 명령어&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0hRy2/btsQSiCSQtq/bAGOqgKGdcNE1dDgADHaOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0hRy2%2FbtsQSiCSQtq%2FbAGOqgKGdcNE1dDgADHaOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;90&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;사용할 컨텍스트 지정 명령어&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Namespace 접근&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;use-context를 통해 특정 클러스터에 접근했다면, 이제 그 하위 namespace에 접근할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 namespace란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;namespace는 클러스터를 하나의 큰 건물이라고 치면, 건물 내부 방이다. namespace는 동일한 물리적 클러스터를 여러 가상 클러스터로 나누는 방법으로, 단일 클러스터 내에서 리소스 그룹을 격리하는 메커니즘이다. (&lt;a href=&quot;https://kubernetes.io/ko/docs/concepts/overview/working-with-objects/namespaces/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;자세한 namespace 내용&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 namespace 목록 추출 및 접근&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 모든 네임스페이스 목록 확인
kubectl get namespaces
kubectl get ns
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cglC4J/btsQSkN9Sqk/5bpAiuV9dNDnRyPqZZmmRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cglC4J/btsQSkN9Sqk/5bpAiuV9dNDnRyPqZZmmRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cglC4J/btsQSkN9Sqk/5bpAiuV9dNDnRyPqZZmmRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcglC4J%2FbtsQSkN9Sqk%2F5bpAiuV9dNDnRyPqZZmmRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;206&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;STATUS: namgspace 상태
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;active: 정상 작동 중&lt;/li&gt;
&lt;li&gt;terminating: 삭제 중&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;AGE: namespace가 생성된 후 경과 시간&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3 특정 Namespace의 Pod 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 특정 namespace의 pod에 접근해서 리소스 상태를 확인하는 작업이 많다. 아래와 같은 명령어로&lt;b&gt; 특정 네임스페이스의 pod들을 확인할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759030314232&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl get pods -n ${namespace}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;772&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/thbMN/btsQTAIXcw1/1YqKZFcrMyUxRJq7D2KfSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/thbMN/btsQTAIXcw1/1YqKZFcrMyUxRJq7D2KfSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/thbMN/btsQTAIXcw1/1YqKZFcrMyUxRJq7D2KfSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FthbMN%2FbtsQTAIXcw1%2F1YqKZFcrMyUxRJq7D2KfSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;296&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;772&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️ Pod는 Node Group의 컨테이너인데, namespace로 접근하는 이유는?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 Pod는 Node 위에서 실행되지만, 여러 이유로 다른 Node로 이동을 하게 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Node 장애 시 다른 Node로 스케줄링&lt;/li&gt;
&lt;li&gt;Node 유지보수를 위한 Pod drain&lt;/li&gt;
&lt;li&gt;새 노드가 추가되면 Pod 재배치&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  물리적 구조(실제 Pod가 실행되는 곳)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cluster&lt;br /&gt;&amp;nbsp;&amp;nbsp;ㄴ&amp;nbsp;Node1&amp;nbsp;(EC2&amp;nbsp;인스턴스)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ㄴ&amp;nbsp;Pod&amp;nbsp;A&amp;nbsp;(production&amp;nbsp;namespace)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ㄴ&amp;nbsp;Pod&amp;nbsp;B&amp;nbsp;(development&amp;nbsp;namespace)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ㄴ&amp;nbsp;Pod&amp;nbsp;C&amp;nbsp;(production&amp;nbsp;namespace)&lt;br /&gt;&amp;nbsp;&amp;nbsp;ㄴ&amp;nbsp;Node2&amp;nbsp;(EC2&amp;nbsp;인스턴스)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ㄴ&amp;nbsp;Pod&amp;nbsp;D&amp;nbsp;(monitoring&amp;nbsp;namespace)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ㄴ&amp;nbsp;Pod&amp;nbsp;E&amp;nbsp;(production&amp;nbsp;namespace)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;  논리적 구조(실제 우리가 관리하는 방식)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;Cluster&lt;br /&gt;&amp;nbsp;&amp;nbsp;ㄴ&amp;nbsp;production&amp;nbsp;namespace&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ㄴ&amp;nbsp;Pod&amp;nbsp;A&amp;nbsp;(Node1에서&amp;nbsp;실행&amp;nbsp;중)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ㄴ&amp;nbsp;Pod&amp;nbsp;C&amp;nbsp;(Node1에서&amp;nbsp;실행&amp;nbsp;중)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ㄴ&amp;nbsp;Pod&amp;nbsp;E&amp;nbsp;(Node2에서&amp;nbsp;실행&amp;nbsp;중)&lt;br /&gt;&amp;nbsp;&amp;nbsp;ㄴ&amp;nbsp;development&amp;nbsp;namespace&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ㄴ Pod B (Node1에서 실행 중)&lt;br /&gt;&amp;nbsp;&amp;nbsp;ㄴ&amp;nbsp;monitoring&amp;nbsp;namespace&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ㄴ&amp;nbsp;Pod&amp;nbsp;D&amp;nbsp;(Node2에서&amp;nbsp;실행&amp;nbsp;중)&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Pod 상세 정보&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 get으로 pod 상태 확인하기&lt;/h3&gt;
&lt;pre id=&quot;code_1759033779401&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Pod 빠르게 확인
kubectl get pods -n ${namespace}

# Node, IP 정보까지 확인
kubectl get pods -n ${namespace} -o wide&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;772&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/thbMN/btsQTAIXcw1/1YqKZFcrMyUxRJq7D2KfSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/thbMN/btsQTAIXcw1/1YqKZFcrMyUxRJq7D2KfSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/thbMN/btsQTAIXcw1/1YqKZFcrMyUxRJq7D2KfSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FthbMN%2FbtsQTAIXcw1%2F1YqKZFcrMyUxRJq7D2KfSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;296&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;772&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;READY:&lt;/b&gt; 준비된 컨테이너 수 / 전체 컨테이너 수를 나타낸다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;1/2의 경우, 메인 앱은 정상이고 사이드카는 죽은 상태다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 get + grep 조합으로 Pod Status 필터링 하기&lt;/h3&gt;
&lt;pre id=&quot;code_1759039840683&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl get pods -n production | grep Error
kubectl get pods -n production | grep Pending
kubectl get pods -n production | grep CrashLoopBackOff
kubectl get pods -n production | grep Terminating&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Error:&lt;/b&gt; 컨테이너가 exit code로 종료된 상황이다. 애플리케이션 에러로 파드가 죽은 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pending:&lt;/b&gt; Pod가 Scheduling 대기 중인 상태다. 주로 리소스 부족이나 노드 조건 불일치에 의해 발생한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CrashLoopBackOff:&lt;/b&gt; 컨테이너가 &lt;span style=&quot;color: #ef5369;&quot;&gt;시작 &amp;rarr; 죽음 &amp;rarr; 재시작&lt;/span&gt;을 반복하고 있는 상황이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Terminating:&lt;/b&gt; Pod가 삭제 중인데 아직 완전히 사라지지 않은 상태로, 보통 graceful shutdown 대기중인 상태다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ImagePullBackOff:&lt;/b&gt; 이미지 다운로드가 실패한 상황이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;+) get + grep 조합에 대한 옵션&lt;/h3&gt;
&lt;pre id=&quot;code_1771914190944&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl get pods -A -o wide | grep 10.11.1.11..&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;-o wide:&lt;/b&gt; 파드 IP, 노드 이름 등 추가 정보를 포함한 넓은 출력 형식을 받을 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;-A:&lt;/b&gt; 모든 네임스페이스에서 조회할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3 Pod 로그 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1759039937665&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl logs --tail=${n} ${pod} -n ${namespace}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;--tail 옵션을 통해 최근 n줄에 대한 로그를 출력한다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.4 Pod 리소스 사용량 확인 / 조정&lt;/h3&gt;
&lt;pre id=&quot;code_1759040054865&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl top pods -n ${namespace}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1892&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjnDyW/btsQTBu1SCA/Q5Qe9tlNRoENp7RyjZN1V1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjnDyW/btsQTBu1SCA/Q5Qe9tlNRoENp7RyjZN1V1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjnDyW/btsQTBu1SCA/Q5Qe9tlNRoENp7RyjZN1V1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjnDyW%2FbtsQTBu1SCA%2FQ5Qe9tlNRoENp7RyjZN1V1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;127&quot; data-origin-width=&quot;1892&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;top 명령어를 통해 개별 Pod의 리소스 사용량을 확인할 수 있다. &lt;b&gt;CPU = 8m&lt;/b&gt; 이라는 것은&lt;b&gt; CPU를 0.8% 사용하고 있다는 뜻&lt;/b&gt;이다. 만약 CPU에 950m와 같은 수치가 보인다면 한 Pod가 CPU를 95% 사용하고 있다는 뜻으로, 리소스를 비효율적으로 사용하고 있음을 의심해볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚫️ Pod는 Node의 리소스를 할당 받아 사용한다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pod는 개별 CPU/Memory를 가지는 것이 아니라, Node의 리소스를 할당 받아서 사용한다. 즉, 다음과 같은 구조를 가진다.&lt;/p&gt;
&lt;pre id=&quot;code_1759040863152&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Node (EC2 인스턴스: t3.xlarge)
├── 실제 리소스: 4 CPU cores, 16GB RAM
│
├── ⚫️ Pod A: 1 CPU, 2GB 할당받음
├── ⚫️ Pod B: 0.5 CPU, 1GB 할당받음
├── ⚫️ Pod C: 2 CPU, 4GB 할당받음
└── 남은 리소스: 0.5 CPU, 9GB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.5 describe로 Pod 세팅 상세 확인하기&lt;/h3&gt;
&lt;pre id=&quot;code_1759041191456&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl describe pod ${pod} -n ${namespace}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 get pod, top pod 명령어와 다르게 해당 Pod에 설정된 구체적인 내용들(&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;b&gt;IP, Port, Liveness, Readiness 등등&lt;/b&gt;)&lt;/span&gt;을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;K8S도 GUI 환경을 제공하지만 보통 업무를 할 때 kubectl 명령어를 많이 사용해서 정리해보았따.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장애 일지 보면서 필요한 명령어들은 조금씩 더 추가해볼 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠베 공부 부단히 해야겠다 ㅠ_ㅠ&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/DevOps</category>
      <category>DevOps</category>
      <category>Infra</category>
      <category>k8s</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/314</guid>
      <comments>https://cobinding.tistory.com/314#entry314comment</comments>
      <pubDate>Sun, 28 Sep 2025 17:13:42 +0900</pubDate>
    </item>
    <item>
      <title>[회고] 사내 해커톤 시뮬레이션 회고!</title>
      <link>https://cobinding.tistory.com/313</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 외부인을 대상으로 해커톤을 주최했다. 이 해커톤의 성공적인 진행을 위해 미리 사내 개발자를 대상으로 시뮬레이션을 진행했고, 자진해서 8시간 해커톤을 참여하게되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmTax5/btsQPDSVDXb/TMScC3FfDut94x6BOW4kkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmTax5/btsQPDSVDXb/TMScC3FfDut94x6BOW4kkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmTax5/btsQPDSVDXb/TMScC3FfDut94x6BOW4kkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmTax5%2FbtsQPDSVDXb%2FTMScC3FfDut94x6BOW4kkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;667&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참여 동기&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;[ 지식 관점 ] &lt;/b&gt;인프라실에서 자연스럽게 인프라 역량을 키우는 데 몰입하다 보니 상대적으로 프로덕트 개발에 대한 집중은 줄어들었다. 그래서 이번 시뮬레이션 대상자 지원은, 놓쳤다고 느꼈던 개발 지식과 열정을 다시 끌어올리기 위해 스스로에게 동기부여를 주기로 했다!&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[ 협업 관점 ]&lt;/b&gt; 시즌테크로 입사한지 6개월차가 되었다. SRE팀은 당근 인프라를 관리하며 서비스 안정성을 책임진다. 또한, 개발자들이 개발에 집중할 수 있도록 인프라 기능을 추상화하고, 서비스 개발에만 집중할 수 있도록 플랫폼을 개발 및 운영한다. 그래서 개발자들의 목소리를 잘 듣는게 중요하다고 생각한다. 사내 개발자들이 평소 어떤 업무 사고방식을 가지고 있는지, 무슨 일을 하는지 더 잘 알고싶었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 8시간 스프린트로 빠르게 서비스 2개 출시하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(해커톤 방식은 구체적으로 설명하지 않고, 주로 느낀점을 바탕으로 방향성을 정리해보는 회고글이다.)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여느 해커톤과 비슷하게 주제를 오픈하고, 그 주제에 대해 기획하고 개발한다. Claude Code가 등장하고 나서 개발 패러다임이 많이 바뀌었는데, 아니나 다를까 해커톤도 예전과는 양상이 많이 바뀌었다고 느꼈다. (물론 시뮬레이션이었지만&amp;hellip;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 해커톤 준비를 할 때도, 나는 아이디어나 코드베이스를 공부하기 보다는, 클코를 어떻게 하면 더 잘 쓰지?에 대해 고민하고, 미리 학습했다. 그 이유는 빠른 시간 안에 최대한 효율을 내야 하는 해커톤에서는 AI를 잘 활용하는게 이득일 것이라 판단했기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미리 준비한 Claude Code 잘 사용하기 전략과 능력자 팀원을 만나, 8시간 안에 2개의 서비스를 출시할 수 있었다. (사실 클코도 클코지만 팀원의 능력이 더 한몫한 것같다.. 존경해요..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. AI를 잘 쓰려면? 프롬프트 엔지니어링과 Context&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1 프롬프트 엔지니어링&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프롬프트 엔지니어링을 잘 하려면&lt;b&gt; 텍스트를 잘 다뤄야 한다&lt;/b&gt;는 점을 배웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 생각을 뽑아내서 글로 잘 쓰는게 첫 단계이기 때문이다. 책 많이 읽고, 글 많이 써야겠다고 다짐하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그리고 개발지식과 경험이 탄탄해야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 해커톤에서도 99% 코드를 Claude Code가 작성해주었지만 아래와 같은 지식이 없었다면 엄청 헤맸을 것을 예상한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구체적인 요구사항 명세서&lt;/li&gt;
&lt;li&gt;API 스펙&lt;/li&gt;
&lt;li&gt;이 프로젝트에서 사용할 기술과 그로부터 얻으려고 하는 이점&lt;/li&gt;
&lt;li&gt;서빙 방식
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #dddddd;&quot;&gt;팀원이 알려준 방식으로 했던거.. 간단하게 구조라도 그려서 추가해보자..!&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://CLAUD.md&quot;&gt;CLAUD.md&lt;/a&gt; 파일에 내가 구현하고자 하는 명세와 스펙을 마크다운으로 작성했다. 중간중간 기능 구현에 필요한 기술은 팀원과 토의하고, Claude Code에게 구현하게 했다. 구현 과정과 결과물은 당연히 사람이 검토하고 반영하도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2 Context 다루기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;Claude Code를 쓰면서 하면 좋은 습관은 중간중간 컨텍스트를 관리해주는 것이다&lt;/b&gt;. Context가 너무 방대해지면, AI도 길을 잃는다. 그리고 한번 AI가 많은 코드 베이스 변화를 주면, 나 또한 따라가기가 힘들다. 그래서 항상 작업 Point를 찍어가면서 지금 나와 AI가 어느 지점에 있는지, 어느 방향으로 가는지 Contex Point를 맞춰가면서 나아가야 한다. 이걸 잘 하려면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업 중간 Context를 Claude.md에 정리&lt;/li&gt;
&lt;li&gt;중요한 피쳐 작업은 Claude Code에게 변경을 맡기기 전에 Point 설정(마음에 안들면 롤백해야 하니까)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 소회&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 발전하면서 엔지니어 자리를 위협하는거 아닌가 하는 의심이 도처에 도사리고 있다. 하지만 오히려 엔지니어의 생산성을 높여주고, 엔지니어들이 특히나 잘 활용할 수 있는 분야이기에, 엔지니어의 역량은 더더욱 중요도가 높아질 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 부단히 지식과 경험을 쌓으려 노력하는 걸 멈추지 않아야 한다. 오히려 내 생각과 지식에 대한 구현을 도맡아줄 동료가 생겼으니, 더 시간을 잘 쓸 수 있고, 더 똑똑해질 수 있다. 그래서 나는 앞으로도 지식과 경험을 쌓는 일에 Aggressive한 태도로 임하며, 변화의 파도 위에서 더욱 성장해 나갈 것을 다짐했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>회고 &amp;amp; 후기/회고 &amp;amp; 후기</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/313</guid>
      <comments>https://cobinding.tistory.com/313#entry313comment</comments>
      <pubDate>Wed, 24 Sep 2025 22:52:53 +0900</pubDate>
    </item>
    <item>
      <title>[Network] CIDR, IP, Subnet Mask</title>
      <link>https://cobinding.tistory.com/312</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;볼 때마다 헷갈려서 아예 각 잡고 정리해보는 CIDR, IP, Subnet Mask 개념&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확실하게 짚고 넘어가자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. CIDR(Classless Inter-Domain Routing)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IP/비트수로 네트워크 범위를 표현하는 방식&lt;/li&gt;
&lt;li&gt;&lt;b&gt;x.x.x.x/ab&lt;/b&gt; 형식&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. IP&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 IP 의미&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크 + 호스트를 8비트씩 점으로 구분해서 10진수로 표기하는 방법
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Network Address&lt;/b&gt;: 해당 IP가 속한 네트워크 구역을 알 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;192.168.1.0/24 네트워크에 있는 모든 장비는 네트워크 주소가 192.168.1.0으로 같다. (서브넷 마스크를 통해 알 수 있다.)&lt;/li&gt;
&lt;li&gt;192.168.1.1/24, 192.168.1.12/24 ,192.168.1.2/24가 목적지인 패킷은 라우터에 의해 모두 같은 네트워크로 라우팅된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Host Address&lt;/b&gt;: 한 네트워크 영역 안에서 특정 장비(호스트)를 구분하는 고유 주소다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;192.168.1.10 &amp;rarr; 프린터&lt;/li&gt;
&lt;li&gt;192.168.1.20 &amp;rarr; 내 노트북&lt;/li&gt;
&lt;li&gt;192.168.1.30 &amp;rarr; 서버&lt;/li&gt;
&lt;/ul&gt;
&amp;rArr; 이들은 전부 192.168.1.0/24라는 네트워크에 속해 있고,&lt;b&gt; 마지막 옥텟(.10, .20, .30)으로만 서로를 구분&lt;/b&gt;한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 공인 IP와 사설 IP&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Public IP:&lt;/b&gt; 인터넷에서 유일하게 식별 가능한 주소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Private IP:&lt;/b&gt; 내부 네트워크 전용, 인터넷에서는 직접 안 보임. RFC1918에서 정의된 범위만 사용 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사설 IP는 3가지 블록으로 정해져 있다.&amp;nbsp;&amp;nbsp;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 105px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 25px;&quot;&gt;
&lt;td style=&quot;height: 25px;&quot;&gt;클래스&lt;/td&gt;
&lt;td style=&quot;height: 25px;&quot;&gt;범위&lt;/td&gt;
&lt;td style=&quot;height: 25px;&quot;&gt;크기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 30px;&quot;&gt;
&lt;td style=&quot;height: 30px;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;height: 30px;&quot;&gt;10.0.0.0 ~ 10.255.255.255&lt;/td&gt;
&lt;td style=&quot;height: 30px;&quot;&gt;/8 (16,777,216 개 주소)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 25px;&quot;&gt;
&lt;td style=&quot;height: 25px;&quot;&gt;B&lt;/td&gt;
&lt;td style=&quot;height: 25px;&quot;&gt;172.16.0.0 ~ 172.31.255.255&lt;/td&gt;
&lt;td style=&quot;height: 25px;&quot;&gt;/12 (1,048,576 개 주소)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 25px;&quot;&gt;
&lt;td style=&quot;height: 25px;&quot;&gt;C&lt;/td&gt;
&lt;td style=&quot;height: 25px;&quot;&gt;192.168.0.0 ~ 192.168.255.255&lt;/td&gt;
&lt;td style=&quot;height: 25px;&quot;&gt;/16 (65,536 개 주소)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;클래스 A(&lt;span style=&quot;color: #ef5369;&quot;&gt;0~127&lt;/span&gt; 대역)&lt;/b&gt;: 보통 클라우드 환경이나 대기업 내부 네트워크에서 많이 사용&lt;b&gt;(ex. 10.x.x.x)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;8비트 네트워크. 기본 서브넷 마스크가 255.0.0.0(/8)으로 호스트 영역이 매우 큼&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클래스 B(&lt;span style=&quot;color: #ef5369;&quot;&gt;128~191&lt;/span&gt; 대역):&lt;/b&gt; RFC1918에서 정의된 Private IP 대역 중 하나&lt;b&gt;(ex. 172.x.x.x)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라우드 서비스에서도 종종 VPC CIDR 기본값으로 쓰인다.&lt;/li&gt;
&lt;li&gt;내부망을 나누는 용도로 적합하다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클래스 C(&lt;span style=&quot;color: #ef5369;&quot;&gt;192~223&lt;/span&gt; 대역):&lt;/b&gt; 비교적 작은 네트워크 대역, 흔히 집/소규모 사무실/공유기에서 많이 사용&lt;b&gt;(ex. 192.168.x.x)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Subnet Mask&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마스크는 &amp;ldquo;가린다&amp;rdquo;, &amp;ldquo;숨긴다&amp;rdquo;라는 뜻이 있다. 네트워크에서 Subnet Mask는 IP 주소에서 &lt;b&gt;네트워크 부분만 드러내고, 호스트 부분은 가려내는 도구다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 Subnet Mask의 역할&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 192.168.1.10같은 IP만 있고, 마스크 정보가 없다면 이 IP가 어떤 네트워크에 속해있는지 알 수 없기에, 라우터가 어떤 네트워크로 패킷을 보내야할 지 판단할 수 없다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;192.168.1.10이 192.168.1.0/24에 속한 건지, 192.168.1.0/16 에 속한 건지 알 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터센터, VPC 등에서는 큰 네트워크를 잘게 쪼개서(Subnetting) 나눠 써야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;10.0.0.0/8을 한 회사가 다 쓸 수 있지만,
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인프라팀: 10.0.1.0/24&lt;/li&gt;
&lt;li&gt;개발팀: 10.0.2.0/24&lt;/li&gt;
&lt;li&gt;보안팀: 10.0.3.0/24&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이렇게 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;네트워크를 쪼개 쓰는 기준&lt;/b&gt;&lt;/span&gt;이 바로 서브넷 마스크다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;브로드캐스트 패킷은 네트워크 전체에 퍼지므로, 네트워크가 크면 불필요한 트래픽이 엄청 늘어난다. 마스크를 이용해서 네트워크를 쪼개면, 브로드캐스트 범위를 작게 제한할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 계산 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.5.232.128/25&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크 영역은 25 비트, &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;호스트 영역은 32-25 = 7 비트&lt;/b&gt;&lt;/span&gt;이므로, 2**7 개의 호스트 사용 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하지만 네트워크에서는 항상 다음과 같은 규칙이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호스트 비트가 전부 0 &amp;rarr; 네트워크 주소&lt;/li&gt;
&lt;li&gt;호스트 비트가 전부 1 &amp;rarr; 브로드캐스트 주소&lt;/li&gt;
&lt;/ul&gt;
&amp;rArr; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;따러서 실제로 사용 가능한 호스트 주소 수 = 2^7 -2 = 126개&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3 Subnet Mask 해석 방법&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IP : 192.168.1.10&lt;/li&gt;
&lt;li&gt;Subnet Mask : /24&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Subnet Mask가 24라는 건, 앞에서부터 24비트가 네트워크 부분이라는 뜻이다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;/24: 11111111.11111111.11111111.00000000 = 255.255.255.0&lt;/li&gt;
&lt;li&gt;/16: 11111111.11111111.00000000.00000000 = 255.255.0.0&lt;/li&gt;
&lt;li&gt;/30: 11111111.11111111.11111111.11111100 = 255.255.255.252
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1로 되어있는 부분이 네트워크 주소로 묶이고, 나머지는 호스트 주소 영역이라는 뜻이다.&lt;/li&gt;
&lt;li&gt;128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255&lt;/li&gt;
&lt;li&gt;128 + 64 + 32 + 16 + 8 + 4 + 0 + 0 = 252&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. BroadCast&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 네트워크 안에 있는 모든 호스트에게 동시에 메시지를 보낸다. &amp;ldquo;동네 사람들 다 들어!&amp;rdquo;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;한 네트워크 안에서&lt;/b&gt; 어떤 장치에 패킷을 보내야 할 지 모를 때, &lt;b&gt;네트워크에 있는 모두에게 물어보는 방식으로 쓰인다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;브로드캐스트가 네트워크 성능에 부담되기에, 서브넷을 쪼개서 범위를 제한한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;호스트 부분을 모두 1로 채운 IP&lt;/b&gt;가 브로드캐스트 주소다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;192.168.1.0/24
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크 부분은 192.168.1 이고, 나머지 192.168.1.255가 브로드캐스트 주소!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>CS/Network</category>
      <category>network</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/312</guid>
      <comments>https://cobinding.tistory.com/312#entry312comment</comments>
      <pubDate>Sun, 21 Sep 2025 16:42:35 +0900</pubDate>
    </item>
    <item>
      <title>[Monitoring] 시계열 데이터와 TSDB</title>
      <link>https://cobinding.tistory.com/311</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인턴 때부터 Grafana 작업을 하면서 Metrics, TSDB, PromQL 관련 내용들을 개인 노션에 쭉 작성했는데, 블로그에도 새로운 카테고리를 생성하고 정리하는 공간을 만들었다. 요즘 Alert Rule을 세팅하는 작업을 하고 있는데, 노션 문서를 자주 참고하게 되어서 아예 블로그로 올려버리기로 결심!&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 시계열 기반 모니터링&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간의 변화에 따라 축적된 데이터들을 수집, 저장, 분석, 알람하는 시스템&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서.. &lt;b&gt;Prometheus&lt;/b&gt;는 다음과 같은 일을 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;15초마다 /metrics 에 접속해서 &lt;b&gt;현재 상태값&lt;/b&gt; 긁어오기&lt;/li&gt;
&lt;li&gt;수집한 메트릭을 시간과 함께 저장&lt;/li&gt;
&lt;li&gt;PromQL 쿼리를 통해 과거 1시간 평균 rate같은 걸 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 운영 시스템에서 시계열 데이터&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;과거에 무슨 일이 있었는지?&lt;/li&gt;
&lt;li&gt;변화 추이는 어떠한지?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어제보다 CPU가 더 많이 사용되는가?&lt;/li&gt;
&lt;li&gt;5분 요청량이 급증/급락 했는가?&lt;/li&gt;
&lt;li&gt;등등&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시간의 흐름을 기반으로&lt;/b&gt; 서비스를 운영한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. TSDB(Time Series Database)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDB는 &lt;b&gt;레코드 중심(row-based) 데이터라서 시간 순으로의 수많은 데이터를 다루기 어렵다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TSDB는 데이터를 table 형식이 아니라 아래와 같이 저장한다.&lt;/li&gt;
&lt;li&gt;metric_name: node_cpu_seconds_total labels: {mode=&quot;user&quot;, instance=&quot;web-1&quot;} timestamps: [10:00:00, 10:00:01, 10:00:02, ...] values: [23.5, 25.1, 22.8, ...]&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Prometheus&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 Prometheus는 시계열 기반의 모니터링 시스템 (&lt;a href=&quot;https://prometheus.io/docs/introduction/overview/&quot;&gt;문서&lt;/a&gt;)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Prometheus Server: Scraping, TSDB, PromQL&lt;/li&gt;
&lt;li&gt;Exporter: 매트릭을 내보내는 앱 or 에이전트 &amp;rarr; 이 Exporter를 node에 접근해서 설치하는 건가?&lt;/li&gt;
&lt;li&gt;&lt;b&gt;metric + label 조합&lt;/b&gt;을 하나의 시계열로 간주한다. 따라서, 아래 두 개는 다른 시계열이다.&lt;/li&gt;
&lt;li&gt;http_requests_total{method=&quot;GET&quot;, status=&quot;200&quot;} http_requests_total{method=&quot;POST&quot;, status=&quot;500&quot;}&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 Prometheus 동작 흐름&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Pull 기반 수집&lt;/b&gt;: endpoint에 직접 가서&lt;span style=&quot;color: #ee2323;&quot;&gt; /metrics&lt;/span&gt; 데이터를 긁어온다.&lt;/li&gt;
&lt;li&gt;Time Series: 내부 TSDB에 메트릭을 시간 기준으로 저장&lt;/li&gt;
&lt;li&gt;PromQL로 쿼리: 매트릭 조회, 시각화&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3 What is Prometheus not? (&lt;a href=&quot;https://medium.com/@dogabudak/pros-and-cons-of-prometheus-b04ab3afcbf7&quot;&gt;아티클&lt;/a&gt;)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로메테우스는 로깅 시스템이 아니라, 메트릭 수집용 도구다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cpu 사용률, 에러율, 요청 수와 같은 숫자 중심 지표를 수집한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메트릭은 Prometheus, 로그는 Loki/ELK/CloudWatch Logs 등으로 분리&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;long-term DB를 지원하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3.4 Prometheus Storage (&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://prometheus.io/docs/prometheus/1.8/storage/&quot;&gt;문서&lt;/a&gt;), (&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://blog.naver.com/PostView.naver?blogId=alice_k106&amp;amp;logNo=221829384846&amp;amp;parentCategoryNo=&amp;amp;categoryNo=27&amp;amp;viewDate=&amp;amp;isShowPopularPosts=false&amp;amp;from=postView&quot;&gt;alice님 블로그&lt;/a&gt;)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Prometheus는 시계열 데이터 조각(chunk)를 메모리에 유지한다.&lt;/li&gt;
&lt;li&gt;storage.local.path 라는 플래그로 수집한 시계열 데이터를 디스크에 저장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3.5 Prometheus Metric Type (&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://prometheus.io/docs/concepts/metric_types/&quot;&gt;문서&lt;/a&gt;)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Counter: 항상 증가하는 값(&lt;b&gt;단조 증가&lt;/b&gt;), 재시작 시 0으로 리셋
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;increase: 내부적으로는 rate() * seconds로 계산&lt;/li&gt;
&lt;li&gt;rate: 1초당 얼마나 증가했는지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Gauge: 현재 메모리 사용량과 같은 증감값, 시스템 자원 상태&lt;/li&gt;
&lt;li&gt;Histogram&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Chunk Data와 Metrics Data의 차이&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Metric은 측정 대상을 나타내는 이름 + 라벨 조합으로, aws_cloudfront_requests같이 수집한 덩어리다.&lt;/li&gt;
&lt;li&gt;Chunk는 각 시계열에 대해서 일정 시간 동안의 값을 묶어서 저장한 덩어리다.&lt;/li&gt;
&lt;li&gt;예를 들어서, http_requests_total{method=&quot;GET&quot;, status=&quot;200&quot;} 라는 메트릭의 데이터가 있다고 했을 때 chunk는 다음과 같다.&lt;/li&gt;
&lt;li data-ke-size=&quot;size16&quot;&gt;chunk 1: [10:00:00 &amp;rarr; 100, 10:00:15 &amp;rarr; 110, 10:00:30 &amp;rarr; 125, ...] chunk 2: [12:00:00 &amp;rarr; 500, 12:00:15 &amp;rarr; 530, ...]&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Monitoring/PromQL &amp;amp; Grafana</category>
      <category>Grafana</category>
      <category>Monitoring</category>
      <category>promql</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/311</guid>
      <comments>https://cobinding.tistory.com/311#entry311comment</comments>
      <pubDate>Mon, 15 Sep 2025 23:25:40 +0900</pubDate>
    </item>
    <item>
      <title>[Network] CDN(Content Delivery Network)</title>
      <link>https://cobinding.tistory.com/310</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Valkey 발표 및 전환을 앞두고..! 이번 스프린트에서 드디어 CloudFront Datadog Alert 작업을 시작하게 되었다. 윈터테크 인턴 시절 CloudFront 리소스에서 Additional Metrics가 비활성화된 점을 발견하고, 프로덕션 환경에 한해 이를 활성화하여 모니터링을 강화하자는 제안을 했으며 팀에서 이를 받아들여 적용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Additional Metrics 활성화 이후 기존 CloudFront Datadog Alert의 정확도가 떨어지는 문제가 발생했다. 이번 스프린트에서는 이 문제를 개선하여 Alert 정확도를 높이고, CDN 모니터링을 체계적으로 강화하는 것을 목표로 한다. 이번 포스팅으로 CDN에 대해 잘 정리해보겠다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. CDN 기본 구조와 목적&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CDN은 정적/동적 컨텐츠(HTML, CSS, JS, 이미지, 동영상)을 전 세계에 분산 배치된 Edge 서버에서 제공하는 시스템이다. CDN이 점점 발전하면서 단순 캐싱 이외에도 DDoS 방어, WAF, 동적 라우팅 등등의 기능을 제공하게 되었다. 이 포스팅은 이러한 CDN의 근본적인 내용을 학습하고 정리하는 목적으로 작성한다!&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자와 가까운 위치에서 응답해서 Latency를 줄인다.&lt;/li&gt;
&lt;li&gt;Origin(원본) 서버의 부하를 낮춘다.&lt;/li&gt;
&lt;li&gt;CDN은 전 세계 Edge Location에 분산되어 있기 때문에, Origin Server보다 더 많은 트래픽을 처리할 수 있다.&lt;/li&gt;
&lt;li&gt;네트워크 트래픽 비용을 절감시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Why CDN?&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;912&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lMVnV/btsQoKyLvu1/ewZ6eD4K7ctntKzOGgpjm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lMVnV/btsQoKyLvu1/ewZ6eD4K7ctntKzOGgpjm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lMVnV/btsQoKyLvu1/ewZ6eD4K7ctntKzOGgpjm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlMVnV%2FbtsQoKyLvu1%2FewZ6eD4K7ctntKzOGgpjm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;487&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;912&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 클라이언트 &amp;lt;-&amp;gt; 서버 Latency 해소&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지와 같이 Client와 Server 간 지리적 위치가 차이나면, 단순히 요청이 한번 왔다갔다 하는 데에도 많은 응답 시간이 걸린다. 이에 CSS와 같이 바뀌지 않는 &lt;b&gt;정적 파일에 대해 Edge Location Server에 저장(캐싱)하여 빠르게 전송하는 방식을 고안&lt;/b&gt;하였고, 그게 CDN이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재는 정적 파일 뿐만 아니라 Cache-Control 헤더를 활용하여 API 응답과 같은 동적인 부분도 캐싱한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 HTTP/1.1의 도메인 연결 제한&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 HTTP/1.1 시절, &lt;b&gt;브라우저는 도메인당 동시 연결 수를 제한&lt;/b&gt;했다. (&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Connection_management_in_HTTP_1.x&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;HTTP Connetion Management Guide From MDN&lt;/a&gt;)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;As a solution, browsers open several connections to each domain, sending parallel requests. Default was once 2 to 3 connections, but this has now increased to a more common use of 6 parallel connections. There is a risk of triggering DoS protection on the server side if attempting more than this number.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저가 &lt;b&gt;하나의 도메인에 동시에 열 수 있는 TCP 연결이 최대 6개로 제한&lt;/b&gt;된다는 뜻이다. 7번째 패킷은 앞의 1~6 번째 패킷의 통신이 끝날 때까지 대기해야 하므로, 응답 성능이 좋지 않다.&lt;/p&gt;
&lt;pre id=&quot;code_1757233372034&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  &amp;lt;!-- 모두 example.com &amp;rarr; 총 6개 연결만 사용 --&amp;gt;
  example.com/logo.png     [연결 1]
  example.com/banner.jpg   [연결 2]
  example.com/icon1.png    [연결 3]
  example.com/icon2.png    [연결 4]
  example.com/style.css    [연결 5]
  example.com/script.js    [연결 6]
  example.com/footer.png   [대기...]  &amp;lt;!-- 7번째는 대기 --&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;(참고) &lt;/b&gt;&lt;/span&gt;HTTP/2.x로 업그레이드되면서 이 점은 해소되었다. 2015년에 IETF에서 RFC 7540으로 공식 발표되었고, 현재 대부분의 브라우저와 서버가 지원하고 있다. 아래는 필자의 깃허브 페이지에 접속했을 때 주고받는 프로토콜이 대부분 h2(HTTP/2)로 설정되어있음을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3418&quot; data-origin-height=&quot;1798&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dxO2KK/btsQoGJU1H2/16t90kbRu3uqswdm4ruVW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dxO2KK/btsQoGJU1H2/16t90kbRu3uqswdm4ruVW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dxO2KK/btsQoGJU1H2/16t90kbRu3uqswdm4ruVW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdxO2KK%2FbtsQoGJU1H2%2F16t90kbRu3uqswdm4ruVW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;395&quot; data-origin-width=&quot;3418&quot; data-origin-height=&quot;1798&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. CDN Architecture&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CDN은 전 세계에 Edge Server를 두고 Origin Server의 콘텐츠를 캐싱한다. 사용자 요청이 들어오면 가까운 Edge에서 빠르게 캐싱된 내용을 응답으로 제공한다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;CloudFront는 브라우저와 Origin 사이에 끼어있는 CDN이다. 모든 요청은 무조건 CF를 먼저 거치게 된다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2136&quot; data-origin-height=&quot;1062&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLGtOt/btsQBiJtZSF/MJklXxTLsjkUXje74CXrrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLGtOt/btsQBiJtZSF/MJklXxTLsjkUXje74CXrrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLGtOt/btsQBiJtZSF/MJklXxTLsjkUXje74CXrrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLGtOt%2FbtsQBiJtZSF%2FMJklXxTLsjkUXje74CXrrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2136&quot; height=&quot;1062&quot; data-origin-width=&quot;2136&quot; data-origin-height=&quot;1062&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Last Mile: 지역 ISP로부터 일반 사용자에게 콘텐츠가 전달되는 구간&lt;/li&gt;
&lt;li&gt;Middle Mile: Edge Server들과 ISP 사이 구간&lt;/li&gt;
&lt;li&gt;First Mile: Origin Server와 ISP 사이 구간&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 Edge Server와 PoP(Point of Presence)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 PoP는 CDN의 &lt;b&gt;물리적 위치&lt;/b&gt;를 나타낸다. &lt;b&gt;하나의 PoP는 여러 대의 Edge Server로 구성&lt;/b&gt;된다. 사용자의 요청은 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;가장 가까운 PoP로 라우팅&lt;/span&gt;&lt;/b&gt; 된 뒤, 그중 내부 Edge Server 하나가 응답을 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- 서울 PoP(ICN)&lt;br /&gt;- 도쿄 PoP(NRT)&lt;br /&gt;- 싱가폴 PoP (SIN)&lt;br /&gt;- 샌프란시스코 PoP(SFO)&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1464&quot; data-origin-height=&quot;1022&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qKlXB/btsQol65iiD/C5elZt4aQdh9XqkRsrVnf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qKlXB/btsQol65iiD/C5elZt4aQdh9XqkRsrVnf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qKlXB/btsQol65iiD/C5elZt4aQdh9XqkRsrVnf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqKlXB%2FbtsQol65iiD%2FC5elZt4aQdh9XqkRsrVnf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;489&quot; data-origin-width=&quot;1464&quot; data-origin-height=&quot;1022&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 Origin Shield&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Origin Shield는 Origin Server 바로 앞단에 위치한 캐시 계층이다. 모든 CDN Edge 요청을 단일 지점으로 모은다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Shiled가 없을 때&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;100개의 Edge Server Cache Miss로 인한 -&amp;gt; Origin 동시 요청
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 콘텐츠를 동시에 100번 요청으로 Origin 부하가 폭증한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Shiled가 있을 때&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Origin Shiled가 100개의 Edge Server 요청을 모아서 한 번에 Origin에 요청
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Shiled가 대표로 1번만 요청하고 나머지 99개는 Shield에서 대기한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. CDN과 DNS&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CDN은 DNS를 시작점으로 사용한다.&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자가 cdn.example.com 요청&lt;/li&gt;
&lt;li&gt;DNS 서버가 해당 도메인의 위치를 CDN 서비스에 알려줌&lt;/li&gt;
&lt;li&gt;CDN은 Location PoP의 Edge Server에 캐싱된 내용 요청&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CloudFront 예시를 통해 알아보자.&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS CloudFront를 통해 Origin에 대한 CDN을 설정하면, &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Alternate Domain Name (CNAME) &amp;amp; Custom SSL Certificate&lt;/b&gt;&lt;/span&gt; 또한 입력 및 생성된다. CloudFront를 생성하면 기본 도메인이 &lt;u&gt;xxxxx.cloudfront.net&lt;/u&gt; 이런 도메인이 생성되는데, 이는 사용자가 기억하기 어려운 도메인이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 CF 자신의 도메인(&lt;u&gt;xxxxx.cloudfront.net&lt;/u&gt;)을 R53에서 생성된 도메인과 CNAME으로 연결한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1176&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buZnEz/btsQmDnwm64/MiQFbiNKKK9mlDmCQGyxck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buZnEz/btsQmDnwm64/MiQFbiNKKK9mlDmCQGyxck/img.png&quot; data-alt=&quot;AWS CloudFront 생성 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buZnEz/btsQmDnwm64/MiQFbiNKKK9mlDmCQGyxck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuZnEz%2FbtsQmDnwm64%2FMiQFbiNKKK9mlDmCQGyxck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;241&quot; data-origin-width=&quot;1176&quot; data-origin-height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AWS CloudFront 생성 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2064&quot; data-origin-height=&quot;920&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HWzcs/btsQpzwP2bC/IbVgY1N0htb2pms0kebLOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HWzcs/btsQpzwP2bC/IbVgY1N0htb2pms0kebLOk/img.png&quot; data-alt=&quot;하위 Tenant 화면이긴 하지만.. 하나의 Distribution에 Domain 설정과 ACM 설정이 되어있는 걸 확인할 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HWzcs/btsQpzwP2bC/IbVgY1N0htb2pms0kebLOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHWzcs%2FbtsQpzwP2bC%2FIbVgY1N0htb2pms0kebLOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;267&quot; data-origin-width=&quot;2064&quot; data-origin-height=&quot;920&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;하위 Tenant 화면이긴 하지만.. 하나의 Distribution에 Domain 설정과 ACM 설정이 되어있는 걸 확인할 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. CDN Cache 정책&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.1 TTL(Time To Live) 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL은 패킷이 네트워크 내부에서 존재하도록 설정된 기간이다. 인터넷 환경에서 목적없이 라우터 &amp;lt;-&amp;gt; 라우터간 무한정으로 떠도는 유령 패킷을 방지하기 위해 설계되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CDN은 이 TTL을 사용하여 Origin 업데이트 사안을 가져오기 전에 &lt;b&gt;캐싱된 콘텐츠를 얼마나 유지할 것인지 결정&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;TTL 값이 너무 길면, Origin과 Edge의 콘텐츠 내용이 차이가 날 수 있다. 반면 TTL 값이 너무 짧으면 Edge Server에 캐싱하는 게 무의미할 수 있기에 잘 설정해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;이러한 TTL 설정은 보통 Cache-Control 헤더에서 확인할 수 있다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r0g9V/btsQpHO8DKP/ynYyOd9FMzwwksdo2G9XgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r0g9V/btsQpHO8DKP/ynYyOd9FMzwwksdo2G9XgK/img.png&quot; data-alt=&quot;헛 cloudflare의 blog의 js 파일을 열었을 때의 Cache-Control 헤더;;&amp;amp;nbsp; 31536000이면 1년 설정이다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r0g9V/btsQpHO8DKP/ynYyOd9FMzwwksdo2G9XgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr0g9V%2FbtsQpHO8DKP%2FynYyOd9FMzwwksdo2G9XgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;231&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;헛 cloudflare의 blog의 js 파일을 열었을 때의 Cache-Control 헤더;;&amp;nbsp; 31536000이면 1년 설정이다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 이렇게 설정하는 건 파일 이름에 해시값이 들어간다. 내용이 바뀌고 빌드가 되면, 파일명도 바뀐다. 사용자는 새로운 파일명을 로드하므로, 캐시와 상관없이 최신 파일을 받는다. 뭔가 업데이트가 드문 리소스의 경우에는 TTL 설정을 길게 잡고, Origin에 요청이 많이 가지 않도록 한다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.2 Cache-Control Header&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSQ6M5/btsQooW3qn2/bdgpFHT5Yt3QxKCki2QL80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSQ6M5/btsQooW3qn2/bdgpFHT5Yt3QxKCki2QL80/img.png&quot; data-alt=&quot;브라우저에서 오고가는 패킷을 살펴보면 위와 같은데, Cache-Control에 다양한 옵션들이 보인다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSQ6M5/btsQooW3qn2/bdgpFHT5Yt3QxKCki2QL80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSQ6M5%2FbtsQooW3qn2%2FbdgpFHT5Yt3QxKCki2QL80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;276&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;662&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;브라우저에서 오고가는 패킷을 살펴보면 위와 같은데, Cache-Control에 다양한 옵션들이 보인다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;public: CDN이 자유롭게 캐싱해도 문제 없는 공개 자원으로, 응답을 &lt;b&gt;모든 캐시(브라우저 &amp;amp; CDN/Proxy)에 저장 가능&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;private: 오직 &lt;b&gt;개인 캐시(브라우저)에만 저장 가능&lt;/b&gt;하고, 공유 캐시(CDN/Proxy)에는 저장 불가능&lt;/li&gt;
&lt;li&gt;max-age: Origin에서 브라우저와 Edge Server에 얼마나 캐싱할 건지 유효시간(TTL) 설정한 값
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;age: CDN POP에 캐싱된 시간(CDN POP에서 자동으로 등록하는 것)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;s-maxage: 공유 캐시(CDN/Proxy)에만 적용되는 캐시 유효 시간&lt;/li&gt;
&lt;li&gt;no-cache: 캐시에 저장은 가능하지만, 사용 전에 항상 Origin Server에 검증&lt;/li&gt;
&lt;li&gt;no-store: 아예 저장 자체 금지!&lt;/li&gt;
&lt;li&gt;must-revalidate: TTL이 지난 후, 반드시 Origin Server 검증 필요&lt;/li&gt;
&lt;li&gt;immutable: 바뀌지 않는값이니, 캐시된 걸 그대로 써라는 뜻&lt;/li&gt;
&lt;li&gt;stale-while-revalidate, stale-if-error: 만료 후에도 일정 시간은 캐시를 그대로 사용(옛 캐시 보여줌)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CDN에 대한 기본적인 내용을 정리해보았다. &lt;i&gt;TLS를 CDN에 설정하면 어떻게 될까?. Dynamic/Static CDN 등&lt;/i&gt;등 더 공부해야할 내용이 많은데.. 심화 버전으로 찾아와보도록 하겠음&lt;/p&gt;</description>
      <category>CS/Network</category>
      <category>AWS</category>
      <category>DevOps</category>
      <category>network</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/310</guid>
      <comments>https://cobinding.tistory.com/310#entry310comment</comments>
      <pubDate>Mon, 8 Sep 2025 00:11:40 +0900</pubDate>
    </item>
    <item>
      <title>[Redis] Redis 7.x &amp;rarr; Valkey 8.x 마이그레이션 PoC</title>
      <link>https://cobinding.tistory.com/309</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis Laps가 SSPL로 전환하면서, Redis 7.x 버전을 fork한 Valkey Engine 프로젝트가 시작되었다. Valkey 7.x는 Redis 7.x를 그대로 fork한 것과 다름 없어서, 마이그레이션을 할 때 클라이언트 라이브러리를 변경하는 것 이외에는 크게 신경 쓸 점이 없다. 하지만 Valkey 8.x Engine부터는 Redis와 꽤 다른 지점이 생긴다. 직접 ElastiCache Redis, Valkey Engine을 생성해서 마이그레이션 과정을 정리해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 기본적인 읽기 전용 Cache&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 환경 정보&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;- Node Type: cache.t4g.small&lt;br /&gt;- Cluster Mode: Enabled&lt;br /&gt;- Shards/Nodes: 1개 / 2개&lt;br /&gt;- Encryption in transit/rest: Disabled&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 ElastiCache Redis에 CLI를 통해 데이터 넣기 &amp;amp; 기본 정보 확인&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;ElastiCache Redis &amp;rarr; Valkey 마이그레이션 전후 비교를 위해서 기본 정보를 확인해본다.&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반복문으로 데이터 넣기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1755940773248&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for i in {1..100000}; do
  redis-cli -h &amp;lt;endpoint&amp;gt; SET &quot;key:$i&quot; &quot;value:$i&quot;
done&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터가 잘 들어갔는지 확인(DBSIZE, GET KEY)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1594&quot; data-origin-height=&quot;366&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cowUBi/btsP4jILHjO/wFyDrXW1oXLGXKt5eyVVAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cowUBi/btsP4jILHjO/wFyDrXW1oXLGXKt5eyVVAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cowUBi/btsP4jILHjO/wFyDrXW1oXLGXKt5eyVVAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcowUBi%2FbtsP4jILHjO%2FwFyDrXW1oXLGXKt5eyVVAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;138&quot; data-origin-width=&quot;1594&quot; data-origin-height=&quot;366&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Cluster Mode Info 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;349&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cj3peR/btsP3ysQKCK/JPy0bVJWtyRKejIujGxeT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cj3peR/btsP3ysQKCK/JPy0bVJWtyRKejIujGxeT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cj3peR/btsP3ysQKCK/JPy0bVJWtyRKejIujGxeT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcj3peR%2FbtsP3ysQKCK%2FJPy0bVJWtyRKejIujGxeT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;337&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;349&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Replication Info 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;285&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oRDDI/btsP5hjgJpn/Mg6bEEZMRZRJTFK0xKlZx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oRDDI/btsP5hjgJpn/Mg6bEEZMRZRJTFK0xKlZx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oRDDI/btsP5hjgJpn/Mg6bEEZMRZRJTFK0xKlZx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoRDDI%2FbtsP5hjgJpn%2FMg6bEEZMRZRJTFK0xKlZx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;222&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;285&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- Replica(=Slave)&lt;/b&gt; 노드가 1개 붙어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- lag=0:&lt;/b&gt; Primary와 Replica 사이의 지연 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 현재 Failover 상황 아님!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- offset:&lt;/b&gt; 복제 로그의 위치를 뜻한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2 redis-benchmark를 통한 GET 요청(읽기) 성능 테스트&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;redis-benchmark 명령어를 통해 간단하게 성능을 비교해본다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1755941278600&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;redis-benchmark -h &quot;$H&quot; -p 6379 -n &amp;lt;총 요청 수&amp;gt; -c &amp;lt;동시 연결 수&amp;gt; -t &amp;lt;테스트할 명령&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;884&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dy0XLn/btsP32UOD2R/EcjHDYaSKannR7gnmwTgX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dy0XLn/btsP32UOD2R/EcjHDYaSKannR7gnmwTgX0/img.png&quot; data-alt=&quot;100,000 번의 GET 요청으로 부하 테스트를 진행한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dy0XLn/btsP32UOD2R/EcjHDYaSKannR7gnmwTgX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdy0XLn%2FbtsP32UOD2R%2FEcjHDYaSKannR7gnmwTgX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;156&quot; data-origin-width=&quot;884&quot; data-origin-height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;100,000 번의 GET 요청으로 부하 테스트를 진행한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;100,000개의 key를 넣고 성능 테스트를 했을 때, 결과를 해석해보면,&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;QPS(초당 처리량)&lt;/b&gt;은 3313.45로, 1초 당 약 3300개의 요청을 처리했다는 뜻이다. 네트워크 왕복 시간까지 합쳐서 계산하게 되는데, 나는 로컬 PC에서 redis-benchmark를 수행하였고, 실제 같은 VPC에 존재하는 인스턴스에서 실행하면 더 빠른 성능을 보일 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지연시간 분포(percentile)&lt;/b&gt; 중 50퍼센타일. 즉, 전체 요청 중 절반은 약 14.9ms 이하로 걸렸다는 뜻이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1756031667223&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;redis-benchmark -h &quot;$H&quot; -n 100000 -c 50 -t get&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1658&quot; data-origin-height=&quot;938&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AD3PS/btsP3bYZNZW/kAfZsC3cTaVX8Zikngx1oK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AD3PS/btsP3bYZNZW/kAfZsC3cTaVX8Zikngx1oK/img.png&quot; data-alt=&quot;50개의 클라이언트로 100,000 번의 GET 요청 부하 테스트를 진행한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AD3PS/btsP3bYZNZW/kAfZsC3cTaVX8Zikngx1oK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAD3PS%2FbtsP3bYZNZW%2FkAfZsC3cTaVX8Zikngx1oK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;339&quot; data-origin-width=&quot;1658&quot; data-origin-height=&quot;938&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;50개의 클라이언트로 100,000 번의 GET 요청 부하 테스트를 진행한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;-c: 동시에 클라이언트 N(50) 개를 생성하여 병렬로 요청을 보내는 것처럼하는 옵션이다.&lt;/li&gt;
&lt;li&gt;-t: 테스트할 명령어를 지정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;100,000개의 key를 넣고, 50개의 클라이언트를 만들어서&amp;nbsp; 성능 테스트를 했을 때, 결과를 해석해보면,&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;총 10만 건의 요청을 126.48초에 처리했다. 이는 약 &lt;b&gt;790 QPS(초당 처리량) 수준&lt;/b&gt;이다.&lt;/li&gt;
&lt;li&gt;동시에 50개 클라이언트 연결을 유지했다.&lt;/li&gt;
&lt;li&gt;각 값(value)는 3 byte 문자열&lt;/li&gt;
&lt;li&gt;대부분의 요청(p99) 이 0.1초 이내(280ms)에 처리되었다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.3 TTL 값 확인&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Redis 엔진에서 Key의 수명을 설정하고, Valkey로 마이그레이션 했을 때 이 TTL이 유효한지 검증해본다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zgGmK/btsP5nDMUuW/diyiyENwR1sXfhTK8HduG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zgGmK/btsP5nDMUuW/diyiyENwR1sXfhTK8HduG1/img.png&quot; data-alt=&quot;key: 12, {1..5}에 대한 TTL 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zgGmK/btsP5nDMUuW/diyiyENwR1sXfhTK8HduG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzgGmK%2FbtsP5nDMUuW%2FdiyiyENwR1sXfhTK8HduG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;245&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;key: 12, {1..5}에 대한 TTL 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TTL(Time To Live)&lt;/b&gt;는 키의 &lt;b&gt;남은 수명&lt;/b&gt;을 보여준다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;양수: 해당 키가 만료되기까지 남은 초&lt;/li&gt;
&lt;li&gt;-1 : 키가 존재하지만, 만료 시간 설정이 없음. 즉, 영구 키를 뜻한다.&lt;/li&gt;
&lt;li&gt;-2: 키가 존재하지 않음. 즉, 이미 만료되었거나 삭제되었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1414&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OX1zD/btsP2K1BvdP/4vIjaRzdKOCGpJqz6eqLp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OX1zD/btsP2K1BvdP/4vIjaRzdKOCGpJqz6eqLp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OX1zD/btsP2K1BvdP/4vIjaRzdKOCGpJqz6eqLp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOX1zD%2FbtsP2K1BvdP%2F4vIjaRzdKOCGpJqz6eqLp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;233&quot; data-origin-width=&quot;1414&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Valkey로 마이그레이션 이후 이 TTL 설정이 잘 남아있는지 확인하기 위해 특정&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;키(500)에 대해서 TTL(expire)을 설정했다. 초 단위로 TTL을 설정할 수 있기에, &lt;b&gt;604800초 == 7일&lt;/b&gt;로 부여했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Redis 7.x &amp;rarr; Valkey 8.x 마이그레이션 이후, 지표 확인해보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis 7.x &amp;rarr; Valkey 8.x 전환에는 boto3의 modify_replication_group API를 사용하였다. parameter_group 또한 같은 워크플로우 내에서 API로 변경하였다. 사내 배포 플랫폼을 통해 변경하였기 때문에 이 부분은 내부 시스템에만 적용된다는 점.. API 이외에도 콘솔에서 Modify하거나 Upgrade to Valkey를 하면되는데, 이 각 방법의 차이점은 AWS Support 미팅 이후에 추가해볼 예정이다!&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2.1 CLI를 통해 Valkey Engine 기본 정보 확인&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Redis Engine에서 Valkey Engine으로 마이그레이션하고, 데이터 변동 사항을 redis-cli를 통해 확인해본다.&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터가 잘 있는지 확인(DBSIZE, GET KEY)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxKcV7/btsP2ZxOxjh/7RWxpMEb879dYcXJ0YOAB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxKcV7/btsP2ZxOxjh/7RWxpMEb879dYcXJ0YOAB0/img.png&quot; data-alt=&quot;오 Key-Value 쌍이 문제 없이 잘 남아있는 모습 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxKcV7/btsP2ZxOxjh/7RWxpMEb879dYcXJ0YOAB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxKcV7%2FbtsP2ZxOxjh%2F7RWxpMEb879dYcXJ0YOAB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;166&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;오 Key-Value 쌍이 문제 없이 잘 남아있는 모습 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Cluster Mode Info 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/umXyi/btsP5HB5t35/q5va40hQrqtRRczacynmp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/umXyi/btsP5HB5t35/q5va40hQrqtRRczacynmp0/img.png&quot; data-alt=&quot;Cluster State ok에 Node도 2개로 잘 마이그레이션된 점을 확인할 수 있다. Redis일 때와 크게 다른 점은 없어 보인다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/umXyi/btsP5HB5t35/q5va40hQrqtRRczacynmp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FumXyi%2FbtsP5HB5t35%2Fq5va40hQrqtRRczacynmp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;291&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Cluster State ok에 Node도 2개로 잘 마이그레이션된 점을 확인할 수 있다. Redis일 때와 크게 다른 점은 없어 보인다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Replication Info 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;297&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf7U30/btsP6fFl0ti/i69dY5DBslQSiZLrBzWHb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf7U30/btsP6fFl0ti/i69dY5DBslQSiZLrBzWHb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf7U30/btsP6fFl0ti/i69dY5DBslQSiZLrBzWHb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf7U30%2FbtsP6fFl0ti%2Fi69dY5DBslQSiZLrBzWHb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;222&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;297&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Valkey로 in-place 전환 즉, 엔진 교체가 이루어지면서 Primary Node의 재시작이 일어나서 master_replid가 재생성된 점을 확인할 수 있다. 여기서 &lt;b&gt;second_repl_offset이 마이그레이션 이전에는 -1이었는데, 그 이유는 Redis Engine에서는 재시작이나 failover가 없었기 때문이다. Valkey로 엔진 교체를 하면서 replid가 한번 바뀌었고, 이 점이 반영되어 second_repl_offset 값이 바뀌었다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Master: Primary Node&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;second_repl_offset: 이전 master_replid가 마지막으로 유효했던 offset 위치)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2.2 redis-benchmark를 통한 GET 요청(읽기) 성능 테스트&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Redis Engine에서 Valkey Engine으로 마이그레이션 한 후, 간단한 성능테스트를 통해 두 엔진 간 차이를 실제로 확인해본다. &lt;br /&gt;오..? 사실 크게 기대 안했는데 읽기 성능이 월등히 좋아진 점이 확인된다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYkT1e/btsP3uRQOkF/moDIABeCrDJtjLeoCobq71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYkT1e/btsP3uRQOkF/moDIABeCrDJtjLeoCobq71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYkT1e/btsP3uRQOkF/moDIABeCrDJtjLeoCobq71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYkT1e%2FbtsP3uRQOkF%2FmoDIABeCrDJtjLeoCobq71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;382&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;10만 건 요청을 Redis Engine에 비해서 &lt;b&gt;약 6배 정도(126초 &amp;rarr; 28초)&lt;/b&gt; 더 빠르게 처리한 점을 확인할 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;대부분의 요청(p99)이 46ms 정도로 처리되었고, 이 또한 이전 테스트에 비해서 &lt;b&gt;약 7배 정도(280ms &amp;rarr; 46ms)&lt;/b&gt; 성능이 좋아졌다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2.3 TTL 값 확인&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Redis Engine에 설정한 Key TTL이 마이그레이션 이후에도 잘 남아있는지 확인해본다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;552&quot; data-origin-height=&quot;98&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctZvQg/btsP4kVrRaQ/kErohgjtoUhPk42ZeKxre0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctZvQg/btsP4kVrRaQ/kErohgjtoUhPk42ZeKxre0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctZvQg/btsP4kVrRaQ/kErohgjtoUhPk42ZeKxre0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctZvQg%2FbtsP4kVrRaQ%2FkErohgjtoUhPk42ZeKxre0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;107&quot; data-origin-width=&quot;552&quot; data-origin-height=&quot;98&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 설정한 TTL 이후에 한 8시간(28,800초)이 지났으니까 Key TTL 설정값도 문제없이 마이그레이션 된 점을 확인할 수 있다..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;알게된 점&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Redis -&amp;gt; Valkey Engine을 직접 마이그레이션하면서 기본 정보들을 알아보았다. 실제로 직접 해보는 거랑 문서로 보는 거랑은 차이가 많이 나서, 기본적이지만 베이스가 되는 것들을 직접 살펴보니 와닿는 것도 많았다. Valkey가 진짜 성능이 좋은게 확 느껴졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 redis-cli 명령어 사용법도 익힐 수 있었고, TTL 설정이나 Primary Node의 재시작 관점도 눈여겨 볼 수 있었다. 사실 Redis/Valkey Client 변경 사항이 가장 클 것같은데... 이점은 AWS Support 미팅에서 잘 챙겨봐야겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DB/In-Memory DB</category>
      <category>AWS</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/309</guid>
      <comments>https://cobinding.tistory.com/309#entry309comment</comments>
      <pubDate>Mon, 25 Aug 2025 00:33:46 +0900</pubDate>
    </item>
    <item>
      <title>[Redis] 캐시와 Redis, ElastiCache</title>
      <link>https://cobinding.tistory.com/308</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 &lt;span style=&quot;color: #ef5369;&quot;&gt;ElastiCache&lt;/span&gt;와 관련한 하나의 에픽을 마무리하게 되었다. AWS 셀프 서비스를 위해서 작업에 집중했는데, 본격적인 사내 개발자 사용자 가이드와 안내 문서를 작성하기에 앞서 &lt;span style=&quot;color: #ef5369;&quot;&gt;캐시와 Redis, ElastiCache&lt;/span&gt;, 더 나아가 Redis &amp;gt; Valkey &lt;span style=&quot;color: #ef5369;&quot;&gt;엔진별 특성/ 마이그레이션 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;내용을 정확히&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;공부하고자, 블&lt;/span&gt;로그 글을 통해 정리해보려고 한다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;그래서 다음과 같은 순서로 글을 쭉 작성하며 정리할 예정!&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐싱 개념 | Redis &amp;amp; ElastiCache&lt;/li&gt;
&lt;li&gt;Python으로 통신하면서 단일 노드 ElastiCache 실습해보기&lt;/li&gt;
&lt;li&gt;클러스터 모드의 ElastiCache 사용하면서 구조 파악하기&lt;/li&gt;
&lt;li&gt;Redis, Valkey 각 엔진을 생성하고, 성능 및 비용 비교하기&lt;/li&gt;
&lt;li&gt;Redis, Valkey 엔진별 차이점 파악하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이번 포스팅은 첫 번째 단계로, 일단 캐싱과 Redis, ElastiCache에 대해 이해해보려 한다.&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 메모리와 캐시&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1468&quot; data-origin-height=&quot;1146&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MWWMt/btsQ6V7UnbJ/ZCVuFVbnWL9MUpsb9yXimK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MWWMt/btsQ6V7UnbJ/ZCVuFVbnWL9MUpsb9yXimK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MWWMt/btsQ6V7UnbJ/ZCVuFVbnWL9MUpsb9yXimK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMWWMt%2FbtsQ6V7UnbJ%2FZCVuFVbnWL9MUpsb9yXimK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;546&quot; data-origin-width=&quot;1468&quot; data-origin-height=&quot;1146&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시는 자주 접근하는 데이터를 빠르게 제공하기 위해 임시 저장소에 보관하는 방법이다. 데이터를 매번 원본 저장소(RDB와 같은 디스크)에서 가져오면 &lt;b&gt;네트워크 통신, 디스크 I/O 등의 비용이 발생&lt;/b&gt;한다. 이를 줄이기 위해서 조금 더 빠른 저장소에 &lt;b&gt;데이터 사본&lt;/b&gt;을 저장해두고, 요청시 바로 꺼내쓴다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1&amp;nbsp; 우리가 캐시 이야기를 하면 보통 Redis를 바로 떠올리게 되는데, 그 이유는 뭘까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;먼저 로컬 캐시와 원격 캐시의 차이를 살펴보자.&lt;/b&gt;&lt;br /&gt;웹 애플리케이션 서버에 직접 캐시를 저장해서 로컬 캐시로 사용하면, 비교적 구현이 쉽고 빠르며, 작은 작업에서는 상당한 개선 효과를 볼 수 있다. 우선 외부 I/O 통신이 없기 때문에 응답 속도가 훨씬 빠르다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;근래의 서비스들은 고가용성을 위해 동일한 서버를 2대 이상 운영하는 경우가 많다. 이때, 로컬 캐시를 이용하면 각 서버 간 데이터 정합성 문제를 일으킬 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;1452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NMgDS/btsPMSdyHAU/gFDkehoPNQ7PM4jVpDnSsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NMgDS/btsPMSdyHAU/gFDkehoPNQ7PM4jVpDnSsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NMgDS/btsPMSdyHAU/gFDkehoPNQ7PM4jVpDnSsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNMgDS%2FbtsPMSdyHAU%2FgFDkehoPNQ7PM4jVpDnSsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;645&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;1452&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;자주 변화하지 않는 데이터에 대한 잦은 조회는 로컬 캐시를 사용하는 것이 좋다. 각 서버 간 데이터를 실시간으로 완벽히 동기화하는 것이 요구된다면 데이터 최신화를 위한 Redis를 사용한다. [&lt;a href=&quot;https://tech.kakaopay.com/post/local-caching-in-distributed-systems/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Pay Tech : 분산 시스템에서 로컬 캐시 활용하기]&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2 캐시 주요 지표&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Cache Hit Ratio
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;요청 중 캐시에서 바로 응답한 비율이다. 하나의 호출 결과를 여러 요청 또는 작업에 사용할 수 있을 경우 사용하는 게 좋다. 각 요청에서 종속 서비스에 대해 고유한 쿼리를 실행하고, 요청 별로 결과값이 달라진다면 캐시 히트 적중률이 낮으므로, 캐시를 통해 얻을 수 있는 이점이 적다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Latency
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;요청에서 응답까지 걸린 시간을 의미한다. 캐시 히트를 통해 응답을 받는다면 응답 지연 시간은 당연히 작은 값을 띈다. 캐시 미스가 많이 나면, 응답 지연 시간은 늘어난다. 이러한 경우에는 캐시 미스 경로 최적화 등을 고려할 수 있다.&lt;/li&gt;
&lt;li&gt;예컨대, AWS에서는 ElastiCache Valkey 엔진에 대해 &lt;span style=&quot;color: #ef5369;&quot;&gt;SuccessfuleWriteRequestLatency&lt;/span&gt;와 같은 지표를 제공한다. 이 메트릭은 1분 간격으로 성공적으로 실행된 &lt;b&gt;모든 쓰기 요청을 처리하는데 걸리는 시간&lt;/b&gt;을 의미한다.&lt;/li&gt;
&lt;li&gt;읽어볼 자료: &lt;a href=&quot;https://aws.amazon.com/ko/blogs/database/monitor-server-side-latency-for-amazon-elasticache-for-valkey/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;aws valkey latency metric&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Evictions
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;캐시는 데이터의 복사본이므로, 캐시 메모리가 부족해지면 삭제 후 나중에 다시 캐시하는 등의 작업을 수행한다. Redis에서는 Evictions 설정을 통해, &lt;b&gt;캐싱된 데이터의 크기가 메모리 크기를 초과할 때, 키를 제거하는 정책을 지정&lt;/b&gt;할 수 있다.&lt;/li&gt;
&lt;li&gt;Expiration이랑 헷갈리면 안된다! Expired는 TTL 만료로 자연 삭제된 것이고, Evictions는 메모리 부족으로 인해 정책에 따라 강제로 제거된 키를 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;rate(redis_evicted_keys_total[5m]) # 메모리 초과로 인해 삭제된 키를 5분마다 측정해서 통계
rate(redis_expired_keys_total[5m]) # TTL 만료로 인해 삭제된 키를 5분마다 측정해서 통계&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.3 캐싱 전략&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시를 이용함에 있어서 주의 깊게 다뤄야 하는 것은 바로 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;데이터 정합성(캐시 일관성) 문제&lt;/b&gt;&lt;/span&gt;다. 앞서 Hit Ratio에서 언급한 것처럼, 어떤 종속 서비스에 대해서 하나의 호출 결과를 여러 요청 또는 작업에 활용할 수 있는 경우에 캐시를 쓰는 것이 좋다.&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;시간이 지나면서 캐싱된 데이터는 source(DB)와의 일관성이 점점 떨어진다. &lt;/span&gt;(cdn에서 invalidation을 하는 이유)&lt;br /&gt;따라서, 원본 데이터의 변경률과 데이터 캐싱을 잘 관리해서 데이터 정합성 문제를 일으키지 않으려면 이러한 캐싱 전략이 중요하다.&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;캐싱 전략에는 3 가지 패턴이 있다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐시 읽기 전략(Read Cache Strategy)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Cache-aside(Lazy Loading)&lt;/li&gt;
&lt;li&gt;Read-through&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;캐시 쓰기 전략(Wrirte Cachce Strategy)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Write-through&lt;/li&gt;
&lt;li&gt;Write-arount&lt;/li&gt;
&lt;li&gt;Write-back (Write-behid)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 서비스는 Read - Write 조합을 활용한다. 캐싱 전략은 여기서 깊이 있게 다루기 보다는, 따로 정리해볼 예정이다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Redis(Remote Dictionary Server)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레디스는 메모리 기반의 Key-Value 데이터 저장소다. Table 구조로 데이터가 저장되는 RDBMS와 달리, 비관계형 구조로서 데이터를 단순히 키밸류 쌍으로 저장한다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;In-memory: 데이터를 디스크가 아니라 메인 메모리(RAM)에 저장하기에, 지연 시간이 훨씬 짧고 처리량이 높다.&lt;/li&gt;
&lt;li&gt;Remote Data Storage이기에, 여러 서버에서 같은 데이터를 공유할 수 있다.&lt;/li&gt;
&lt;li&gt;다양한 자료구조를 지원한다. (String, Set, Hashes, List...)&lt;/li&gt;
&lt;li&gt;쓰기 성능 증대를 위한 클라이언트 측 샤딩을 지원한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Sharding: 같은 테이블 스키마를 가진 데이터(row)를 다수의 db에 분산해서 저장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;레디스는 기본적으로 싱글 스레드로 수행되기 때문에, 안정적인 인프라를 구축하기 위해서는 Replication 구조가 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. ElastiCache 살펴보기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;992&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEki3l/btsPNaegg66/MmhSpWeyIfZtNRMQnQ2vTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEki3l/btsPNaegg66/MmhSpWeyIfZtNRMQnQ2vTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEki3l/btsPNaegg66/MmhSpWeyIfZtNRMQnQ2vTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEki3l%2FbtsPNaegg66%2FMmhSpWeyIfZtNRMQnQ2vTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;400&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;992&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cluster Mode&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드는 캐시 엔진을 실행하는 서버다. 클러스터는 이러한 노드들을 모아놓은 그룹으로, 전체 캐시 시스템이다. 노드는 Primary Node와 Replica Node로 나뉘는데, 보통 Primary Node는 쓰기 전용, Replica Node는 읽기 전용으로 사용된다. 노드는 독립적으로 데이터를 가지고 있고(하나의 독립적인 인스턴스), 필요한 경우 다른 노드들과 통신한다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;하나의 샤드는 Primary Node와 Replica Node로 구성된다. Replica Node는 FailOver를 위해 Primary Node를 복제하여 대체되기도 한다. 샤드 수를 늘려서 처리량(Throughput)과 저장 용량을 증가할 수 있다. 또한, 여러 개의 Primary Node를 통해 읽기/쓰기를 분산하여 병렬 처리를 통한 이점을 얻을 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1612&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cz8R6o/btsPWdAThIH/aSIbeHHddukygfNkQxtkFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cz8R6o/btsPWdAThIH/aSIbeHHddukygfNkQxtkFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cz8R6o/btsPWdAThIH/aSIbeHHddukygfNkQxtkFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcz8R6o%2FbtsPWdAThIH%2FaSIbeHHddukygfNkQxtkFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;143&quot; data-origin-width=&quot;1612&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Multi AZ(Availability Zone)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고가용성(HA) 확보를 위해 여러 Zone에 노드를 분산하는 방식이다. Primary Node와 Replica Node를 서로 다른 Zone에 배치하여, AZ 장애가 발생했을 때 대응 체계를 마련한다.&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;열심히 공부했던 것들이 스터디에서만 빛을 발하는 슬픈 현실;ㅠ&amp;nbsp;일단 이번에 Redis, Valkey 업무를 맡으면서 이쪽은 아예 정복을 해야겠다는 다짐을 했다. 캐쉬 이야기 2탄도 곧 작성해보겠다.&lt;/p&gt;</description>
      <category>DB/In-Memory DB</category>
      <category>AWS</category>
      <category>DevOps</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/308</guid>
      <comments>https://cobinding.tistory.com/308#entry308comment</comments>
      <pubDate>Sat, 16 Aug 2025 08:57:44 +0900</pubDate>
    </item>
    <item>
      <title>[Git] Git Rebase 학습하고 실습해보기</title>
      <link>https://cobinding.tistory.com/307</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git rebase를 실무에서 여러 브랜치에 적용하려니까 헷갈리는 점이 많아서 이번 기회에 git 공부를 확실히 하고자 블로그를 작성하게 되었다. 이 포스팅에서는 git 명령어 뿐만 아니라 &lt;a title=&quot;fork&quot; href=&quot;https://git-fork.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;fork&lt;/a&gt;라는 도구도 함께 사용하며 익힐 것!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. git rebase&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git rebase는 말 그대로 &lt;b&gt;브랜치의 base를 다시(re) 설정&lt;/b&gt;한다. git rebase를 통해 커밋 그래프를 단순하게 가져가고, 의미 있는 커밋들만 남겨서 더 원활한 협업을 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;656&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbS05y/btsPEw94GBH/uLQUXTeWZAWZIstFQ3DMUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbS05y/btsPEw94GBH/uLQUXTeWZAWZIstFQ3DMUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbS05y/btsPEw94GBH/uLQUXTeWZAWZIstFQ3DMUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbS05y%2FbtsPEw94GBH%2FuLQUXTeWZAWZIstFQ3DMUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;276&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;656&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 master 브랜치의 a3 커밋을 베이스로 작업을 진행하고 있고, master 브랜치는 또 다른 작업들이 진행된 상황이다. 이 master 브랜치의 최신 작업 내용을, 내가 현재 작업 중인 feature 브랜치에도 적용하고 싶을 때 rebase 명령어를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;보통 main 브랜치에 merge하기 이전에, rebase 작업이 요구된다.&lt;/b&gt; 이유는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내 feature 브랜치의 커밋을 최신 main 위로 재적용해서, 마치 main에서 방금 브랜치를 만든 것처럼 히스토리를 정리한다.&lt;/li&gt;
&lt;li&gt;충돌 방지 &amp;amp; 문제가 발생하더라도 미리 rebase 과정을 통해 해결할 수 있다.&lt;/li&gt;
&lt;li&gt;rebase -i HEAD로 커밋을 깔끔하게 정리할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 다음과 같은 방법으로 rebase를 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1754198046068&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# main 브랜치로 이동
git checkout main
# 최신 main 적용
git fetch origin 
git pull origin main
# feature 브랜치로 이동
git checkout feature
# main을 base로 rebase 실행
git rebase main&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 거치면 git history가 다음과 같이 정리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1594&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqEo1r/btsPDWnFy1I/MfQYirpYEO3DsJ2qgpy8WK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqEo1r/btsPDWnFy1I/MfQYirpYEO3DsJ2qgpy8WK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqEo1r/btsPDWnFy1I/MfQYirpYEO3DsJ2qgpy8WK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqEo1r%2FbtsPDWnFy1I%2FMfQYirpYEO3DsJ2qgpy8WK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;155&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1594&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. fork로 rebase main 실습해보기&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4xHsR/btsPENKznPA/BxcLoYtD0VWi5yjHWrlMDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4xHsR/btsPENKznPA/BxcLoYtD0VWi5yjHWrlMDK/img.png&quot; data-alt=&quot;의도적으로 만든 상황: 그래프 잘보기ㅜ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4xHsR/btsPENKznPA/BxcLoYtD0VWi5yjHWrlMDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4xHsR%2FbtsPENKznPA%2FBxcLoYtD0VWi5yjHWrlMDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;81&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;196&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;의도적으로 만든 상황: 그래프 잘보기ㅜ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 Initial commit에서 로컬 feature 브랜치가 분기&lt;/li&gt;
&lt;li&gt;로컬 feature-001에서 작업 후 add/commit, origin push는 X&lt;/li&gt;
&lt;li&gt;origin main 변경사항 있음!&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;fork에서 제공하는 git history 그래프를 잘 활용하면 좋은데, 지금 origin/main이 local/feature-001보다 앞선 상황이다. 즉, 로컬브랜치가 origin main pull 했을 당시보다 origin 작업이 진행된 것.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;766&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p39Mm/btsPDGL99ug/DrRTUY57oq0mM2gB8pjA8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p39Mm/btsPDGL99ug/DrRTUY57oq0mM2gB8pjA8K/img.png&quot; data-alt=&quot;요기서 main에 rebase를 하면(=내 로컬 변경 사항을 오리진 main 위로 올리기)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p39Mm/btsPDGL99ug/DrRTUY57oq0mM2gB8pjA8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp39Mm%2FbtsPDGL99ug%2FDrRTUY57oq0mM2gB8pjA8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;400&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;766&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;요기서 main에 rebase를 하면(=내 로컬 변경 사항을 오리진 main 위로 올리기)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2256&quot; data-origin-height=&quot;998&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8fh4H/btsPDWusHLN/BN5rBUGH5MweVRrR8ONp41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8fh4H/btsPDWusHLN/BN5rBUGH5MweVRrR8ONp41/img.png&quot; data-alt=&quot;옵션 뜻: 현재 워킹 디렉토리에 수정 사항이 있을 경우, 그것을 임시로 stash해놓고 rebase가 끝난 후 다시 적용하는 옵션으로 rebase를 안전하게 진행할 수 있게 해줌&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8fh4H/btsPDWusHLN/BN5rBUGH5MweVRrR8ONp41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8fh4H%2FbtsPDWusHLN%2FBN5rBUGH5MweVRrR8ONp41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;310&quot; data-origin-width=&quot;2256&quot; data-origin-height=&quot;998&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;옵션 뜻: 현재 워킹 디렉토리에 수정 사항이 있을 경우, 그것을 임시로 stash해놓고 rebase가 끝난 후 다시 적용하는 옵션으로 rebase를 안전하게 진행할 수 있게 해줌&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2236&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NYFBG/btsPEuLixU6/G1VKA8Yr1VDoQpnyP4IcD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NYFBG/btsPEuLixU6/G1VKA8Yr1VDoQpnyP4IcD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NYFBG/btsPEuLixU6/G1VKA8Yr1VDoQpnyP4IcD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNYFBG%2FbtsPEuLixU6%2FG1VKA8Yr1VDoQpnyP4IcD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;58&quot; data-origin-width=&quot;2236&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rebase 이후에 깔끔해진 분기 모습!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 push --force를 내 로컬 브랜치에 하면, origin main 변경 사항 위에 커밋들이 깔끔하게 올라가는 걸 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2234&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beke6t/btsPDN5pFl5/327NLE4AudcwkkR6kZ1GC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beke6t/btsPDN5pFl5/327NLE4AudcwkkR6kZ1GC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beke6t/btsPDN5pFl5/327NLE4AudcwkkR6kZ1GC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbeke6t%2FbtsPDN5pFl5%2F327NLE4AudcwkkR6kZ1GC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;53&quot; data-origin-width=&quot;2234&quot; data-origin-height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 깔끔하게 진행하려고 local main - orign main도 싱크 맞춰줌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1956&quot; data-origin-height=&quot;1298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHt13f/btsPF5XOkRU/ITvtk8RaouwYINsx3VpVZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHt13f/btsPF5XOkRU/ITvtk8RaouwYINsx3VpVZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHt13f/btsPF5XOkRU/ITvtk8RaouwYINsx3VpVZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHt13f%2FbtsPF5XOkRU%2FITvtk8RaouwYINsx3VpVZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;465&quot; data-origin-width=&quot;1956&quot; data-origin-height=&quot;1298&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rebase로 정리된 상태에서 force push 해보면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2312&quot; data-origin-height=&quot;1676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EqS1e/btsPHlFSihi/RUgx1N6VCsuIw0ISFRxu7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EqS1e/btsPHlFSihi/RUgx1N6VCsuIw0ISFRxu7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EqS1e/btsPHlFSihi/RUgx1N6VCsuIw0ISFRxu7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEqS1e%2FbtsPHlFSihi%2FRUgx1N6VCsuIw0ISFRxu7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;507&quot; data-origin-width=&quot;2312&quot; data-origin-height=&quot;1676&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요렇게 PR을 깔끔하게 생성할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1804&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Leqo5/btsPEvJ60sz/yMMKUAaqe7r4qwVAfUa921/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Leqo5/btsPEvJ60sz/yMMKUAaqe7r4qwVAfUa921/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Leqo5/btsPEvJ60sz/yMMKUAaqe7r4qwVAfUa921/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLeqo5%2FbtsPEvJ60sz%2FyMMKUAaqe7r4qwVAfUa921%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;221&quot; data-origin-width=&quot;1804&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PR이 머지되면 rebase된 내용 또한 잘 반영되어있는 걸 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;rebase 이후에는 왜 force push를 해야할까?&lt;/h4&gt;
&lt;p data-end=&quot;844&quot; data-start=&quot;809&quot; data-ke-size=&quot;size16&quot;&gt;Rebase는 &quot;히스토리를 다시 쓰는 작업&quot;이기 때문이다!&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1011&quot; data-start=&quot;846&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;879&quot; data-start=&quot;846&quot;&gt;커밋 내용이 같아 보여도 &lt;b&gt;커밋 ID가 새로 생성됨&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;914&quot; data-start=&quot;880&quot;&gt;그래서 기존 원격 브랜치의 히스토리와 &lt;b&gt;일치하지 않음&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;956&quot; data-start=&quot;915&quot;&gt;Git은 이걸 &amp;ldquo;위험한 덮어쓰기&amp;rdquo;라고 생각해서, &lt;b&gt;기본 푸시 거부&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1011&quot; data-start=&quot;957&quot;&gt;대신 --force를 사용해서 &lt;b&gt;&quot;내 히스토리로 강제로 덮어쓰겠다&quot;&lt;/b&gt; 라고 명시해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. rebase로 내 로컬 커밋 깔끔하게 정리하기(rebase -i)&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(작성 예정)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 알게된 점&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;origin feature branch에 push 하기전에 main과 rebase를 하자.&lt;/li&gt;
&lt;li&gt;main rebase 이전에는 local main - origin main 요렇게도 fetch를 하자&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>회고 &amp;amp; 후기/개발 일지</category>
      <category>Git</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/307</guid>
      <comments>https://cobinding.tistory.com/307#entry307comment</comments>
      <pubDate>Sun, 3 Aug 2025 15:02:52 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] CloudWatch 비용 숨바꼭질 : per metric-month</title>
      <link>https://cobinding.tistory.com/303</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 KAU 크레딧이 끝나서 운영 비용을 줄일 방법을 다시 고안 중이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;청구서를 유심히 보다가 발견한 &lt;b&gt;CloudWatch 비용&lt;/b&gt;...!&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1562&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byHROI/btsLog3G88D/E02Y47MUikoaFt86r35zO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byHROI/btsLog3G88D/E02Y47MUikoaFt86r35zO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byHROI/btsLog3G88D/E02Y47MUikoaFt86r35zO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyHROI%2FbtsLog3G88D%2FE02Y47MUikoaFt86r35zO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;186&quot; data-origin-width=&quot;1562&quot; data-origin-height=&quot;484&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CloudWatch Agent를 EC2에 설치하면서 구성파일을 만든다. 이때 Agent가 수집할 내용을 설정했기에 발생하는 비용이다. 한 달에 $ 2 정도 청구되고 있는데 현재는 CloudWatch Agent를 사용하지 않으므로 설정만 깔끔하게 없애주었다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CloudWatch Agent 파일 접근&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;에이전트 설정 파일 위치 :&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #f6e199; color: #ee2323;&quot;&gt;/opt/aws/amazon-clouwatch-agent/bin/config.json&lt;/span&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;파일 접근&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1734539404981&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo vi /opt/aws/amazon-cloudwatch-agent/bin/config.json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;파일 내용 및 삭제&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1734539321150&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
        &quot;agent&quot;: {
                &quot;metrics_collection_interval&quot;: 60,
                &quot;run_as_user&quot;: &quot;cwagent&quot;
        },
        &quot;logs&quot;: {
                &quot;logs_collected&quot;: {
                        &quot;files&quot;: {
                                &quot;collect_list&quot;: [
                                        {
                                                &quot;file_path&quot;: &quot;/var/log/messages&quot;,
                                                &quot;log_group_class&quot;: &quot;STANDARD&quot;,
                                                &quot;log_group_name&quot;: &quot;koala-admin-dev&quot;,
                                                &quot;log_stream_name&quot;: &quot;i-0a1e93421c6feb72e&quot;,
                                                &quot;retention_in_days&quot;: -1
                                        }
                                ]
                        }
                }
        },
         &quot;metrics&quot;: {
                // 생략
                
                &quot;metrics_collected&quot;: {
                   //   수집 항목 제거  
                }
        }
}
~&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;삭제 및 저장&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;에디터의 명령 모드에서 dd를 누르면 빠르게 지울 수 있다! ➡️&amp;nbsp;&lt;u&gt; ESC 누르고, 키보드 dd 누르기&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;위 부분을 지운 뒤에 &lt;u&gt;esc + ZZ로 저장&lt;/u&gt;하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;변경 사항 적용 및 Agent 종료&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경사항을 적용하려면 재부팅한다. 더이상 쓸일이 없으면 종료시켜도 좋다.&lt;/p&gt;
&lt;pre id=&quot;code_1734539683723&quot; class=&quot;sqf&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a stop
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a start&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잡았다 요놈 !  &lt;/p&gt;</description>
      <category>DevOps/AWS</category>
      <category>AWS</category>
      <category>DevOps</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/303</guid>
      <comments>https://cobinding.tistory.com/303#entry303comment</comments>
      <pubDate>Thu, 19 Dec 2024 01:34:17 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 처리율 제한(Rate Limit)으로 악의적인 공격 차단하기</title>
      <link>https://cobinding.tistory.com/302</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;KOALA 서비스 회원가입에서는 총 2 가지의 인증이 요구된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이메일 인증&lt;/li&gt;
&lt;li&gt;문자 인증&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2614&quot; data-origin-height=&quot;1720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NOEwo/btsKTJlL0Tl/bpoM9cpRWo6gF3HKqXQ4O0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NOEwo/btsKTJlL0Tl/bpoM9cpRWo6gF3HKqXQ4O0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NOEwo/btsKTJlL0Tl/bpoM9cpRWo6gF3HKqXQ4O0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNOEwo%2FbtsKTJlL0Tl%2FbpoM9cpRWo6gF3HKqXQ4O0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;461&quot; data-origin-width=&quot;2614&quot; data-origin-height=&quot;1720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자 인증은 1 건당 대략 20원 정도 청구된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 누군가 의도적으로 많은 요청을 보내면, 불필요한 요청이 쌓이고 비용이 지불된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 예방하기 위한 처리율 제한 기능을 추가해보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;처리율 제한(Rate Limit)&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;일정 시간 내&lt;/b&gt;에 요청되는 &lt;b&gt;최대 횟수를 제한&lt;/b&gt;하는 기술이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;과도한 요청으로 인한 서비스 장애 및 피해를 방지한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 5회 이상 잘못된 비밀번호를 입력할 경우, 15분 동안 해당 계정의 로그인 시도를 차단&lt;/li&gt;
&lt;li&gt;초당 최대 1000명의 사용자가 접속할 수 있도록 제한하며, 초과 시 대기열에 넣거나 &quot;사이트가 과부하 상태입니다&quot;라는 메시지를 표시&lt;/li&gt;
&lt;li&gt;한 사용자가 특정 API에 1초에 100번의 요청을 보내고자 할 때, 1분 동안 60회의 요청만 가능하도록 제한&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;처리율 제한 알고리즘&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 구현하는 방법은 3가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 토큰 버킷 알고리즘(Tocken Bucket Algorithm) ✔️&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;759&quot; data-origin-height=&quot;303&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJewHI/btsKSR5ZEmS/6YMxoEK3cqf9qHNYkV5qQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJewHI/btsKSR5ZEmS/6YMxoEK3cqf9qHNYkV5qQK/img.png&quot; data-alt=&quot;https://www.krakend.io/docs/throttling/token-bucket/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJewHI/btsKSR5ZEmS/6YMxoEK3cqf9qHNYkV5qQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJewHI%2FbtsKSR5ZEmS%2F6YMxoEK3cqf9qHNYkV5qQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;160&quot; data-origin-width=&quot;759&quot; data-origin-height=&quot;303&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.krakend.io/docs/throttling/token-bucket/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버킷과 토큰으로 요청을 관리한다.&lt;/li&gt;
&lt;li&gt;일정 속도로 버킷에 토큰이 채워지며, 요청이 발생할 때마다 토큰이 소비된다.&lt;/li&gt;
&lt;li&gt;버킷 안의 토큰이 모두 소진되면 요청이 제한된다.&lt;/li&gt;
&lt;li&gt;짧은 시간에 집중되는 트래픽(burst of traffic)도 처리 가능하다. ➡️ 버킷에 토큰이 남아있기만 하다면 시스템에 요청이 전달된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 누출 버킷 알고리즘(Leaky Bucket Algorithm)&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bttRLQ/btsKS4j0Ay4/BwgKS9wmcPfXeNhgkSVJoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bttRLQ/btsKS4j0Ay4/BwgKS9wmcPfXeNhgkSVJoK/img.png&quot; data-alt=&quot;https://www.geeksforgeeks.org/leaky-bucket-algorithm/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bttRLQ/btsKS4j0Ay4/BwgKS9wmcPfXeNhgkSVJoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbttRLQ%2FbtsKS4j0Ay4%2FBwgKS9wmcPfXeNhgkSVJoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;614&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;614&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.geeksforgeeks.org/leaky-bucket-algorithm/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;바구니에 물이 들어오고 구멍을 통해 일정한 속도로 물이 빠져나간다. &lt;b&gt;양동이의 물높이는 일정하게 유지되면서 물이 넘치지 않도록&lt;/b&gt; 설계되어 있다. ➡️ &lt;u&gt;양동이에 물이 가득차면 더 이상 물을 붓지 못하듯이,&lt;/u&gt; 시스템의 처리 능력을 초과하는 요청이 들어오면 새로운 요청은 거부 및 대기한다.&lt;/li&gt;
&lt;li&gt;요청은&lt;b&gt; 큐(Queue)&lt;/b&gt;에 저장되고 FIFO으로 요청이 처리된다.&lt;/li&gt;
&lt;li&gt;큐 크기를 제한하여 메모리 사용량을 조정할 수 있다.&lt;/li&gt;
&lt;li&gt;단시간에 요청이 몰리면 큐에 요청이 쌓이고, 제때 처리하지 못하면 최신 요청들은 버려지게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 슬라이딩 윈도우 알고리즘(Sliding Window Algorithm)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;time-based!&lt;/b&gt;  요청이 들어오는 시간을 &quot;윈도우&quot;로 설정한다. ➡️&lt;u&gt; 윈도우는 고정값이 아닌 동적으로 변한다.&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이 시간 범위 내에서 허용된 요청을 카운트하여 처리한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;일정 시간 내에(슬라이딩 윈도우 안에) 요청이 초과되면 더 이상 요청을 받지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; background-color: #f6e199;&quot;&gt;토큰 버킷 알고리즘을 선택한 이유&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;① 토큰 버킷 알고리즘은 정해진 기간 동안 &lt;u&gt;&lt;b&gt;사용자마다 개별적인 토큰&lt;/b&gt;&lt;/u&gt;을 갖게 되고, 이를 통해 처리율을 제한한다. 리딩 버킷은 다수의 사용자를 대상으로 처리율을 제한하므로 토큰 버킷 알고리즘이 더 적합하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;② 슬라이팅 윈도우도 마찬가지로 특정 시간 동안&amp;nbsp; 모든 사용자로부터의 요청 수를 카운트하여 제한한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;③ &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;u&gt;&lt;b&gt;유연성있는 처리율 제한&lt;/b&gt;&lt;/u&gt;을 지원한다.&lt;/p&gt;
&lt;pre id=&quot;code_1732433311042&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Param: (버킷에 담을 최대 토큰 개수, 버킷에 토큰을 채우는 주기)
Refill.intervally(MAX_REQUESTS_PER_DAY, Duration.ofDays(1))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 구현 방식으로 토큰 생성 주기나 소비 속도를 유연하게 조정할 수 있다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;토큰 버킷 알고리즘 라이브러리&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰 버킷을 구현하기 위한 라이브러리는 3 가지가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Bucket4j&lt;/li&gt;
&lt;li&gt;Resilence4j&lt;/li&gt;
&lt;li&gt;Guava LateLimiter&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Bucket4j&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java 기반으로 구현된 처리율 제한 라이브러리다.&lt;/li&gt;
&lt;li&gt;thread- safe하도록 구현되어 있다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;원자적 변수&lt;/b&gt;: &lt;span style=&quot;color: #006dd7;&quot;&gt;AtomicInteger&lt;/span&gt;, &lt;span style=&quot;color: #006dd7;&quot;&gt;AtomicLong&lt;/span&gt;과 같은 클래스를 사용해서 스레드 간의 경쟁 조건을 방지하였다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CAS(Compare-And-Swap) 연산&lt;/b&gt;: 토큰을 소비할 때 &lt;span style=&quot;color: #006dd7;&quot;&gt;CAS 연산&lt;/span&gt;을 통해 현재 토큰 수를 확인하고 새로운 수로 업데이트 한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. Resilence4j&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MSA 등과 같은 분산 환경에서 사용하기 좋다.&lt;br /&gt;&lt;b&gt;Circuit Breaker, Fallback&lt;/b&gt; 기술을 통해 다른 모듈로 장애가 퍼지지 않도록 방지하고, 대체 처리를 하는 등 분산 환경 운영에 도움된다.&amp;nbsp;&lt;/li&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;분산 환경에서의 사용을 위해 Hazelcast, Consul 등의 분산 저장소와 연동 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. Guava&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구글에서 제공하는 라이브러리로, 동기식 API만 지원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #f6e199; color: #000000;&quot;&gt;Bucket4j를 선택한 이유&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;f&lt;u&gt;loat, double 자료형을 사용하지 않고&lt;/u&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;오직 정수형으로만&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;연산을 처리한다.&amp;nbsp;&lt;br /&gt;부동 소수점 연산은 과학/공학 계산용으로 설계되어 있다. 넓은 범위의 수를 빠르게 정밀한 &amp;lsquo;근사치&amp;rsquo;로 계산하는 것에는 유용하지만 정확도를 요구하는 연산에서는 사용하면 안 된다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://sebin-chu.notion.site/item-60-float-double-1319f848809a4b52b887b125ee05fc11?pvs=4&quot;&gt;( 정수형의 안정성에 대한 상세한 내용 )&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 라이브러리는 기본적으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;lock-free, thread-safe&lt;/b&gt;하도록 설계되었다. 멀티 스레딩 환경에서의 확장성이 높고, 다양한 동시성에 대응 가능하다.&lt;br /&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://github.com/bucket4j/bucket4j&quot;&gt;해당 깃허브&lt;/a&gt;에서 &lt;b&gt;Atomic 구현 &amp;amp; CAS&lt;/b&gt;에 대해 자세히 확인할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GC(Gabage Collector) 부담 최소화를 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;boxing type 대신 primitive type을 사용한다.&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://sebin-chu.notion.site/item-61-bc9892d73fd34f08bbdc8bc965052749?pvs=4&quot;&gt;(Boxing vs. Primitive에 대한 상세한 내용)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;spring boot의 Interceptor 기능을 통해서 요청 처리 전후 Rate Limit 적용이 가능하다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Interceptor - MVC Handler&lt;/b&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot;&gt;&lt;code&gt;@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
       bu registry.addInterceptor(new MyInterceptor())
                .addPathPatterns(&quot;/**&quot;)
                .excludePathPatterns(&quot;/login&quot;, &quot;/logout&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Bucket4j로 처리율 제한하기&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 라이브러리 의존성 추가&lt;/p&gt;
&lt;pre id=&quot;code_1732348550334&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:7.6.0'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Refill, Bandwidth, Bucket class 사용&lt;/p&gt;
&lt;pre id=&quot;code_1732348588536&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Refill refill = Refill.intervally(10, Duration.ofMinutes(1));
Bandwidth limit = Bandwidth.classic(10, refill);
Bucket bucket = Bucket.builder()
    .addLimit(limit)
    .build();

for (int i = 1; i &amp;lt;= 10; i++) {
    assertTrue(bucket.tryConsume(1));
}
assertFalse(bucket.tryConsume(1));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;✶ 프로젝트 보안을 위해 상세 구현은 &lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://www.baeldung.com/spring-bucket4j&quot;&gt;Baeldung&lt;/a&gt;에서 제공하는 예시 코드로 대체하도록 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Refill: 토큰 리필 방식을 정의한다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;intervally(토큰 리필 개수, 토큰 리필 주기) 메서드로 버킷을 컨트롤한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Bandwidth: 버킷이 허용하는 요청수와 리필 전략을 정의한다.&lt;/li&gt;
&lt;li&gt;Bucket: 실제 요청을 처리한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;addLimit: 처리율 제한을 정의한다.&lt;/li&gt;
&lt;li&gt;tryConsume: 요청에 따른 토큰 소비를 시도한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실제 적용하며 발생한 문제&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;개선 전&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 IP를 &lt;b&gt;HttpServletRequest&lt;/b&gt;의 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;u&gt;getRemoteAddr()&lt;/u&gt;&lt;/span&gt;를 통해 받았다. 이 메서드를 사용하면 클라이언트의 공인 IP 주소를 받게되는데 그게 문제 발생의 원인이었다..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 해당 서비스를 이용하는 학생은 주로 항공대 학생으로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 IP에 접속할 가능성이 높기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음과 같이 개선하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;개선 후&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TVIz4/btsKVsiR5Ah/A33dkSmvR9WBWQE8YcvJy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TVIz4/btsKVsiR5Ah/A33dkSmvR9WBWQE8YcvJy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TVIz4/btsKVsiR5Ah/A33dkSmvR9WBWQE8YcvJy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTVIz4%2FbtsKVsiR5Ah%2FA33dkSmvR9WBWQE8YcvJy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;339&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;764&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732349277507&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;String userRandomId = RandomUtils.getRandomString(36);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 요청이 들어오면 랜덤값을 생성하고, 이러한 randomId를 통해 버킷을 생성함으로써 해결하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Dev/Spring &amp;amp; JPA</category>
      <category>Back-end</category>
      <category>dev</category>
      <category>spring</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/302</guid>
      <comments>https://cobinding.tistory.com/302#entry302comment</comments>
      <pubDate>Sat, 23 Nov 2024 17:08:22 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] HTTP Request Client(webclient, feignclient)</title>
      <link>https://cobinding.tistory.com/301</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; HTTP Client&lt;/b&gt;에 대해서는 인턴십 때 익히 알고 있었다. 당시 회사 코드는 단순한 MVC 구조가 아니라&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;모두 HTTP Client 요청&lt;/span&gt;을 날렸다.&amp;nbsp; 이때&lt;b&gt; Webclient&lt;/b&gt;를 사용했는데, &lt;span style=&quot;background-color: #fbf3db;&quot; data-token-index=&quot;0&quot;&gt; Asynchronous &amp;amp; Non-blocking&lt;/span&gt;으로 리소스 자원의 효율성을 극대화했다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;보통&lt;u&gt; front &amp;rarr; back&lt;/u&gt;&lt;br /&gt;회사 코드는&lt;u&gt; front &amp;rarr; back &amp;rarr; platform&lt;/u&gt;&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 그렇게 첫 HTTP Client를 사용했었고.. 이후 KOALA 프로젝트에서 Feign을 접했다. 작년 10월부터 올해 7월, HTTP Client를 사용하며 노션에 정리해둔 내용을 오픈한다...  당시에 &lt;span style=&quot;color: #006dd7;&quot;&gt;feign의 동시성&lt;/span&gt; 관련해서 헷갈리는 점이 너무 많았으나, 공식문서와 사례를 읽으면서 모두 정리해봤다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. HTTP Request Client&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 프로토콜을 사용하여 외부 API나 서비스와 통신하기 위한 기능을 제공하는 라이브러리다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP 메서드를 사용하여 서버에 요청을 보내는 기능&lt;/li&gt;
&lt;li&gt;요청 헤더 및 본문 설정&lt;/li&gt;
&lt;li&gt;응답 처리&lt;/li&gt;
&lt;li&gt;오류 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 HTTP Request Client를 구현하는 방법은 대표적으로 3가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;① WebClient&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;② FeignClient&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;③ RestClient ④ RestTemplate&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중, 마지막 &lt;b&gt;RestTemplate&lt;/b&gt;은&lt;span style=&quot;color: #f89009;&quot;&gt; deprecated&lt;/span&gt;된다는 말이 나올 만큼 선호도가 낮은 방식이므로 이 글에서는 생략한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;궁금한 점이 있다면 &lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html&quot;&gt;이 문서&lt;/a&gt;를 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Http Request Client를 왜 사용했는가?&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;KOALA 서비스 API 구현을 위해서 백준 사이트 문제를 가져와야 했었다.&lt;/li&gt;
&lt;li&gt;문제를 가져올 수 있는 방법은 두 가지 &lt;u&gt;&lt;b&gt;① 백준 사이트 크롤링&lt;/b&gt;&lt;/u&gt; &amp;nbsp;&lt;u&gt;&lt;b&gt;② 직접 JSON 파싱해서 사용&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;크롤링은 법적 문제와 부하 문제가 항상 동반한다. ➡️ 실제로 백준 사이트에서도 크롤링을 법적으로 금지하고 있다.&lt;/li&gt;
&lt;li&gt;백준 문제 크롤링은 응답값 자체가 복잡하고, JSON 배열에 있는 값을 일일이 파싱해서 원하는 값만 도출하는 로직이 필요하다.&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1590&quot; data-origin-height=&quot;674&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDoWID/btsKEb2WiFh/AW8iWqqey6n23fTEHTfhn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDoWID/btsKEb2WiFh/AW8iWqqey6n23fTEHTfhn1/img.png&quot; data-alt=&quot;백준(https://www.acmicpc.net/) 크롤링 금지 관련 공지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDoWID/btsKEb2WiFh/AW8iWqqey6n23fTEHTfhn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDoWID%2FbtsKEb2WiFh%2FAW8iWqqey6n23fTEHTfhn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;170&quot; data-origin-width=&quot;1590&quot; data-origin-height=&quot;674&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;백준(https://www.acmicpc.net/) 크롤링 금지 관련 공지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1592&quot; data-origin-height=&quot;1056&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eeTpUq/btsKTwN0Vuw/z8tue0FAFBLAgjr4Gx476k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eeTpUq/btsKTwN0Vuw/z8tue0FAFBLAgjr4Gx476k/img.png&quot; data-alt=&quot;JSON 배열... 상당히 복잡하다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eeTpUq/btsKTwN0Vuw/z8tue0FAFBLAgjr4Gx476k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeeTpUq%2FbtsKTwN0Vuw%2Fz8tue0FAFBLAgjr4Gx476k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;265&quot; data-origin-width=&quot;1592&quot; data-origin-height=&quot;1056&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JSON 배열... 상당히 복잡하다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법을 리서치 하다가 &lt;a style=&quot;background-color: #e6f5ff; color: #0070d1; text-align: left;&quot; href=&quot;https://solvedac.github.io/unofficial-documentation/#/&quot;&gt;비공식 API 문서를 발견&lt;/a&gt;했다! &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;이 비공식 문서 API를 HTTP Client로 호출하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;개발시간 단축과 효율적인 구현 가능하다. &lt;/b&gt;따라서&lt;span style=&quot;color: #006dd7;&quot;&gt;solved.ac API&lt;/span&gt;를 FeignClient를 통해 사용하기로 결정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Http Request Client의 여러 구현 방식을 이해하기 위한 상식&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) Reactive Streams API&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;asynchronous &lt;/span&gt;processing with&lt;span style=&quot;background-color: #f6e199;&quot;&gt; non-blokcking&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;비동기 스트림 처리를 위한 표준 인터페이스를 정의&lt;/li&gt;
&lt;li&gt;Publisher, Subscriber, Subscription, Processor 등의 인터페이스를 제공하여 &lt;span style=&quot;color: #006dd7;&quot;&gt;데이터 스트림을 효과적으로 처리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;JDK 9에 포함되어 다양한 Reactive 라이브러리에서 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 기존의 요청-응답 방식과는 차이가 나는 &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;스트림 형태의 데이터 처리 방식&lt;/span&gt;&lt;/b&gt;이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;1. 데이터가 생산되면 Publisher &amp;rarr; Subscriber에게 전달 &lt;br /&gt;2. Subscriber는 데이터를 비동기적으로 처리&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) Spring MVC와 Spring Webflux&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spring Webflux:&lt;/b&gt; Reactive Streams 기반의 웹 프레임워크로, Servlet 기반의 웹 애플리케이션 모델인 Spring MVC와는 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종류 Feign Client Web Client&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 225px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 8.95349%; height: 19px;&quot;&gt;종류&lt;/td&gt;
&lt;td style=&quot;width: 36.1628%; height: 19px;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Feign Client&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 54.7674%; height: 19px;&quot;&gt;&lt;b&gt;Web Client&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 8.95349%; height: 19px;&quot;&gt;방식&lt;/td&gt;
&lt;td style=&quot;width: 36.1628%; height: 19px;&quot;&gt;&lt;b&gt;Spring MVC&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 54.7674%; height: 19px;&quot;&gt;&lt;b&gt;Spring Webflux&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 8.95349%; height: 19px;&quot;&gt;모델&lt;/td&gt;
&lt;td style=&quot;width: 36.1628%; height: 19px;&quot;&gt;요청-응답 모델&lt;/td&gt;
&lt;td style=&quot;width: 54.7674%; height: 19px;&quot;&gt;Reactive Stream 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 111px;&quot;&gt;
&lt;td style=&quot;width: 8.95349%; height: 111px;&quot;&gt;동작&lt;/td&gt;
&lt;td style=&quot;width: 36.1628%; height: 111px;&quot;&gt;synchronous &amp;amp; blocking&lt;br /&gt;&lt;br /&gt;- 클라이언트는 응답을 기다리는 동안 블로킹되어 다른 작업 불가능 (&lt;span style=&quot;color: #9d9d9d;&quot;&gt;완전 불가능은 아니다. 간단한 방법이 있는데 프로젝트에 어떻게 적용했는지 뒤에 설명해보겠다.)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 54.7674%; height: 111px;&quot;&gt;asynchronous &amp;amp; non-blocking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 57px;&quot;&gt;
&lt;td style=&quot;width: 8.95349%; height: 57px;&quot;&gt;데이터&lt;br /&gt;처리&lt;/td&gt;
&lt;td style=&quot;width: 36.1628%; height: 57px;&quot;&gt;단일 객체 또는 컬렉션 데이터 ex) 사용자가 특정 상품 정보를 요청하면 서버는 해당 상품 정보 객체 반환&lt;/td&gt;
&lt;td style=&quot;width: 54.7674%; height: 57px;&quot;&gt;Reactive Streams API를 사용하여 데이터 스트림을 처리&lt;br /&gt;&lt;br /&gt;ex1) 실시간 주식 시세 정보 요청이 들어오면 서버는 주식 시세 데이터를 실시간으로 스트리밍 형태로 전송 &lt;br /&gt;&lt;br /&gt;ex2) 대용량 파일 다운로드 요청이 들어왔을 때, 서버는 파일 데이터를 청크 단위로 나누어 스트리밍 형태로 전송&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.95349%;&quot;&gt;스레드&lt;br /&gt;관리&lt;/td&gt;
&lt;td style=&quot;width: 36.1628%;&quot;&gt;- thread pool 사용&lt;br /&gt;- 각 요청마다 스레드 생성&lt;/td&gt;
&lt;td style=&quot;width: 54.7674%;&quot;&gt;- 적은 수의 스레드&lt;br /&gt;- 논블로킹 방식의 I/O 작업&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. WebClient&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring webflux 기반의 라이브러리, HTTP 요청을 수행하는 클라이언트가 포함되어있다. 스레드나 동시성 처리를 할 필요없이 비동기 논리의 선언적 구성이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&amp;ldquo;It is fully non-blocking, it supports streaming&amp;ldquo;&lt;/i&gt; - &lt;a href=&quot;http://docs.spring.io&quot;&gt;docs.spring.io&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;비동기성과 논블로킹 처리&lt;/span&gt;에 초점을 둔 Http Client&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;WebClient Configuration&lt;/h4&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@Configuration
public class WebClientConfig {

    @Bean
    public WebClient webClient(WebClient.Builder webClientBuilder) {
        return webClientBuilder
                .baseUrl(&quot;${feign.svc1.url}&quot;)
                .build();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;Webclient Interface&lt;/span&gt;&lt;/h4&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Component
public class SolvedAcProblemSearchClient {

    private final WebClient webClient;

    public SolvedAcProblemSearchClient(WebClient webClient) {
        this.webClient = webClient;
    }

		// 빌더 패턴
    public Mono&amp;lt;ProblemResponse&amp;gt; searchProblems(int page, String query, String sort, String direction) {
        return webClient.get()
                .uri(uriBuilder -&amp;gt; uriBuilder
                        .queryParam(&quot;page&quot;, page)
                        .queryParam(&quot;query&quot;, query)
                        .queryParam(&quot;sort&quot;, sort)
                        .queryParam(&quot;direction&quot;, direction)
                        .build())
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(ProblemResponse.class);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. FeignClient&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring MVC 기반의 HTTP 클라이언트 구현체다. 선언적 방식의 웹 서비스 클라이언트로, 쉽게 웹 클라이언트를 생성할 수 있다. &amp;rarr; 동기식 처리/블로킹 방식&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Netflix에서 개발된 Http Client Binder &amp;rarr; Spring Cloud 진영에 통합되면서 &lt;b&gt;spring-cloud-starter-openfeign로 사용&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;HTTP 요청을 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;추상화/표준화하는 과정의 복잡성을 줄이기 위해&lt;/span&gt; 만들어졌다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;웹 서비스 클라이언트 구현이 쉽고 직관적이다. 이건 써보면 안다..&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;사용성이 월등히 높은게 느껴진다!&lt;/u&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;익숙한 Spring MVC 구조를 가지고 있으며 어노테이션으로 개발이 가능하다. 참고로 WebClient는 빌더 패턴으로 구현하고, 각각의 메서드 파라미터를 유의해야 한다. (실제로 인턴십 중에 platform으로 요청하는 client를 짜면서 URL 관련 문제로 허둥댄 적이 있다..) 아래 코드 예시를 보자.&lt;/li&gt;
&lt;li&gt;API의 Restfulness에 관계없이 일관된 방식으로 API 호출 가능하다. (비표준적인 URL 구조나 HTTP 메서드 등을 사용하더라도 표준화된 방식으로 API 호출이 가능하다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkrOnq/btsKCV1nmKo/Zdh58lMl3LNYtn8LAAubRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkrOnq/btsKCV1nmKo/Zdh58lMl3LNYtn8LAAubRK/img.png&quot; data-alt=&quot;예시 코드를 통해 간단하게 둘의 구현 비교(좌: webclient 우:feignclient)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkrOnq/btsKCV1nmKo/Zdh58lMl3LNYtn8LAAubRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkrOnq%2FbtsKCV1nmKo%2FZdh58lMl3LNYtn8LAAubRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1812&quot; height=&quot;742&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예시 코드를 통해 간단하게 둘의 구현 비교(좌: webclient 우:feignclient)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;FeignClient와 동시성 처리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Feign과 동시성을 이해하는 것에 많은 어려움이 있었다. 여기에 작성하면 너무 길어져 &lt;a href=&quot;https://sebin-chu.notion.site/FeignClient-bd77bbc32d1643dfa32ce3aae2efac30?pvs=4&quot;&gt;노션 링크&lt;/a&gt;로 대체한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Feign을 사용할 사람이라면 한 번 읽어보는 것을 추천한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결론&lt;/b&gt;만 공유하면 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;1. feign의 내부 구현이 자바 11 HttpClient, 자바 17에서의 synchronized &amp;rarr; reentrantLock로 변경되면서 많은 부분이 해결됨&lt;br /&gt;2. 블로그 구현 방식도 괜찮은 부분이 있다. Apache Client 등을 사용 &amp;rarr; 안정적&lt;br /&gt;3. 동시성 문제를 일으킬 만큼 다량의 스레드를 쏟아내는 로직 자체를 feign과 함께 만나기가 어려움 &amp;rarr; 동시성이 걱정되는 로직이 있다면 webclient를 쓰면 됨&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Feign을 구현하기 위해 공부하면서&lt;i&gt; &quot;동시성 문제가 있을 수 있다고....?&quot;&lt;/i&gt;에서부터 출발하였고, 결론은 위와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 서비스는 단 하나의 호출만 했고, &lt;span data-token-index=&quot;1&quot;&gt;확장 가능성을 고려해도 &lt;/span&gt;동시성 문제가 우려될 만큼의 외부 API 호출 기능이 도입되지 않을 것이다. Feign을 통해 구현하면 &lt;span style=&quot;color: #ee2323;&quot;&gt;사용 편의성뿐만 아니라 Spring의 Scheduler와 @Async를 활용한 비동기 요청이 가능한 구조로 설계할 수 있다.&lt;/span&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;따라서 사용 편의성과 비동기 처리를 모두 갖춘 Feign을 선택했다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. FeignClient 사용방법&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6-1. FeignClient 활성화&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Application 단에 &lt;span style=&quot;color: #6164c6;&quot;&gt;@EnableFeignClients&lt;/span&gt; 적용 &amp;rarr; Feign 클라이언트를 찾아서 자동으로 등록&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6-2. 의존성 추가&lt;/h4&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.0.0'
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6-3. Feign용 Conf Class&lt;/h4&gt;
&lt;pre id=&quot;code_1731244019257&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class FeignClientConfig {

  @Bean
  Request.Options requestOptions() {
    return new Request.Options(5, TimeUnit.SECONDS, 3, TimeUnit.SECONDS, false);
  }

  @Bean
  Retryer retry() {
    return new Retryer.Default(500, 1000, 2);
  }

  @Bean
  Logger.Level feignLoggerLevel() {
    return Level.ALL;
  }

  @Bean
  public RequestInterceptor requestInterceptor() {
    return requestTemplate -&amp;gt; {
      requestTemplate.header(&quot;Content-Type&quot;, &quot;application/json&quot;);
      requestTemplate.header(&quot;Accept-Encoding&quot;, &quot;gzip&quot;);
    };
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✶ RequestInterceptor&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d7hh7t/btsKDfSPOkM/cpU0fZJt2nTuNHHiuZJ5dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d7hh7t/btsKDfSPOkM/cpU0fZJt2nTuNHHiuZJ5dk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d7hh7t/btsKDfSPOkM/cpU0fZJt2nTuNHHiuZJ5dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd7hh7t%2FbtsKDfSPOkM%2FcpU0fZJt2nTuNHHiuZJ5dk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;70&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;956&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KhBT7/btsKDCmroxF/XRIuYsvEOKyxf4FEi4oIBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KhBT7/btsKDCmroxF/XRIuYsvEOKyxf4FEi4oIBK/img.png&quot; data-alt=&quot;Feign 클라이언트에서 HTTP 요청을 보내기 전에 HTTP 요청을 수정할 수 있는 인터페이스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KhBT7/btsKDCmroxF/XRIuYsvEOKyxf4FEi4oIBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKhBT7%2FbtsKDCmroxF%2FXRIuYsvEOKyxf4FEi4oIBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;365&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;956&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Feign 클라이언트에서 HTTP 요청을 보내기 전에 HTTP 요청을 수정할 수 있는 인터페이스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6-4. @FeignClient 인터페이스&lt;/h4&gt;
&lt;pre id=&quot;code_1731244449879&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@FeignClient(name = &quot;solvedAc&quot;, url = &quot;${feign.solved-ac.url}&quot;, configuration = FeignClientConfig.class)
public interface SolvedAcClient {

    @GetMapping(&quot;/search/problem&quot;)
    ProblemResponse searchProblems(
        @RequestParam(&quot;page&quot;) int page,
        @RequestParam(&quot;query&quot;) String query,
        @RequestParam(&quot;sort&quot;) String sort,
        @RequestParam(&quot;direction&quot;) String direction
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개별 Feign Client에 적용할 인터페이스&lt;/li&gt;
&lt;li&gt;사용할 이름과 요청 url은 &lt;span style=&quot;color: #006dd7;&quot;&gt;하드코딩하지 않고 YAML 파일에 정의&lt;/span&gt; &amp;rarr; 유연성/재사용성/관리 용이/보안&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6-5. 호출&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 요청할 API와 관련한 인터페이스를 정의하고,&lt;br /&gt;서비스 함수에서 사용할 특정 FeignClient interface에 대한 의존성 주입 + 호출만 하면 구현 끝이다. &lt;br /&gt;간단하죠&lt;/p&gt;
&lt;pre id=&quot;code_1731244521520&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class CreateAlgorithmService {

  private final SolvedAcClient solvedAcClient;
  private final AlgorithmRepository algorithmRepository;
  private static final int NUMBER_PER_PAGE = 30;

  public void createAlgorithm() {
    AlgorithmResponse initResponse = solvedAcClient.searchAlgorithm(1);
    int pageCount = initResponse.getCount() / NUMBER_PER_PAGE + 1;

    for (int page = 1; page &amp;lt;= pageCount; page++) {
      log.info(&quot;{}번 페이지 알고리즘 저장&quot;, page);
      AlgorithmResponse algorithmResponse = solvedAcClient.searchAlgorithm(page);
      List&amp;lt;AlgorithmDto&amp;gt; algorithmDtoList = algorithmResponse.getAlgorithmList();

      List&amp;lt;Algorithm&amp;gt; algorithmList = new ArrayList&amp;lt;&amp;gt;();
      for (AlgorithmDto algorithmDto : algorithmDtoList) {
        algorithmList.add(algorithmDto.toEntity());
      }
      algorithmRepository.saveAll(algorithmList);
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6-7. 결과&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2226&quot; data-origin-height=&quot;1410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKkfo2/btsKCw19T8C/03TlASanZ7KMQJaCJXpKKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKkfo2/btsKCw19T8C/03TlASanZ7KMQJaCJXpKKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKkfo2/btsKCw19T8C/03TlASanZ7KMQJaCJXpKKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKkfo2%2FbtsKCw19T8C%2F03TlASanZ7KMQJaCJXpKKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;412&quot; data-origin-width=&quot;2226&quot; data-origin-height=&quot;1410&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 느낀점&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;-&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;background-color: #fbf3db;&quot; data-token-index=&quot;2&quot;&gt;webclient가 web flux로부터 나와서, &lt;span style=&quot;color: #ee2323;&quot;&gt;fully asynchronous &amp;amp; non-blocking&lt;/span&gt;에 유의하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인턴십 사례처럼...&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청은 synch로 받더라도 이 요청을 실제로 처리하는 platform 서버로는 비동기식으로 요청 및 처리를 하게 되는 구조&lt;br /&gt;&amp;rArr; &lt;span style=&quot;background-color: #fbf3db;&quot; data-token-index=&quot;1&quot;&gt;이런식의 전략이 필요할 때 webclient를 사용하는 것이지 간편하다고만 해서 사용하면 안 되는 것을 알았다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 블로그는&amp;hellip;. 내용의 질도 중요하지만 날짜도 중요하다. &lt;br /&gt;계속해서 개선이 되고 있는 기술은 이전에 작성된 블로그와 맞지 않는 부분이 많기에 주의해야 한다. 또, 한 번 오개념이 작성된 블로그가 뜨면 그 오개념을 여러 사람이 따라가고, 이게 AI 답변에도 영향을 준다고 느꼈다. 최대한 공식적으로 작성된 기술 문서를 참고하고, 그 기술이 나온 기반과 배경을 따라가면 유추할 수 있는 부분이 있다. 그걸 해석하는 능력 또한 개발에 있어서 중요하며, 이를 함양하기 위해 노력해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzpDR8/btsKEIzi8tz/peqCFk764M5MiIgQoKbhZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzpDR8/btsKEIzi8tz/peqCFk764M5MiIgQoKbhZ1/img.png&quot; data-alt=&quot;지식의 확장은 곧 나의 확장 !&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzpDR8/btsKEIzi8tz/peqCFk764M5MiIgQoKbhZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzpDR8%2FbtsKEIzi8tz%2FpeqCFk764M5MiIgQoKbhZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;212&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;지식의 확장은 곧 나의 확장 !&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Dev/Spring &amp;amp; JPA</category>
      <category>Back-end</category>
      <category>dev</category>
      <category>spring</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/301</guid>
      <comments>https://cobinding.tistory.com/301#entry301comment</comments>
      <pubDate>Sun, 10 Nov 2024 22:18:01 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 네트워크 원리(6) 웹 서버에 도착하여 응답 데이터가 웹 브라우저로 돌아간다</title>
      <link>https://cobinding.tistory.com/300</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;성공과 실패를 결정하는 1% 네트워크 원리를 공부하고 정리한 글이다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;1248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DW3qO/btsKwvooxFQ/bwWkuyUeS3kq4nb0DKqj1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DW3qO/btsKwvooxFQ/bwWkuyUeS3kq4nb0DKqj1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DW3qO/btsKwvooxFQ/bwWkuyUeS3kq4nb0DKqj1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDW3qO%2FbtsKwvooxFQ%2FbwWkuyUeS3kq4nb0DKqj1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;477&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;1248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY 1 | 서버의 개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-1) 클라이언트와 서버의 차이점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 머신은 용도에 따라 다양한 OS, 하드웨어가 있다. 하지만 네트워크에 관한 부분( LAN 어댑터, 프로토콜 스택, 소켓 라이브러리 등)의 기능은 클라이언트와 서버가 같은 구졸르 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; TCP/IP 기능은 하드웨어나 OS가 무엇이든지 달라지지 않기 때문에 기능이 통일되어 있다고 봐도 무방하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 그렇다면 차이점은?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소켓 라이브러리의 connection과 데이터 송수신&lt;/li&gt;
&lt;li&gt;서버 애플리케이션은 동시에 다수의 클라이언트 PC와 대화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 프로그램으로 여러 클라이언트를 다루는 것은 X. 보통은 1:1로 대화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-2) 서버 애플리케이션의 구조&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;916&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d9FAWo/btsKynbCLva/wY0v4741nC6kXAoRP9FTx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d9FAWo/btsKynbCLva/wY0v4741nC6kXAoRP9FTx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d9FAWo/btsKynbCLva/wY0v4741nC6kXAoRP9FTx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd9FAWo%2FbtsKynbCLva%2FwY0v4741nC6kXAoRP9FTx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;334&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;916&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(b): 클라이언트가 새로 접속할 때마다 잇달아 기동되며, 서버와 클라이언트가 일대일로 대응한다.&lt;/li&gt;
&lt;li&gt;서버는 소켓을 미리 만들어 두고 접속 대기 상태로 변경한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-3) 서버측의 소켓과 포트번호&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 서버의 연결과정&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;[디스크립터] &lt;/span&gt;소켓 작성&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;[bind]&lt;/span&gt; 소켓에 포트 할당&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;[listen]&lt;/span&gt; 대기 상태라는 정보를 기록&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;[accept]&lt;/span&gt; 소켓 복제 후, 접속을 접수하는 대기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 접수를 기다리는 첫 소켓은 계속 복제용으로 사용된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접속 대기 중인 상태에서 accept를 호출하고, 클라이언트의 요청을 받을 때 &lt;b&gt;클라이언트의 소켓과 접속하기 위한 소켓&lt;/b&gt;은 접속 대기 중인 소켓을 복제해서 만든다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새소켓을 만들지 않고 접속 대기 소켓을 그대로 사용하면, 다음 클라이언트가 접속할 때 곤란해진다.&lt;/li&gt;
&lt;li&gt;이러한 사태를 막기 위해 소켓은 항상 복제해서 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 접속 대기 중인 소켓을 복제해서 쓰면 포트 번호는 어떻게 구분할까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포트 번호는 소켓을 식별하기 위해 사용되는 것이다. 하지만 소켓들이 모두 다른 포트 번호를 사용하는 것은 아니다. 소켓은 총 4 가지 정보를 통해 식별된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;scr IP&lt;/li&gt;
&lt;li&gt;scr Port&lt;/li&gt;
&lt;li&gt;des IP&lt;/li&gt;
&lt;li&gt;des Port&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*서버 측의 포켓에는 같은 포트 번호를 가진 여러 개의 소켓이 존재한다. 하지만 클라이언트 측의 소켓은 모두 다른 포트 번호를 할당하므로, 클라이언트 측의 포트 번호에 따라 소켓을 지정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY 2 | 서버의 수신 동작&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-1) ⭐️⭐️LAN에 데이터가 도착했을 때&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;(도착한 패킷의) 프리앰블 부분에서 클록을 추출한다.&lt;/li&gt;
&lt;li&gt;추출한 클록을 같은 간격으로 연장한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프리앰블은 신호가 일정 간격으로 규칙적으로 변화한다. 변화의 타이밍을 조사하면 클록이 어느 위치에 있는지 알 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;클록이 위치한 곳을 찾고, 여기서 데이터 신호의 변화 방향(+,- 전압)을 조사한다.&lt;/li&gt;
&lt;li&gt;이 변화방향을 통해서 디지털 신호(1,0)로 바꾼다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*클록: 타이밍 신호를 제공하는 것. 딱 이 시점에 데이터를 전송하면, 온전한 데이터 전송이 가능하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 전기 신호 &amp;rarr; 디지털 데이터를 추출하고&amp;hellip;&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;패킷의 맨 마지막에 있는 프레임 체크 시퀀스(FCS)를 통해 오류 유무를 검사한다.&lt;/li&gt;
&lt;li&gt;패킷 맨 앞의 MAC 헤더의 수신처를 조사해서 자신이 이 패킷의 수신처가 맞는지 확인한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;이더넷의 원리에 따르면 일단 패킷 뿌리고 받는 쪽이 확인하는 구조&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이 디지털 데이터를 LAN 어댑터 내부의 버퍼 메모리에 저장한다.&lt;/li&gt;
&lt;li&gt;CPU는 다른 일을 하고 있기에 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;인터럽트로 패킷이 도착했음을 알린다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 인터럽트가 실행된 CPU는&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;실행하고 있던 작업을 중단하고 &lt;b&gt;LAN 드라이버&lt;/b&gt;로 실행을 전환한다.&lt;/li&gt;
&lt;li&gt;LAN 드라이버가 버퍼에 있는 패킷을 추출하고, MAC 타입에 따른 프로토콜을 판별한다.&lt;/li&gt;
&lt;li&gt;프토로콜을 처리하는 장치를 호출한다.&lt;/li&gt;
&lt;li&gt;IP 프로토콜이라면 TCP/IP의 프로토콜 스택을 호출하고, 패킷을 건네준다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-2) IP 담당 부분의 수신 동작&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;대상이 자신인지 주소를 확인한다.&lt;/li&gt;
&lt;li&gt;Fragmentation에 대해 확인한다. &lt;span style=&quot;color: #006dd7;&quot;&gt;만약, 분할되어 있는 경우에는 임시로 메모리에 저장해두고 분할된 패킷이 모두 도작한 시점에 조립한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;IP 헤더의 프로토콜 항목(TCP or UDP)을 조사후, 담당 부분에 패킷을 건네준다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-3) TCP 담당 부분이 접속 패킷을 수신했을 때 (3way-handshaking)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 서버 측에서 대기 중인 소켓을 확인한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 헤더의&lt;span style=&quot;color: #ee2323;&quot;&gt; SYN = 1&lt;/span&gt;이면, &lt;span style=&quot;color: #ee2323;&quot;&gt;접속 접수 동작&lt;/span&gt;을 뜻한다. 따라서 해당 패킷과 같은 포트 번호를 가진 대기 상태의 소켓이 있는지 확인한다. 만약 없으면 그 포트 번호에 해당하는 대기중인 서비스나 프로세스가 없다는 뜻으로, 오류를 통지한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어서 서버 포트는 80인데 클라이언트가 81로 요청해서, 서버의 81에는 대기 중인 소켓이 없는 상황&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 대기 중인 포트를 가진 소켓을 확인했다면 패킷을 복사하여 소켓을 만들고 TCP 헤더를 만든다.&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;대기 중인 소켓의 복제본을 만들고, 여기에 필요한 정보를 기록한다. 동시에 메모리 영역을 확보한다.&lt;/li&gt;
&lt;li&gt;패킷을 받았음을 나타내는 ACK, 시퀀스 번호, 윈도우 값 등을 기록한 TCP 헤더를 만든다.&lt;/li&gt;
&lt;li&gt;만들어진 TCP 헤더를 IP 담당 부분에 보내고, 클라이언트에 반송한다.&lt;/li&gt;
&lt;li&gt;클라이언트는 다시 ACK를 보내고, 접속 동작이 완료된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-4) TCP 담당 부분이 데이터 패킷을 수신했을 때(전송 과정)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ TCP 담당 부분이 수신하는 과정&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;4 가지 정보&lt;/span&gt;를 통해 제대로 도착한 건지 확인한다.&lt;/li&gt;
&lt;li&gt;패킷을 통해 데이터 송수신 진행 상황과 TCP 헤더 정보를 확인하고, 데이터 송수신이 올바르게 동작하고 있는지 점검한다.&lt;/li&gt;
&lt;li&gt;분할된 패킷이라면 수신 버퍼에 저장한다. 지난 번 패킷에서 수신한 데이터 조각의 다음에 연결하는 식으로 데이터가 분할되기 전의 상태로 되돌린다.&lt;/li&gt;
&lt;li&gt;이 과정을 통해 수신 버퍼에 데이터가 저장되면 응답용 TCP 헤더를 만든다.&lt;/li&gt;
&lt;li&gt;ACK를 기록하고, IP 담당 부분에 의뢰하여 클라이언트에게 반송한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 애플리케이션이 소켓을 받는 과정&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;애플리케이션이 소켓 라이브러리 호출&lt;/b&gt;을 통해 데이터를 기다리는 상태를 유지한다.&lt;/li&gt;
&lt;li&gt;TCP 담당 부분이 수신 동작이 끝나는 것과 동시에 애플리케이션에게 데이터를 건네준다.&lt;/li&gt;
&lt;li&gt;제어는 애플리케이션으로 넘어가고, HTTP request 내용을 확인한다.&lt;/li&gt;
&lt;li&gt;브라우저에 데이터를 반송한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-5) 연결 끊기(4 way handshaking)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 서버측에서 연결 끊기를 요청하는 상황이라면&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;애플리케이션이 소켓 라이브러리의 close()를 호출한다.&lt;/li&gt;
&lt;li&gt;TCP 담당 부분이&lt;span style=&quot;color: #ee2323;&quot;&gt; FIN = 1&lt;/span&gt;을 설정한 TCP 헤더를 만들고, IP 담당 부분에 전송한다.&lt;/li&gt;
&lt;li&gt;IP 담당 부분은 클라이언트에 이를 보낸다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;클라이언트는 ACK&lt;/span&gt;를 반송하고, close()를 계속 호출하며 &lt;span style=&quot;color: #ee2323;&quot;&gt;FIN을&lt;/span&gt; 보낸다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;서버가 ACK를 반송하고, 연결이 끊긴다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY 3 | 웹서버 SW가 리퀘스트 메시지의 의미를 해석하고 이에 응한다.&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-1) 조회의 URI를 실제 파일명으로 반환한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 웹 서버의 경우, read에서 받은 데이터의 내용이 HTTP Request Message다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Request Message에 따라 적절한 처리를 실행하여 응답 메시지를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ URI에 기록된 경로는 가상 디렉토리 구조에서의 경로명이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;Like&amp;hellip; ubuntu 심볼릭 링크처럼,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 읽어올 때 가상 디렉토리와 실제 디렉토리의 대응 관계를 조사하고 실제 디렉토리의 경로명으로 변환한 후 파일을 읽어 데이터를 반송한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-2) CGI 프로그램을 작동하는 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 웹 서버에서 프로그램을 작동시키는 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 HTTP의 리퀘스트 메시지가 HTML 문서에 액세스할 때와는 다르다. 웹 서버에서 프로그램을 작동시키는 경우, 프로그램에서 처리하는 데이터를 HTTP 리퀘스트 메시지 안에 넣어 브라우저에서 웹 서버로 보내는 것이 일반적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 리퀘스트 메시지를 보낸 웹 서버의 동작&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;URI에 쓰여있는 파일명을 통해 이게 프로그램인지 판단한다.&lt;/li&gt;
&lt;li&gt;예를 들어 .php, .exe와 같은 확장자를 등록해두고, 파일명의 확장자가 이와 일치하면 프로그램으로 간주한다.&lt;/li&gt;
&lt;li&gt;프로그램이라고 판단되면 웹 서버는 이 프로그램을 작동시키도록 OS에 의뢰한다.&lt;/li&gt;
&lt;li&gt;리퀘스트 메시지에서 받은 데이터를 작동시킨 프로그램에 전송한다.&lt;/li&gt;
&lt;li&gt;프로그램은 받은 데이터를 처리하여 출력을 웹 서버에 돌려준다. &amp;larr; 웹 서버는 이에 관여 X&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-3) 웹 서버로 수행하는 액세스 제어&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 특정 부서, 특정 사용자 등 조건에 따라 접근을 제어하는 액세스 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 다음 세 가지에 적용한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클라이언트의 주소&lt;/li&gt;
&lt;li&gt;클라이언트의 도메인명
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;DNS 서버를 이용해서 IP 주소 조사&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;사용자명과 패스워드
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;서버에 사전 등록&lt;/li&gt;
&lt;li&gt;클라이언트에게 입력하라는 Request 통지&lt;/li&gt;
&lt;li&gt;클라이언트가 입력한 내용과 사전 등록된 암호를 비교 대조&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-4) 응답 메시지를 되돌려 보낸다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 클라이언트의 요청에 대한 응답을 되돌려 보낸다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 동작 방식은 클라이언트가 Request한 것과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소켓 라이브러리의 wirte를 호출하여 응답 메시지를 프로토콜 스택에 건네주고, 디스크립터를 통해 통신에 사용되고 있는 소켓 통지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY 4 | 웹 브라우저가 응답 메시지를 받아 화면에 표시한다.&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-1) 응답 데이터의 형식을 보고 본질을 파악한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 서버가 전송한 응답 메시지는 &lt;span style=&quot;color: #006dd7;&quot;&gt;다수의 패킷으로 나뉘어&lt;/span&gt; 클라이언트에게 도착한다. 클라이언트는 이를 받아, LAN에서 디지털 신호로 변환하고, &lt;span style=&quot;color: #006dd7;&quot;&gt;프로토콜 스택&lt;/span&gt;이 분할된 패킷을 모아서 원래의 응답 메시지로 되돌린 후 브라우저에 건네준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 브라우저가 화면 표시를 할 때는 데이터 유형에 따라 달라진다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹에서 취급하는 데이터는 문장, 화상, 음성, 영상 등 다양하고 종류에 따라 표시 방법이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ &amp;lsquo;Content-Type&amp;rsquo; 헤더를 통해 데이터 종류를 미리 파악한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답 메시지의 맨 앞의 Content-Type 헤더로 종류를 파악하는 것이 원칙이다. 여기에는 다음과 같은 형식의 데이터 종류를 쓴다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Content-Type: text/html (주타입/서브 타입)&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시와 같이 데이터의 종류가 텍스트인 경우에는 어떤 문자 코드를 사용하는지 판단해야 한다. 따라서 다음과 같이 charset으로 문자 코드의 정보를 부가한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Content-Type: text/html; charset = utf-8&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*Content-Type에 정의되는 데이터 종류는&lt;span style=&quot;color: #006dd7;&quot;&gt; MIME 사양에 규정되어 있다.&lt;/span&gt; 파일 확장자나 데이터 내용의 포맷 등에서 종합적으로 판단하는 방법도 있다. 예를 들어 .html 이라면 HTML 문서로 간주하거나 문서의 젤 앞부분을 보고 &amp;lt;html&amp;gt;이라고 되어 있으면 HTML 이라고 간주한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ &amp;lsquo;Content-Encoding&amp;rsquo; 헤더를 통해 데이터 변환에 대해 조사한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;압축 기술이나 부호화 기술에 따라 원래 데이터를 변환하고 나서 메시지에 저장한 경우에는 어떤 변환을 했는지 이 Content-Encoding 헤더에 기록하고, 응답을 받은 브라우저는 이 필드를 조사하여 필요에 따라 데이터를 원래대로 되돌린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-2) 브라우저 화면에 웹 페이지를 표시하여 액세스를 완료한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 종류가 판명되면 종류에 따라 화면 표시 프로그램을 호출하여 데이터를 표시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ HTML&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[기본 html을 화면에 표시]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;html의 경우 문장의 레이아웃이나 글꼴의 종류 등을 기록한 태그가 내장되어 있으므로 태그의 의미를 해석하여 문장을 배치하면서 화면에 표시한다. 실제 화면 표시 동작은 OS가 담당하므로, OS에 대해 화면의 어떤 위치에 , 어떤 문자를 어떤 글꼴로 표시할 것인지 지시하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[화상 데이터가 포함된 경우]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML 문서 데이터와 화상 데이터를 별도의 파일에 저장하고, html의 문장 데이터 안에 화상이 내장되었음을 나타내는 태그를 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[애플리케이션 데이터의 경우]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인이든 독립된 애플리케이션이든 호출 프로그램이 브라우저에 설정되어 있으므로, 그에 따라 프로그램을 호출하여 데이터를 브라우저에 건네고, 이를 화면에 표시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 네트워크 스터디가 끝났다...!!   중요한 내용이니까 앞으로 복기 잘 하자!&lt;/p&gt;</description>
      <category>CS/Network</category>
      <category>CS</category>
      <category>network</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/300</guid>
      <comments>https://cobinding.tistory.com/300#entry300comment</comments>
      <pubDate>Tue, 5 Nov 2024 21:40:40 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 네트워크 원리(5) 서버측의 LAN에는 무엇이 있는가?</title>
      <link>https://cobinding.tistory.com/299</link>
      <description>&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;성공과 실패를 결정하는 1% 네트워크 원리를 공부하고 정리한 글이다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Chapter 5 - 방화벽과 캐시 서버의 탐험&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;1248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IyBrE/btsKgdhlKBM/Ak0QPitQv0ArzN68s24Lbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IyBrE/btsKgdhlKBM/Ak0QPitQv0ArzN68s24Lbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IyBrE/btsKgdhlKBM/Ak0QPitQv0ArzN68s24Lbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIyBrE%2FbtsKgdhlKBM%2FAk0QPitQv0ArzN68s24Lbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;477&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;1248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY 1 | 웹 서버의 설치 장소&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-1) 사내에 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트의 POP/프로바이더로부터 오는 패킷을 방화벽에서 한 번 거르는 방법이 보편화되어 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 서버에서 동작하는 특정 애플리케이션에 액세스하는 패킷만 통과시키고, 그 외 패킷은 모두 차단한다.&lt;/li&gt;
&lt;li&gt;액세스를 허가한 애플리케이션에 보안 구멍이 있을 수 있지만, 클 to 서구조에 비하면 위험성이 낮다.&lt;/li&gt;
&lt;li&gt;현재는 이 구조를 빠져나가는 다양한 수법이 많이 생겨셔 더불어서 바이러스 검사, 부정 침입 검사 등의 구조를 함께 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-2) 데이터 센터에 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 센터로부터 서버를 빌리는 형식&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 센터는 프로바이더의 중심인 NOC에 접속되어 있거나, IX와 직접 연결되어 있기도 해서 고속 접근이 가능하다.&lt;/li&gt;
&lt;li&gt;내진 구조의 건물 / 자가 발전 장치 / 24시간 관리되는 시설이 많으므로 안정성이 높다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY | 방화벽의 원리와 동작&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-1) 패킷 필터링형이 주류이다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 상에는 다양한 패킷이 흐르므로&amp;hellip; 이를 분별하는게 쉽진 않았다. &lt;br /&gt;&amp;rarr; &lt;b&gt;패킷 필터링&lt;/b&gt;이 성능, 가격, 편의성 등에서 가장 많이 보급되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-2) 패킷 필터링의 조건 설정 개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패킷의 헤더 통신과 관련한 여러 제어 정보가 있다. 이 헤더로 필터링한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 사내 랜과 공개 랜이 분리되어 있고, 웹 서버에서 인터넷측에 액세스하는 것을 금지하고 있는 상황에서 패킷의 움직임&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;송수신처 IP 주소로 시종점을 판단한다.&lt;/li&gt;
&lt;li&gt;인터넷 &amp;rarr; 웹 서버로 보내는 패킷의 송신처(시작점)은 정확히 알 수 없다. 하지만 종점이 웹 서버라는 점이 명확하므로, 시점이 어디든 상관없이 웹 서버의 IP 주소에 일치하는 패킷은 통과시킨다는 조건을 설정하면 된다.&lt;/li&gt;
&lt;li&gt;받은 패킷에 대한 ACK(수신 확인 응답)을 보낸다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-3) 애플리케이션을 한정할 때 포트 번호를 사용한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수선처의 IP 주소 + 포트 번호를 함께 확인해서 일치하는 패킷만 통과하도록 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-4) 컨트롤 비트로 접속 방향을 판단한다 - TCP의 SYN, ACK&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TCP를 사용하는 웹&lt;/b&gt;은 양방향으로 패킷이 흐른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 단순히 웹 서버 &amp;rarr; 인터넷으로 흐르는 패킷을 정지시키면, 인터넷 &amp;rarr; 웹 서버 액세스 동작도 정지된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 웹 서버 &amp;rarr; 인터넷 통신 막기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;패킷의 액세스 방향을 판단하여 정지시켜야 하는데, 이를 TCP 헤더의 컨트롤 비트로 판단한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1oKSe/btsKhONGag8/yfnSgrwh5hT4PO9jtdbgJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1oKSe/btsKhONGag8/yfnSgrwh5hT4PO9jtdbgJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1oKSe/btsKhONGag8/yfnSgrwh5hT4PO9jtdbgJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1oKSe%2FbtsKhONGag8%2FyfnSgrwh5hT4PO9jtdbgJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;253&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;808&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP의 최초의 패킷만 SYN=1, ACK=0이 헤더에 설정된다. 따라서 이 헤더의 컨트롤비트 값을 확인하고, 최초의 패킷이면서 수신처가 인터넷, 송신처가 웹 서버라면 이것을 차단하도록 설정한다. 그럼 최초의 패킷에 대한 전송이 실패하고 그에 따라 두 번째 SYN, ACK를 받을 수 없으므로 TCP connection에 실패하여 통신이 불가능해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 인터넷 &amp;rarr; 웹 서버 패킷은?&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초의 패킷은 수신처가 웹 서버이고 컨트롤 비트를 확인했을 때 최초의 패킷이므로 패킷 필터링을 통과한다. 두 번째 패킷은 송신처가 웹 서버이지만 컨트롤 비트 값이 바뀌었기에 최초값으로 추정되지 않고 패킷 필터링을 잘 통과하여 통신에 성공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ UDP는 TCP와 달리 접속관계(SYN, ACK)가 없어서 액세스 방향 판단이 불가능하다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어느 정도 위험을 각오한 상태에서 애플리케이션의 패킷을 전부 통과&lt;/li&gt;
&lt;li&gt;불편을 감수하고 애플리케이션을 전면적으로 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-5) 사내 LAN에서 공개 서버용 LAN으로 조건 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사내 LAN &amp;harr; 공개 서버용 LAN 각각의 IP 주소를 양쪽 모두 잘 설정해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;❓ 2-6) 밖에서 사내 LAN으로 액세스 할 수 없다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 패킷 필터링형 방화벽은 주소 변환의 기능도 가지고 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;사내 LAN &amp;harr; 인터넷을 왕래하는 패킷은 주소 변환이 필요하다.&lt;/span&gt; 인터넷에 있는 라우터는 Private 주소를 테이블에 등록하지 않기 때문이다. 따라서 이 구조의 패킷 통신에서는 주소 변환이 필요하다. &amp;rarr; 주소 변환을 할 지/ 하지않을 지에 대해 설정해주면 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;패킷 필터링형 방화벽의 내장 라우터는 Private 주소도 OK.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-7) 방화벽을 통과한다&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조건에 따라 차단 대상이 된 패킷은 버리고, 버린 기록을 남긴다. 이를 분석하여 침입자의 수법 확인, 향후 부정 침입에 대한 대책 등으로 보안을 강화한다.&lt;/li&gt;
&lt;li&gt;통과된 패킷의 중계 동작은 라우터의 동작과 같다. &amp;rarr; 라우터에 부가기능을 넣은 것과 같다고 생각하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-8) 방화벽으로 막을 수 없는 공격&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 웹 서버에 취약점이 있어 특수한 데이터를 포함한 패킷을 받으면 서버가 다운되는 상황일 때&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방화벽에 설정한 송수신 IP와 포트만 확인한다. 패킷의 데이터는 전혀 고려하지 않기에 방화벽은 이러한 구조에 대처할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;대처법은?&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;웹 서버의 취약점/버그 고치기&lt;/li&gt;
&lt;li&gt;패킷 내용을 조사하여 해당 데이터가 포함된 패킷을 차단하는 장치를 별도로 두기&lt;/li&gt;
&lt;li&gt;미지의 위험성은 완벽히 대처하기 어렵다&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;STORY 3 | 복수 서버에 리퀘스트를 분배한 서버의 부하 분산&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;3-1) 처리 능력이 부족하면 복수 서버로 부하 분산된다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 서버에 액세스가 증가할 때&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;회선을 빠르게 하기 &amp;rarr; 서버의 처리 능력이 따라잡지 못할 수 있다.&lt;/li&gt;
&lt;li&gt;서버 머신을 고성능으로 교체하기 &amp;rarr; 한계가 있다.&lt;/li&gt;
&lt;li&gt;&amp;rArr; 복수 서버를 사용하여 서버 한 대당 처리량을 줄인다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 여러 대의 웹 서버를 설치하고 한 대가 담당하는 사용자 수를 줄인다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS 서버에서 분배&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DNS 서버에 같은 이름으로 여러 대의 웹 서버를 등록한다. DNS 서버는 조회가 있을 때마다 차례대로 IP 주소를 되돌려 준다. &amp;rArr;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;라운드 로빈&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;192.0.2.60 &amp;rarr; 192.0.2.70 &amp;rarr; 192.0.2.80&lt;/li&gt;
&lt;li&gt;192.0.2.70 &amp;rarr; 192.0.2.80 &amp;rarr; 192.0.2.60&lt;/li&gt;
&lt;li&gt;192.0.2.80 &amp;rarr; 192.0.2.60 &amp;rarr; 192.0.2.70&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;DNS는 웹 서버의 상태를 확인하지 않고 중계만 하므로, 웹 서버가 중단/고장된 상황에 대응하기 어렵다.&lt;/li&gt;
&lt;li&gt;라운드 로빈을 통해 웹 서버를 분배하면 웹 사이트의 연속성이 끊긴다. 예를 들어서, 첫 번째 페이지에서 성명 입력 후에 두 번째 페이지에서 신용카드 번호를 입력하는 케이스가 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;TO-DO : GCP LB 라운드 로빈&amp;hellip;.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;3-2) 부하 분산 장치를 이용해 복수의 웹 서버로 분할된다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부하 분산 장치 IP를 웹 서버 IP 대신 DNS에 등록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 대화가 복수의 페이지에 걸쳐있다면&amp;hellip;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP stateless: HTTP의 기본 동작은 먼저 Request Message를 보내기 전에 TCP 연결을 하고, 전송이 끝나면 연결을 끊는다.&lt;br /&gt;Cookie: HTTP 전후 관계를 파악하기 위한 값을 HTTP 헤더 필드에 부가하는 방법이 고안되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY 4 | 캐시 서버를 이용한 서버의 부하 분산&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-1) 캐시 서버의 이용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;■ &lt;b&gt;캐시 서버는 프록시 구조를 사용하여 데이터를 캐시에 저장하는 서버다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시 서버는 웹 서버의 &lt;span style=&quot;color: #006dd7;&quot;&gt;데이터를 디스크에 저장한다.&lt;/span&gt; 이후, 클라이언트에서 요청이 오면 이를 확인해서 바로 송신한다. 즉 데이터를 읽고 송신만 하므로 서버보다 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 캐시 서버 또한, 부하 분산처럼 웹 서버 대신 DNS에 캐시 서버 IP를 등록한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-2) 캐시 서버는 갱신일로 콘텐츠를 관리한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 두 가지 케이스&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RLjHp/btsKgcQlsOX/kRutE03LoE5fkkDfOL6LyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RLjHp/btsKgcQlsOX/kRutE03LoE5fkkDfOL6LyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RLjHp/btsKgcQlsOX/kRutE03LoE5fkkDfOL6LyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRLjHp%2FbtsKgcQlsOX%2FkRutE03LoE5fkkDfOL6LyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;514&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1157&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장되어 있든 없든, 일단 리퀘스트를 전송하는 것까진 동일하다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 데이터가 캐시 서버에 저장되어 있지 않은 경우&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;985&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFMhkV/btsKhJsbNhn/JH7pPc0K60hPqYbrH6H3QK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFMhkV/btsKhJsbNhn/JH7pPc0K60hPqYbrH6H3QK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFMhkV/btsKhJsbNhn/JH7pPc0K60hPqYbrH6H3QK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFMhkV%2FbtsKhJsbNhn%2FJH7pPc0K60hPqYbrH6H3QK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;438&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;985&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;헤더 중에 Via 필드 &amp;amp; URI의 디렉토리(어떤 웹 서버에 캐시 정보를 요청해야 할 지 판단)&amp;rarr; URI의 디렉토리를 보고, 리퀘스트 주인이 어느 웹 서버인지 판단하여 소켓 통신을 한다. &amp;rarr; 캐시 서버는 요청을 받으면&lt;span style=&quot;color: #006dd7;&quot;&gt; &amp;lsquo;Via&amp;rsquo; 헤더 필드&lt;/span&gt;를 추가하여 웹 서버에 리퀘스트를 보낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐시에 데이터가 없으면 &lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;lsquo;If-Modified-Since&amp;rsquo;를 추가하지 않고 그대로&lt;/span&gt; 요청을 보내서 웹 서버는 데이터를 그대로 전송해준다. 이후, 캐시 서버는 이 내용들을 캐시로 저장해둔다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 데이터가 캐시 서버에 저장되어 있는 경우&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1097&quot; data-origin-height=&quot;948&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRzooO/btsKf6WUnXY/pyo3i85bnsWCyailpkNbRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRzooO/btsKf6WUnXY/pyo3i85bnsWCyailpkNbRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRzooO/btsKf6WUnXY/pyo3i85bnsWCyailpkNbRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRzooO%2FbtsKf6WUnXY%2Fpyo3i85bnsWCyailpkNbRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;432&quot; data-origin-width=&quot;1097&quot; data-origin-height=&quot;948&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lsquo;If-Modified-Since&amp;rsquo; 필드를 통해서 캐시: &amp;ldquo;이때의 값을 디스크에 저장하고 있다.&amp;rdquo; 라고 알린다. 서버는 이 값을 확인하고, 변경이 없었던 경우에는 304 Not Modified를 보내준다. &amp;rarr; 이때 서버는 데이터의 최종 갱신 일시를 조사하는 것으로 끝나고, 응답 메시지 내용도 짧아서 부담이 훨씬 적어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 변경된 경우, 웹 서버는 최신 데이터를 반송한다. 캐시서버는 &amp;lsquo;Via&amp;rsquo; 헤더를 추가하여 사용자에게 전송하고 데이터를 캐시에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-3) 프록시의 원점은 포워드 프록시다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 포워드 프록시는 클라이언측에 두는 캐시 서버로, 프록시 서버의 원형이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 포워드 프록시가 등장했을 때는 방화벽을 실현하는 목적이 있었다. 앞서 &lt;b&gt;패킷 필터링형 방화벽&lt;/b&gt;은 단순히 IP주소와 포트로 패킷 송수신을 제어한다. 그러면 패킷의 내용 등에 대한 보안 설정을 하기에 어려움이 있다. 이를 해결하기 위해서 포워드 프록시를 방화벽으로 사용한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 프록시는 리퀘스트의 내용을 조사한 후 전송하므로 리퀘스트의 내용에 따라 액세스가 가능한지 판단할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 포워드 프록시를 사용할 때에는 브라우저 설정 화면으로 세팅한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 브라우저의 설정 화면에 준비되어 있는 프록시 서버라는 항목에 포워드 프록시의 IP 주소를 설정한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;포워드 프록시를 설정하면 URL과 상관없이 모든 요청이 포어드 프록시로 들어오며, http://&amp;hellip;.를 그대로 request에 작성한다. &amp;rarr; 캐시 서버와 같이 어느 서버에 보낼 지 판단하지 않아도 되고, 모든 웹 서버에 전송할 수 있다.&lt;/li&gt;
&lt;li&gt;포워드 프록시를 설정하지 않으면 http://&amp;hellip;..에서 웹 서버의 이름을 제외하고, 파일이나 프로그램의 경로명의 일부를 추출하여 이것을 리퀘스트의 URI 부분에 기록한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;URL과 URI의 차이&lt;/span&gt; &amp;rArr; URL은 요청을 보낼 서버의 주소를 찾는 거고, URI는 해당 주소에서 이 요청을 처리하는 방을 찾는 것&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-4) 포워드 프록시를 개량한 리버스 프록시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 포워드 프록시를 사용하기 위해 반드시 해야하는 브라우저 설정은 수고나 장애의 원인 및 제약사항이 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 따라 브라우저에 프록시를 설정하지 않아도 사용할 수 있도록 개량되었다. 즉, URI에 http:// &amp;hellip; 이 부분을 모두 적지 않아도 리퀘스트 메시지를 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 포워드 프록시 vs. 리버스 프록시&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목록 포워드 프록시 리버스 프록시&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;설정&lt;/td&gt;
&lt;td&gt;브라우저에 직접&lt;/td&gt;
&lt;td&gt;프록시 서버 IP를 DNS에 등록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;URI 형식&lt;/td&gt;
&lt;td&gt;&amp;lsquo;http://&amp;hellip;.&amp;rsquo; 모두&lt;/td&gt;
&lt;td&gt;URI 경로만 &amp;rarr; 클라이언트가 URI의 세부 정보를 입력하지 않고도 서비스에 접근할 수 있도록 도움. 즉, 서버의 요청 중개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;위치&lt;/td&gt;
&lt;td&gt;클라이언트와 서버 사이에&lt;/td&gt;
&lt;td&gt;서버 측에&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;활용&lt;/td&gt;
&lt;td&gt;보안, 캐싱 등&lt;/td&gt;
&lt;td&gt;부하 분산, 종료, SSL 등&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-5) 트랜스페어런트 프록시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 패킷의 IP 헤더를 조사하여 액세스 대상 웹서버를 알아내는 방법을 트랜스페어런트 프록시라고 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통의 리퀘스트 메시지와 같이 요청값을 작성할 수 있어서, 포워드 프록시처럼 브라우저에 설정할 필요가 없다. 또한, 리버스 프록시처럼 프록시 서버 IP를 DNS에 등록하는 것이 아니다. 따라서 브라우저에서 웹 서버로 리퀘스트 메시지가 흘러가는 길에 트랜스페어런트 프록시를 설치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY 5 | 콘텐츠 배포 서비스&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1) 콘텐츠 배포 서비스를 이용한 부하 분산&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 캐시 서버를 놓는 장소가 어디인지에 따라 장단점이 있다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 쪽에 배치
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 서버 부하만 억제, 인터넷 트래픽 X&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;클라이언트 쪽에 배치
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;혼잡에 휘말려드는 일이 없으므로, 패킷의 흐름이 안정된다.&lt;/li&gt;
&lt;li&gt;클라이언트 측의 네트워크 관리자가 소유하므로, 웹 서버 관리자가 접근할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;양쪽의 장점을 활용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트 측의 프로바이더에 캐시 서버를 두고, 이를 서버 관리자가 컨트롤 할 수 있다.&lt;/li&gt;
&lt;li&gt;인터넷 트래픽을 억제하는 효과 + 웹 서버 관리자가 캐시 서버를 관리할 수 있다&lt;/li&gt;
&lt;li&gt;인터넷에 공개하는 서버는 인터넷의 어디에서 액세스하는지 알 수 없다. 따라서 이 방법을 실행하기 위해 프로바이더의 POP 전부에 캐시 서버를 설치해야 하므로 비현실적이다. 이를 해결하기 위해 CDS 방법을 쓴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ CDS(Content Delivery Service)&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시 서버를 설치하고, 웹 서버 운영자에게 대출하는 서비스다. 이 서비스를 제공하는 사업자는 프로바이더와 계약해서 캐시 서버를 설치하고, 이를 판매한다.&amp;nbsp;CDSP가 설치한 캐시 서버는 여러 웹 서버 운영자가 공동으로 이용 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-2) 가장 가까운 캐시 서버의 관점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ DNS 동작 방식과 라우팅 테이블을 활용한다.&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;인터넷에 총 4개의 캐시 서버가 있다고 가정했을 때, 각각 장소의 라우터에서 경로 정보를 모아둔다. 이 각각의 라우터에서 경로 정보를 모아 DNS 서버에 저장한다.&lt;/li&gt;
&lt;li&gt;이 테이블로 DNS 메시지의 송신처(클라이언트)에 이르는 경로를 조사한다. (DNS 연대로 조사)&lt;/li&gt;
&lt;li&gt;모든 라우터에 대해 조사하고 비교하면 어느 캐시 서버의 라우터가 클라이언트와 가까운지 알 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-3) 리피터용 서버로 액세스 대상을 분배한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 리다이렉트 서버와 HTTP Location 요청 헤드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Location:&amp;nbsp; 그 데이터는 이쪽 서버에 있으므로, 그쪽으로 다시 액세스하세요. 이런 접근을 리다이렉트라고 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클라이언트가 웹 서버의 IP 주소를 조회하면 DNS 서버는 라다이렉트용 서버의 IP 주소를 회답한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리다이렉트용 서버를 웹 서버 측의 DNS 서버에 등록해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;리다이렉트용 서버가 가장 가까운 캐시 서버를 찾아서 Location 헤더를 붙여 응답을 돌려 보내면 클라이언트는 캐시 서버에 다시 액세스 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리다이렉트용 서버도 DNS 서버와 같이 라우터 정보를 모은 테이블이 있다.&lt;/li&gt;
&lt;li&gt;리다이렉트는 클라이언트가 보내는 HTTP 메시지의 송신처 IP 주소를 바탕으로 거리를 판단하므로 정밀도가 높다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;패킷의 왕복시간을 계산하여 최적의 캐시 서버에 액세스 하도록 스크립트 프로그램을 내장한 페이지를 반송하는 방법도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-4) 캐시 내용의 갱신 방법에서 성능의 차이가 난다&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최초에 저장 후, 두 번째에 갱신 &amp;rarr; 비효율&lt;/li&gt;
&lt;li&gt;이를 방지하기 위해서 웹 서버에서 데이터를 갱신할 경우, 즉시 캐시 서버도 갱신하는 구조&lt;/li&gt;
&lt;li&gt;이때 동적인 부분과 정적인 부분을 구분하고 변하지 않는 부분만 캐시에 저장&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>CS/Network</category>
      <category>CS</category>
      <category>network</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/299</guid>
      <comments>https://cobinding.tistory.com/299#entry299comment</comments>
      <pubDate>Thu, 24 Oct 2024 00:41:41 +0900</pubDate>
    </item>
    <item>
      <title>[BackEnd] 커뮤니티 게시물 목록 조회 API 쿼리를 QueryDsl로 구현해보기</title>
      <link>https://cobinding.tistory.com/298</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코알라 커뮤니티 기능 중 &lt;b&gt;게시판 목록 조회 API&lt;/b&gt;를 위해 &lt;b&gt;QueryDsl&lt;/b&gt;을 학습 및 구현해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요구사항은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정렬 (최신순, 조회수, 좋아요수)&lt;/li&gt;
&lt;li&gt;검색 (제목, 내용, 작성자)&lt;/li&gt;
&lt;li&gt;페이징&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세 가지 조건 하에, DB에서 값을 가져오기 위해 QueryDsl을 활용하였다. &lt;br /&gt;* &lt;a href=&quot;https://www.notion.so/JPQL-vs-QueryDsl-1248ac8cc8948051a4c9f73e266550be?pvs=21&quot;&gt;JPQL 대신 QueryDsl을 사용한 이유&amp;nbsp;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;필요한 Entity, Request&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게시판 전체 목록을 조회할 때 단순히 GET을 하는 것이 아니라, &lt;u&gt;개요에서 언급한 3가지 조건을 만족시켜야 한다.&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zJKsm/btsKeAjw0c8/8yzIAXBu8sQhWgg0QjdK30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zJKsm/btsKeAjw0c8/8yzIAXBu8sQhWgg0QjdK30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zJKsm/btsKeAjw0c8/8yzIAXBu8sQhWgg0QjdK30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzJKsm%2FbtsKeAjw0c8%2F8yzIAXBu8sQhWgg0QjdK30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;368&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;368&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조회 조건을 만족하기 위해 다음과 같은 &lt;span style=&quot;color: #000000; background-color: #f6e199;&quot;&gt;Request record&lt;/span&gt;를 생성한다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Schema(description = &quot;게시글 목록 검색 요청 객체&quot;)
public record SearchBoardRequest(

    @Schema(description = &quot;검색 키워드(게시글 제목, 게시글 내용, 작성자)&quot;)
    String searchKeyword,

    @Schema(description = &quot;게시글 카테고리&quot;)
    BoardCategory category,

    @Schema(description = &quot;게시글 정렬 조건&quot;, allowableValues = {&quot;LATEST&quot;, &quot;LIKE&quot;, &quot;VIEW_COUNT&quot;})
    BoardSort sort,

    @Min(1)
    @Schema(description = &quot;페이지 번호&quot;, type = &quot;integer&quot;, requiredMode = RequiredMode.REQUIRED)
    int page,

    @Min(10)
    @Schema(description = &quot;페이지별 개수&quot;, type = &quot;integer&quot;, requiredMode = RequiredMode.REQUIRED)
    int size
) {

    public PageRequest pageRequest() {
        return PageRequest.of(page - 1, size);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;PageRequestJPA&lt;/span&gt;에서 제공하는 페이지 번호는 0부터 시작한다. 하지만 웹사이트 상에서의 페이지는 1부터 시작하므로 항상 -1을 설정한다.&lt;br /&gt;- 이 Request 쿼리를 실행하면 결과로 &lt;span style=&quot;color: #ee2323;&quot;&gt;Page&amp;lt;T&amp;gt;&lt;/span&gt; 객체가 반환되는데, 전체 데이터 수, 페이지 수, 현재 페이지의 데이터 목록 등을 포함하고 있다. 요청한 페이지 번호와 페이지 크기(한 페이지에 포함될 데이터 수)를 기반으로, DB 쿼리에서 데이터 범위를 지정할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;Request 객체를 record로 설정&lt;/span&gt;하는 이유
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;기본적으로 불변이라서 일관성 유지에 좋다.&lt;/li&gt;
&lt;li&gt;생성자, toString() , hashCode(), equals() 메소드를 자동으로 생성한다.&lt;/li&gt;
&lt;li&gt;DTO(데이터 전송 객체)로 사용되어, 필드의 의미가 명확하다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전체 코드를 나눠서 쿼리 살펴보기&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. select로 조건에 따른 결과를 Dto 형태로 반환&lt;/h4&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;  List&amp;lt;ListBoardDto&amp;gt; boardList =
      queryFactory
        .select(Projections.fields(
            ListBoardDto.class,
            board.id.as(&quot;boardId&quot;),
            board.category.stringValue().as(&quot;category&quot;),
            board.title,
            board.member.name.as(&quot;createdName&quot;),
            board.createdTime,
            Expressions.booleanTemplate(
                &quot;case when {0} &amp;gt; {1} then true else false end&quot;,
                board.createdTime, LocalDateTime.now().minusDays(3))
              .as(&quot;newBoardYn&quot;),
            board.viewCount,
            board.fixYn,
            board.deleteYn
          )
        )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;category는 다음과 같은 &lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt;Enum&lt;/span&gt; 타입으로 정의되어 있다.&lt;br /&gt;이런 enum 값을 문자열로 변환해야 DB에서 사용할 수 있다. 따라서 &lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt;stringValue()&lt;/span&gt;로 변환해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1729611922543&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@AllArgsConstructor
public enum BoardCategory {
    NOTICE(&quot;공지&quot;),
    FREE(&quot;자유&quot;),
    QUESTION(&quot;질문&quot;),
    INFO(&quot;정보&quot;),
    PROMOTION(&quot;홍보&quot;);

    private final String name;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Expressions.booleanTemplate&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;최신글 여부를 위한 조건부로직(SQL의 CASE)이다. Template을 사용해서 복잡한 SQL 쿼리 로직을 동적으로 보다 간편하게 작성할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. where 절로 카테고리, 키워드 설정&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;.where(
    searchCategory(request.category()),
    searchKeyword(request.searchKeyword()),
    board.saveYn.isTrue()
)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt;searchCategory&lt;/span&gt; 메소드에 따라 게시물의 카테고리랑 요청 카테고리랑 비교해서 일치하는 게시물만 반환한다.board 객체의 category를 QueryDsl의 &lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt;eq&lt;/span&gt; 메소드로 일치하는지 비교한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1729611959447&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static Predicate searchCategory(BoardCategory category) {
    if (category == null) {
      return null;
    }
    return board.category.eq(category);
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt;searchKeyword&lt;/span&gt;도 마찬가지.&lt;br /&gt;그런데 키워드는 세 가지 옵션이 가능하기에 다음과 같은 &lt;span style=&quot;color: #ee2323;&quot;&gt;BooleanBuilder&lt;/span&gt;로 구현한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1729612002318&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  private Predicate searchKeyword(String searchKeyword) {
        if (!StringUtils.hasText(searchKeyword)) {
            return null;
        }
        BooleanBuilder builder = new BooleanBuilder();
        builder.or(board.title.contains(searchKeyword));
        builder.or(board.content.contains(searchKeyword));
        builder.or(board.member.name.contains(searchKeyword));
        return builder;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt;builder.or(...)&lt;/span&gt;를 사용하여 여러 조건을 결합하면, 주어진 키워드가 제목이나 내용, 작성자 이름 중 하나라도 포함될 경우 조건이 참이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;BooleanBuilder&lt;/b&gt;&lt;br /&gt;조건을 or, and로 결합할 때 가독성이 좋아진다.&lt;br /&gt;여러 조건을 동적으로 추가할 수 잇다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서 처음 QueryDsl을 적용하며 Template이나 where절에 넣는 메소드 등 배운 점이 많아서 정리해보았다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Dev/Backend</category>
      <category>Back-end</category>
      <category>querydsl</category>
      <category>spring</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/298</guid>
      <comments>https://cobinding.tistory.com/298#entry298comment</comments>
      <pubDate>Wed, 23 Oct 2024 00:57:22 +0900</pubDate>
    </item>
    <item>
      <title>[백준/1987] 알파벳 | DFS와 백트래킹</title>
      <link>https://cobinding.tistory.com/297</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1987&quot;&gt;알파벳&lt;/a&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만의 백준 포스팅!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코테에서 부족함을 절실히x99 느끼고 다시 알고리즘을 본격적으로 시작했다.. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1987&quot;&gt;문제 바로 가기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;알고리즘&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말이 보드를 탐색하는 조건은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;① (1,1)부터 탐색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;② &lt;u&gt;지금까지 지나온 모든 칸에 적혀있는 알파벳과 달라야&lt;/u&gt; 이동할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 조건 아래에서 가능한 경로 중 최대한 큰 수를 구하는 것이 목표다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, DFS를 통해 탐색하는 방법을 선택했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728654690295&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def dfs(y,x,cnt):
    visited[y][x] = True
    alphabet[ord(graph[y][x])-65] = True
   # print('시작점: ', graph[y][x])

    # 현재 위치에서 네 방향의 위치 확인
    for dy, dx in dr:
        ny, nx = y+dy, x+dx
        if 0 &amp;lt;= ny &amp;lt; r and 0 &amp;lt;= nx &amp;lt; c :
            alpha = graph[ny][nx]
            #print('탐색 과정: ', alpha, &quot; 좌표: &quot;, ny,nx)
            if not alphabet[ord(alpha)-65]:
                visited[ny][nx] = True
                alphabet[ord(alpha)-65] = True
                cnt = dfs(ny,nx,cnt+1)
    return cnt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS는 모든 경로에 대해서 깊이 있는 탐색을 한다. 하지만 &lt;span style=&quot;color: #ee2323;&quot;&gt;도착지점이 중복되면 다른 경로는 탐색하지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2506&quot; data-origin-height=&quot;1776&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/StpdO/btsJ21VgO4d/AaaWKui80V5DYEFnSjb4RK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/StpdO/btsJ21VgO4d/AaaWKui80V5DYEFnSjb4RK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/StpdO/btsJ21VgO4d/AaaWKui80V5DYEFnSjb4RK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FStpdO%2FbtsJ21VgO4d%2FAaaWKui80V5DYEFnSjb4RK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;350&quot; height=&quot;248&quot; data-origin-width=&quot;2506&quot; data-origin-height=&quot;1776&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nqcjn/btsJ4s4RvnX/eGu41fB0lf71S6WqwwPkSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nqcjn/btsJ4s4RvnX/eGu41fB0lf71S6WqwwPkSk/img.png&quot; data-alt=&quot;재귀 호출 되면 바로 방문 처리를 하니까 아래 if에서 걸림&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nqcjn/btsJ4s4RvnX/eGu41fB0lf71S6WqwwPkSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnqcjn%2FbtsJ4s4RvnX%2FeGu41fB0lf71S6WqwwPkSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;548&quot; height=&quot;114&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;114&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;재귀 호출 되면 바로 방문 처리를 하니까 아래 if에서 걸림&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;① A-B-C, ② A-D-C 경로 중에 ①이 먼저 탐색되면 ②는 탐색하지 않는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 백트래킹은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;방문 시도 - 재귀 탐색 - 방문 해제 &lt;/span&gt;이 과정을 거쳐서, 목적지 노드가 중복되더라도 여러 경로를 탐색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DFS는 모든 노드를 최대한 깊게 탐색하지만, 목적지가 중복되는 경로라면 하나의 경로만 탐색함 =&amp;gt;&lt;span style=&quot;color: #006dd7;&quot;&gt; O(V+E)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;백트래킹은 3단계를 통해서 여러 노드를 탐색한다. (각 단계마다 최선의 선택) =&amp;gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;O(2^V)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;이 문제는 탐색 문제인데도 범위가 낮은 이유가 있엇다..! 이걸 보고 백트래킹으로 최장경로를 탐색한다고 유추할 수 있으면 좋았을 것..&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2086&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beqIl3/btsJ3fZYZU9/BMHol5cLAfYs1qDmG6QOS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beqIl3/btsJ3fZYZU9/BMHol5cLAfYs1qDmG6QOS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beqIl3/btsJ3fZYZU9/BMHol5cLAfYs1qDmG6QOS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeqIl3%2FbtsJ3fZYZU9%2FBMHol5cLAfYs1qDmG6QOS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;107&quot; data-origin-width=&quot;2086&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전체 코드&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;pre id=&quot;code_1728655480836&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sys import stdin, setrecursionlimit
setrecursionlimit(3000)
input=stdin.readline

r,c = map(int, input().split())
graph = [input().rstrip() for _ in range(r)]
alphabet = [False] * (ord('Z')-ord('A')+1)
dr = [(1,0),(-1,0),(0,1),(0,-1)]
max_cnt = 0

def dfs(y,x,cnt):
    global max_cnt
    alphabet[ord(graph[y][x])-65] = True

    max_cnt = max(max_cnt, cnt)
    for dy, dx in dr:
        ny, nx = y+dy, x+dx

        if 0 &amp;lt;= ny &amp;lt; r and 0 &amp;lt;= nx &amp;lt; c :
            alpha = graph[ny][nx]
            if not alphabet[ord(alpha)-65]:
                alphabet[ord(alpha)-65] = True # 노드 방문
                dfs(ny,nx,cnt+1) # 재귀 탐색
                alphabet[ord(alpha) - 65] = False # 방문 해제!

dfs(0,0,1)
print(max_cnt)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서도 DFS에서는 cns를 누적하고, return 하는 식으로 구현했는데 백트래킹은 탐색하는 경로마다 max값을 찾아주는 식으로 구현을 수정했다. 또한 좌표 자체에 대한 방문 체크 대신 alphabet 방문 체크만 잘 해주면 되니까 좌표 방문 체크는 없애주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;알게된 점&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;백트래킹 - DFS 차이&lt;/li&gt;
&lt;li&gt;백트래킹으로 최장경로 찾기 ➡️ 이게 DFS로 안 되는 이유&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/BOJ&amp;amp;Programmers</category>
      <category>graph</category>
      <category>Problem Solving</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/297</guid>
      <comments>https://cobinding.tistory.com/297#entry297comment</comments>
      <pubDate>Fri, 11 Oct 2024 23:07:25 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 네트워크 원리(4) 액세스 회선을 통해 인터넷의 내부로</title>
      <link>https://cobinding.tistory.com/296</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;성공과 실패를 결정하는 1% 네트워크 원리를 공부하고 정리한 글이다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Chapter 4 - 액세스 회선과 프로바이더의 탐험&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5장과 더불어 가장 생소하고 어려웠던 내용....   라우터부터 액세스 회선에서 이동하는 부분은 잘 알아두면 좋을 듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;1248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1tY2g/btsKgfzivPf/Lhpx3igaMw9EjaNL7WraG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1tY2g/btsKgfzivPf/Lhpx3igaMw9EjaNL7WraG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1tY2g/btsKgfzivPf/Lhpx3igaMw9EjaNL7WraG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1tY2g%2FbtsKgfzivPf%2FLhpx3igaMw9EjaNL7WraG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;358&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;1248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY 1 | ADSL 기술을 이용한 액세스 회선의 구조와 동작&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-1) 인터넷의 기본은 가정이나 회사의 LAN과 같다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패킷을 중계하는 부분과 라우터의 기본적인 구조/동작은 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 인터넷과 LAN의 차이점&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;중계 장치 간의 거리 : 가정이나 회사의 LAN은 멀어봤자 수백미터 정도이지만, 인터넷은 한국과 미국을 연결하는 부분은 태평양을 넘어야 하므로 케이블로 연결하는 것이 어려울 정도로 중계 장치 간의 거리가 멀다.&lt;/li&gt;
&lt;li&gt;패킷의 중계 대상을 제어하는 부분 - 히 경로 정보를 테이블에 등록하는 점 : 인터넷의 라우터는 경로 정보가 10만 개 이상 등록되어 있고, 이는 시시각각 변한다. 이 모든 걸 사람이 통제하기 어렵기 때문에 &lt;span style=&quot;color: #ee2323;&quot;&gt;자동화해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-2) 사용자와 인터넷을 연결하는 액세스 회선&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패킷 IP 헤더의 수신처 IP와 테이블을 비교하여 해당 위치로 보낸다는 점(&lt;b&gt;패킷 중계 동작&lt;/b&gt;)은 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 이더넷과 인터넷의 패킷 송신 동작의 차이점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 인터넷 접속용 라우터는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;액세스 회선의 규칙에 따라&lt;/span&gt; 패킷 송신 동작을 실행한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;액세스 회선(통신회선, 접근회선)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반 가정이라면 ADSL, FTTH, CATV, 전화회선 등을 이용한다. 회사의 경우 전용 회선이 추가된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ADSL(Asymmetric Digital Subscriber)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전봇대에 부설된 전화용 금속제 케이블을 이용하여 고속으로 통신하는 기술&lt;/li&gt;
&lt;li&gt;상향(사용자 &amp;rarr; 인터넷 : 업로드)과 하향(인터넷 &amp;rarr; 사용자 : 다운로드)의 통신 속도가 서로 다르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-3) ADSL 모델에서 패킷을 셀로 분할한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 인터넷 접속용 라우터를 거친 패킷은 총 3가지&lt;span style=&quot;background-color: #f6e199;&quot;&gt;(PPP, PPPoE, MAC) 헤더&lt;/span&gt;가 붙는다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5XLQb/btsJQfGAFxl/mJRsL3V9MBmQxJgU29if8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5XLQb/btsJQfGAFxl/mJRsL3V9MBmQxJgU29if8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5XLQb/btsJQfGAFxl/mJRsL3V9MBmQxJgU29if8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5XLQb%2FbtsJQfGAFxl%2FmJRsL3V9MBmQxJgU29if8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;667&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1668&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;■ ADSL 모뎀은 패킷을 셀 단위로 작게 분할한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;셀: 맨 앞 부분에 헤더(5 BYTE)와 데이터(48 BYTE)로 이어지는 작은 데이터의 덩어리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ATM 통신 기술에 사용하기 위해서 &lt;b&gt;패킷 &amp;rarr; 셀로 분할&lt;/b&gt;한다. 셀을 사용하는 상태로 두면 전반적인 설비들과 연대할 수 있기에 패킷을 굳이 셀로 분할한다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;** 셀로 분할하지 않는 ADSL 사업자도 있다! 반드시 써야하는 건 x 설계에 따라 달라진다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ATM(Asynchronous Transfer Mode)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전화 회선의 개념에 기초한 구형 전화 기술의 연장선에 있는 통신 방식&lt;/li&gt;
&lt;li&gt;셀을 운반하는 부분은 TCP/IP와 비슷(but, 컴퓨터 통신에는 적합하지 않고 인터넷 통신에 적합하다!)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-4) ADSL은 변조 방식으로 셀을 신호로 변환한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ LAN의 경우, 사각형 신호를 통해 0과 1을 나타내는 신호로 변환했지만 ADSL은 좀 더 복잡한 방식을 쓴다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;사각형 신호의 경우&amp;hellip;&lt;/i&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파형이 뭉개지기 쉽다.&lt;/li&gt;
&lt;li&gt;거리가 떨어졌을 때 오류가 발생하기 쉽다.&lt;/li&gt;
&lt;li&gt;넓은 범위의 주파수가 포함되어 있다. (낮은 주파수 ~ 높은 주파수 모두 케어) 즉, 잡음을 제어하기 어렵다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 직교 진폭 변조(QAM) = 진폭 변조(ASK) + 위상 변조(PSK)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;893&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvKxvL/btsJQ4jUqLg/UkRBHnqWH6XMYxpHiLCvn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvKxvL/btsJQ4jUqLg/UkRBHnqWH6XMYxpHiLCvn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvKxvL/btsJQ4jUqLg/UkRBHnqWH6XMYxpHiLCvn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvKxvL%2FbtsJQ4jUqLg%2FUkRBHnqWH6XMYxpHiLCvn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;318&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;893&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;진폭 변조&lt;/b&gt; (그림의 빨간색 부분)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파동의 진폭 크기에 따라 0과 1을 대응&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;b&gt;위상 변조&lt;/b&gt; (그림의 파란색 부분)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파는 일주기만에 원래의 모습으로 돌아간다. 이 주기별로 파의 각도를 나타낸 것을 위상이라고 한다.&lt;/li&gt;
&lt;li&gt;위상변조는 이 위상에 따라 비트를 대응하는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;직교 진폭 변조&lt;/b&gt;는 신호의 진폭과 위상에 각각 1비트를 대응시켜, 한 개의 파에 2비트 분의 데이터를 대응하여 두 방식을 결합한 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-5) ADSL은 파를 많이 사용하여 고속화를 실현한다.&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;주파수가 서로 다른 파를 혼합하여 파를 합성한다.&lt;/li&gt;
&lt;li&gt;특정 주파수만 통과 가능한 필터 회로를 통해 주파수마다 파를 분리한다. 이를 통해 여러 주파수의 파를 합성한 것을 신호로 사용하는 것도 가능하다.&lt;/li&gt;
&lt;li&gt;다수의 파에 비트값을 대응시켜 고속화한다.&lt;/li&gt;
&lt;li&gt;잡음이 없는 주파수의 파에는 다수의 비트를 대응시키고, 잡음이 많은 주파수의 파에는 소수의 비트를 대응시킨다. 모든 비트 수의 합계로 전체 전송 속도가 결정된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*ADSL은 다운로드 속도가 더 빠르다! &amp;rarr; 비트 수 차이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-6) 스플리터의 역할&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s5xRG/btsJRBBw5Ru/QqPngpi1pKcUnrIKtuAPK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s5xRG/btsJRBBw5Ru/QqPngpi1pKcUnrIKtuAPK1/img.png&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1668&quot; data-is-animation=&quot;false&quot; width=&quot;400&quot; height=&quot;593&quot; style=&quot;width: 27.7767%; margin-right: 10px;&quot; data-widthpercent=&quot;28.1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s5xRG/btsJRBBw5Ru/QqPngpi1pKcUnrIKtuAPK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs5xRG%2FbtsJRBBw5Ru%2FQqPngpi1pKcUnrIKtuAPK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1125&quot; height=&quot;1668&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eyq7bI/btsJQzxT00K/Ak5zXlK7aZUQDXkmtrU3dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eyq7bI/btsJQzxT00K/Ak5zXlK7aZUQDXkmtrU3dk/img.png&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;652&quot; data-is-animation=&quot;false&quot; style=&quot;width: 71.0605%;&quot; data-widthpercent=&quot;71.9&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eyq7bI/btsJQzxT00K/Ak5zXlK7aZUQDXkmtrU3dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feyq7bI%2FbtsJQzxT00K%2FAk5zXlK7aZUQDXkmtrU3dk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1125&quot; height=&quot;652&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ ADSL 모뎀과 DSLAM 모델 사이에서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;ADSL용 신호와 전화용 신호를 분리&lt;/span&gt;한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전화기 측에 ADSL 신호가 흐르지 않도록 한다. &amp;rarr; 전화기에는 전화 신호만 흐르고 ADSL 모뎀으로 향하는 신호는 그대로 흐른다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 스플리터는 전화기로 들어오는 ADSL을 차단할 뿐만 아니라, ADSL로 가는 전화기의 영향도 차단하는 기특한 기능을 가졌다!
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스플리터를 통해서 수화기를 들고 / 내려논 경우의 신호 전달을 제어할 수 있다. 만약 스플리터가 없다면 수화기를 든/내린 경우의 신호 전달 방향이 달라서 ADSL에서 트레이닝을 이에 맞춰 다시 실행해야 한다. 그러면 그 사이에 통신이 수십 초 중단된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-7) 전화국까지의 여정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 스플리터의 모듈형 커넥터 &amp;rarr; 배선반(IDF, MDF) &amp;rarr; 보안기&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배선반MDF(Main Distribution Frame): 주 배선반&lt;/li&gt;
&lt;li&gt;IDF(Intermediate Distribution Frame): 중간 배선반&lt;/li&gt;
&lt;li&gt;보안기&lt;/li&gt;
&lt;li&gt;낙뢰와 같이 외부 전화선에서 과대한 전류가 흘러들지 않도록 보호하는 장치&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;597&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cu6Khw/btsJQhj23Ie/qrwB6KxtfGAhABUTZPcBO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cu6Khw/btsJQhj23Ie/qrwB6KxtfGAhABUTZPcBO0/img.png&quot; data-alt=&quot;배선반 용어가 낯설어서 찾아봤는데 사진을 보니 딱 감이 온다! 아파트에 살면 한 번쯤은 꼭 보는 구조물&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cu6Khw/btsJQhj23Ie/qrwB6KxtfGAhABUTZPcBO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcu6Khw%2FbtsJQhj23Ie%2FqrwB6KxtfGAhABUTZPcBO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;250&quot; height=&quot;336&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;597&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;배선반 용어가 낯설어서 찾아봤는데 사진을 보니 딱 감이 온다! 아파트에 살면 한 번쯤은 꼭 보는 구조물&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 신호는 보안기까지 지나서 전주(電柱 - 전봇대)의 전화 케이블로 이동한다. 전체의&amp;hellip;.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전화 케이블은 사용자와 가까우면 전봇대 내부에 부설되어있다. 도중에 전주의 둘레에 묶은 금속 파이프 속에 들어가 여기에서 지하로 들어간다. - 이를 &lt;b&gt;궤선점&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 케이블을 한꺼번에 전주를 통해 전화국까지 연결하는 것은 비현실적이기에, 전화국에 어느 정도 가까운 곳에서 지하에 케이블을 매설한다. 전화국에 가까워지면 지하 케이블 수가 증가하므로, 이것을 모아서 매설하는 부분은 지하도처럼 된다. - 이를 동도라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 동도를 통해 전화국에 들어간 케이블은 MDF에 하나씩 연결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 전화 케이블 속을 신호가 통과할 때 여러 가지 잡음의 영향을 받는다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전화 케이블은 금속제 신호선 속에 전기 신호가 흐른다는 점이 이더넷 트위스트 케이블과 공통된다. 또한, 전화 케이블은 ADSL과 같은 높은 주파수의 신호가 흐른다는 점을 가정하지 않았기에 잡음의 영향을 받기 더 쉽다. &amp;rarr; 이더넷과 같이 케이블 외부의 주파수 / 케이블 내부에서 발생하는 잡음의 영향으로 신호가 변형된다.&lt;/li&gt;
&lt;li&gt;잡음이 많은 장소에 전화 케이블이 가설되면 속도가 저하될 가능성이 있다. ex) 전철의 선로 옆&lt;/li&gt;
&lt;li&gt;케이블의 쿼드 속/인접한 서브 유닛의 가까운 위치에 ADSL 신호선과 ISDN 회선의 신호선이 함께 들어있는 경우, ISDN에서 누설되는 잡음의 영향이 있다. (최근엔 많이 해결된 상황)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-9) DSLAM을 통과하여 BAS에 도달한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 배선반, 스플리터를 통과하여 DSLAM에 도착하면 전기 신호가 셀로 복원된다. &amp;rarr; ADSL과 같음&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;552&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mqUWP/btsJQ4RO1VB/3JqU5KxlZmKuEzV2cQxBF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mqUWP/btsJQ4RO1VB/3JqU5KxlZmKuEzV2cQxBF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mqUWP/btsJQ4RO1VB/3JqU5KxlZmKuEzV2cQxBF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmqUWP%2FbtsJQ4RO1VB%2F3JqU5KxlZmKuEzV2cQxBF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;147&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;552&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;셀로 복원하는 작업을 여러 개의 ADSL로 하면 장소 차지 + 동작 감시가 어렵기에, ADSL 모뎀의 기능을 하나로 묶어, DSLAM으로 만들어서 사용한다. 즉, DSLAM은 ADSL을 여러 개 모아둔 장치다!&lt;/li&gt;
&lt;li&gt;한 가지 다른 점은, ASDL 모뎀은 이더넷 인터페이스, DSLAM은 ATM 인터페이스를 가진 것이 대부분이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ DSLAM을 나온 셀은 BAS라는 중계 장치에 도착한다. - 패킷 복원 및 터널링 중계&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;BAS는 ATM 인터페이스로 구성되어, 수신한 셀을 원래의 패킷으로 복원한다.&lt;/li&gt;
&lt;li&gt;수신한 패킷의 맨 앞부분의 MAC 헤더와 PPPoE 헤더를 버린다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MAC과 PPPoE는 BAS에 도착하기 위해 사용된 것이기 때문에, 이더넷 인터페이스의 라우터가 패킷을 수신했을 때 MAC 헤더를 버리는 것과 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이후, 터널링용 헤더를 붙여 터널링 출구로 중계한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY 2 | 광섬유를 이용한 액세스 회선(FTTH)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-1) 광섬유의 기본&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빛 신호를 전달하는 가느다란 유리 또는 플라스틱&amp;nbsp;섬유의 일종이다.&amp;nbsp;안쪽에 있는 코어 속에 광신호를 흘려서 데이터를 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;광신호는 의외로 단순하다. 등이 켜지면 1, 꺼져서 어두우면 0을 나타내는 것 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 광섬유를 이용한 통신의 원리&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;디지털 데이터를 전기 신호로 변환한 후, 전기 신호를 광신호로 변환한다.&amp;rarr; 전압이 높으면 밝아지고, 낮으면 어두워진다.&lt;/li&gt;
&lt;li&gt;전기 신호를 LED나 포토 다이오드같은 &lt;b&gt;광원&lt;/b&gt;에 입력하면, 입력된 전기 신호의 전압에 따라 빛을 발한다.&lt;/li&gt;
&lt;li&gt;이 빛을 &lt;b&gt;광섬유&lt;/b&gt; 안에 통과시켜서 흘려 보낸다.&lt;/li&gt;
&lt;li&gt;수신측에서는 &lt;b&gt;수광 소자&lt;/b&gt;(빛에 감응하여 밝기에 따라 전압을 일으킴)를 통해 빛으로부터 전기 신호를 낸다.&lt;/li&gt;
&lt;li&gt;이 전기 신호를 디지털 데이터로 변환해서 수신한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-2) 싱글모드와 멀티모드의 차이&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;광섬유로 빛을 통과시켜서 전달하는 건 꽤나 직관적이지만, 그 빛의 전달 방법이 상당히 복잡하다..... 상당히...상당히...!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 입사각이 작은 빛만 경계면에서 전반사되어, 코어를 통해 흐른다.&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;광원(LED 등)에서 나온 빛은 광섬유의 코어 부분에 들어간다. 광원에서 나온 빛은 흩어지므로 광섬유의 코어 부분에 여러 각도로 들어간다. 입사각이 크면 경계선에서 굴절되어 밖으로 나가버린다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입사각이 작은 빛이 전부 코어 속을 진행하는 것은 아니다. &amp;rarr; 위상차가 없는 경우에만 소멸되지 않고 광섬유 속을 흐른다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빛은 코어와 클래드의 경계면에 부딪혀서 반사될 때 위상이 달라진다. 따라서 빛이 소멸되지 않고 유지되는 환경 유지를 위해, 코어와 클래드의 위상이 달라지지 않는 각을 찾아서 코어의 직경을 설정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 빛 소멸 방지 &amp;rarr; 코어와 클래드의 위상이 달라지지 않는 각도 찾기 &amp;rarr; 코어의 직경 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코어의 직경은 2 가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;싱글모드&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 작은 빛만 입사되도록 코어를 가늘게 만든 광섬유다.&lt;/li&gt;
&lt;li&gt;신호의 변형이 적다. &amp;rarr; 케이블 길이가 길어져도 상관없다. &amp;rarr; 멀리 떨어진 장소에 있는 건물 사이에 사용한다. (FTTP)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;멀티모드&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코어의 직경이 굵고 입사각이 큰 빛도 들어가므로 위상이 가장 작은 것, 두 번째로 작은 것, 세 번째로 작은 것과같이 복수의 빛이 코어속을 진행하는 광섬유다.&lt;/li&gt;
&lt;li&gt;빛의 양이 많다 &amp;rarr; 신호의 변형이 잦다. &amp;rarr; 광섬유의 길이를 짧게 해야 오류를 줄일 수 있다. &amp;rarr; 한 건물 내에서 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-3) 광섬유를 분기시켜서 비용을 절감한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ADSL 대신 광섬유를 사용해서 사용자 측의 인터넷 접속용 라우터와 인터넷 측의 BAS를 연결하는 것이 FTTH다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY 3 | 복수 서버에 리퀘스트를 분배한 서버의 부하 분산&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-1) 처리 능력이 부족하면 복수 서버로 부하 분산된다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 서버에 액세스가 증가할 때&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;회선을 빠르게 하기 &amp;rarr; 서버의 처리 능력이 따라잡지 못할 수 있다.&lt;/li&gt;
&lt;li&gt;서버 머신을 고성능으로 교체하기 &amp;rarr; 한계가 있다.&lt;/li&gt;
&lt;li&gt;&amp;rArr; 복수 서버를 사용하여 서버 한 대당 처리량을 줄인다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 여러 대의 웹 서버를 설치하고 한 대가 담당하는 사용자 수를 줄인다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS 서버에서 분배&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DNS 서버에 같은 이름으로 여러 대의 웹 서버를 등록한다. DNS 서버는 조회가 있을 때마다 차례대로 IP 주소를 되돌려 준다. &amp;rArr; &lt;b&gt;라운드 로빈&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;192.0.2.60 &amp;rarr; 192.0.2.70 &amp;rarr; 192.0.2.80&lt;/li&gt;
&lt;li&gt;192.0.2.70 &amp;rarr; 192.0.2.80 &amp;rarr; 192.0.2.60&lt;/li&gt;
&lt;li&gt;192.0.2.80 &amp;rarr; 192.0.2.60 &amp;rarr; 192.0.2.70&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;DNS는 웹 서버의 상태를 확인하지 않고 중계만 하므로, 웹 서버가 중단/고장된 상황에 대응하기 어렵다.&lt;/li&gt;
&lt;li&gt;라운드 로빈을 통해 웹 서버를 분배하면 웹 사이트의 연속성이 끊긴다. 예를 들어서, 첫 번째 페이지에서 성명 입력 후에 두 번째 페이지에서 신용카드 번호를 입력하는 케이스가 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;TO-DO : GCP LB 라운드 로빈&amp;hellip;.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-2) 부하 분산 장치를 이용해 복수의 웹 서버로 분할된다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부하 분산 장치 IP를 웹 서버 IP 대신 DNS에 등록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 대화가 복수의 페이지에 걸쳐있다면&amp;hellip;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP stateless: HTTP의 기본 동작은 먼저 Request Message를 보내기 전에 TCP 연결을 하고, 전송이 끝나면 연결을 끊는다.&lt;br /&gt;Cookie: HTTP 전후 관계를 파악하기 위한 값을 HTTP 헤더 필드에 부가하는 방법이 고안되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY 4 | 프로바이더의 내부&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GbIcq/btsKf9691lq/GOssQDTlmXCdX4d6kSFHkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GbIcq/btsKf9691lq/GOssQDTlmXCdX4d6kSFHkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GbIcq/btsKf9691lq/GOssQDTlmXCdX4d6kSFHkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGbIcq%2FbtsKf9691lq%2FGOssQDTlmXCdX4d6kSFHkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;570&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-1) POP와 NOC&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 액세스 회선, 터널링을 통과한 패킷은 프로바이더의 POP에 설치된 라우터에 도착한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POP(Point Of Center): ADSL/FTTH같은 액세스 회선은 사용자가 계약한 프로바이더의 설비에 연결되어있는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NOC(Network Operation Center): POP에서 들어온 패킷이 여기로 모여든다. 이곳에서 목적지 근처의 POP나 다른 프로바이더로 패킷이 흘러간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-2) 건물 밖은 통신 회선 등으로 접속한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 건물 내부에서 모든 패킷이 모이는 종점 라우터, POP &amp;amp; NOC&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POP나 NOC는 건물 안에 하나씩은 있는 서버룸/머신룸과 비슷하다. 건물 안의 어딘가에 있으므로 그 안에 있는 라우터는 케이블로 직접 접속하거나, 스위치를 경유하여 접속한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;건물 밖과는&amp;hellip;.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;케이블로 연결&lt;br /&gt;&lt;br /&gt;보통 이 케이블은 트위스트 페어 케이블을 사용하는데 아무래도 패킷 송수신양이 엄청 많다보니, 트페케이블의 한계에 도달할 때가 있다. 이런 경우는 광섬유 케이블을 사용한다.&lt;/li&gt;
&lt;li&gt;광섬유 케이블&lt;br /&gt;&lt;br /&gt;광섬유 케이블을 건물 각각에 가지고 있다는 것은 현실적인 측면(기술, 비용)에서 어렵다. 따라서 광섬유를 가진 회사는 자체적으로 사용 + 다른 곳에 광섬유 대출해주기를 동시에 한다. 이 광섬유를 세분화해서 일부분을 고객에게 빌려주는 형태다. &amp;rarr; 이런 종류의 서비스를 통신 회선이라고 한다!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY 5 | 프로바이더를 경유하여 흐르는 패킷&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1) 프로바이더끼리의 접속&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 서버 -클라이언트가 같은 프로바이더에 접속한 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 라우터끼리 정보 교환 후 테이블 자동 업데이트해서 패킷을 중계한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 서버 -클라이언트가 다른 프로바이더에 접속한 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로바이더는 다른 프로바이더와 정보 교환도 하므로, 라우팅 테이블에 송신할 프로바이더의 경로 정보가 등록되어 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 결국 인터넷에는 동일 프로바이더인지, 다른지 상관없이 모든 경로가 등록되어 있다. 이러한 경로표로 패킷 중계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-2) 프로바이더끼리 경로 정보 교환하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ BGP(Border Gateway Protocol)&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1356&quot; data-origin-height=&quot;564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FU2fb/btsKhKLnL0Y/686pbWTJbD7fl9jd48JZG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FU2fb/btsKhKLnL0Y/686pbWTJbD7fl9jd48JZG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FU2fb/btsKhKLnL0Y/686pbWTJbD7fl9jd48JZG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFU2fb%2FbtsKhKLnL0Y%2F686pbWTJbD7fl9jd48JZG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;208&quot; data-origin-width=&quot;1356&quot; data-origin-height=&quot;564&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;주변의 여러 라우터들에게 어떤 라우터가 어느 AS에 속해 있는지에 대한 정보를 소문 내는 것&amp;nbsp;이다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트랜지트(transit): 한 프로바이더로부터 경로 정보를 받아, 모든 프로바이더에 패킷을 보낼 수 있게 되는 상태. 즉, 한 프로바이더로부터 모든 프로바이더의 주소 정보를 알아낸다.&lt;/li&gt;
&lt;li&gt;비트랜지트/피어(peer) : 피어들 간의 정보만 교환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-3) 사내 네트워크에서 자동 등록하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최단 경로를 선택하되, 의도하지 않은 상대로부터 오는 패킷을 중지하는 구조로 되어있다. 단순히 최단경로로만 구성하면 의도치 않게 비용 부담에 응하지 않는 프로바이더로부터 패킷이 흘러올 수 있고, 다른 회선을 이용해버릴 수도 있기 때문이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;경로 정보를 교환하는 상대 지정하기&lt;/li&gt;
&lt;li&gt;사내에서는 무차별적으로 모든 라우터와 경로 정보를 교환하지만, 프로바이더 간에는 특정 라우터와 1:1로 한다. 이 일대일으로 대응된 경로 정보를 통지한 상대 이외의 패킷은 흘러들어오지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-4) IX(Internet eXchange)의 필요성&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;984&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mdR45/btsKggSx9as/IFjLiDtKxTTtf64qqVpAg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mdR45/btsKggSx9as/IFjLiDtKxTTtf64qqVpAg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mdR45/btsKggSx9as/IFjLiDtKxTTtf64qqVpAg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmdR45%2FbtsKggSx9as%2FIFjLiDtKxTTtf64qqVpAg0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;394&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;984&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로바이더끼리 직접 1:1로 연결하지 않아도 IX를 통해 통신할 수 있다.&lt;/li&gt;
&lt;li&gt;자연재해에 대비하여, 자가 발전 설비를 갖춘 내진 구조의 건물 안에 있다.&lt;br /&gt;&lt;br /&gt;정확히 말하면 건물 어딘가에 IX가 있고, IX의 중심에는 고속 LAN의 인터페이스를 다수 장착한 &lt;b&gt;레이어 2 스위치&lt;/b&gt;가 있다. 이때 IX의 스위치는 스위칭 허브와 같이 동작한다. &amp;rarr; ARP를 통한 중계 대상 라우터의 MAC 조사 &amp;rarr; MAC 헤더에 기록하여 패킷 전송&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>CS/Network</category>
      <category>CS</category>
      <category>network</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/296</guid>
      <comments>https://cobinding.tistory.com/296#entry296comment</comments>
      <pubDate>Mon, 30 Sep 2024 14:00:38 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 네트워크 원리 (3) 케이블의 앞은 LAN 기기였다.</title>
      <link>https://cobinding.tistory.com/295</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;성공과 실패를 결정하는 1% 네트워크 원리를 공부하고 정리한 글이다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Chapter 3 - 허브와 스위치, 라우터의 탐험&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;1248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yVZwI/btsKf54JO0k/y3up8hLDKV47Q6JpqkUnJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yVZwI/btsKf54JO0k/y3up8hLDKV47Q6JpqkUnJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yVZwI/btsKf54JO0k/y3up8hLDKV47Q6JpqkUnJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyVZwI%2FbtsKf54JO0k%2Fy3up8hLDKV47Q6JpqkUnJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;358&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;1248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;STORY 1 | 케이블과 리피터, 허브 속에 신호가 흘러간다.&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;1-1) 하나하나의 패킷이 독립적으로 동작한다.&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;컴퓨터에서 송신된 패킷은 중계 장치(허브, 라우터 등)에 의해 운반된다. 이때, 중계장치는 어떤 데이터인지 전혀 신경쓰지 않고 전송에만 집중한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;1-2) LAN 케이블은 신호를 약화시키지 않는 것이 핵심이다.&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 높은 주파수와 낮은 주파수&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;882&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KKM18/btsJqaYN8z0/QiEWWJrdPY2LTYnh1JsVR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KKM18/btsJqaYN8z0/QiEWWJrdPY2LTYnh1JsVR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KKM18/btsJqaYN8z0/QiEWWJrdPY2LTYnh1JsVR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKKM18%2FbtsJqaYN8z0%2FQiEWWJrdPY2LTYnh1JsVR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;882&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;882&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;1-3) &amp;lsquo;꼼&amp;rsquo;은 잡음을 방지하기 위한 방법이다.&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;트위스티 페어 케이블은 마주 꼬여있는 상태를 통해서 잡음을 막는다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;잡음의 원인은 케이블에서 발생하는 전자파다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;전자파가 금속 등의 도전체에 닿으면 그 내부에 전류가 발생한다. &amp;rarr; 케이블 주위에 전자파가 있으면&lt;span&gt;&amp;nbsp;&lt;/span&gt;보내는 신호와 &lt;span style=&quot;color: #006dd7;&quot;&gt;다른 전류가 케이블 내에 흐르게 되고,&amp;nbsp;이러한 잡음이&amp;nbsp;신호와 뒤섞여서 파형이 변형된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 케이블에 영향을 주는 전자파&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;기기에서 누설되는 전자파
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;신호선에 전자파가 닿으면, 전자파 진행 방향의 오른쪽으로 전류가 생긴다. &lt;span style=&quot;color: #006dd7;&quot;&gt;케이블의 선을 마주 꼬면 형태가 나선형이 되어 꼰 옆의 선에서 전류가 흐르는 방향이 반대가 된다.&lt;/span&gt; 전자파로부터 오른쪽으로 전류가 생기면, 이 나선형 모양으로부터 전류가 다시 왼쪽으로 흘러 서로 상쇄되어 잡음에 의한 전류가 약해진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;케이블 밖&lt;/b&gt;에서 오는 것으로,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&amp;lsquo;선을 꼼&amp;rsquo;으로써&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;막을 수 있다.&lt;/li&gt;
&lt;li&gt;같은 케이블 안의 인접한 신호선에서 누설되는 전자파 -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;크로스토크(crosstalk)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;신호선 안에는 신호라는 전류가 흐른다. 이 전류에 의해 주위에 전자파가 생기고, 다른 신호선에 대한 잡음이 된다. 이 잡음은 거리가 가까운 것이 문제다. 전자파는 발생 근원에서 떨어지면 확산되어 약해진다. 하지만 한 개의 케이블 안에서의 신호선들은 거리가 매우 가깝다.&lt;/li&gt;
&lt;li&gt;신호선을 마주 꼴 때 어떤 부분에서는 + 신호가, 어떤 부분에서는 - 신호가 가까워지면 이 +, -에서는 잡음의 영향이 반대가 되어 각각의 균형이 잡히면서 잡음의 영향이 줄어ㅈ든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이것을 막는 대책도&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;신호선을 마주 꼬는 것&lt;/b&gt;이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;1-4) 리피터 허브는 연결되어 있는 전체 케이블에 신호를 송신한다.&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 리피터 허브의 송신 원리&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;신호가 리피터 허브에 도착하면 &lt;span style=&quot;color: #ee2323;&quot;&gt;일단 LAN 전체에 신호를 모두 뿌린다.&lt;/span&gt; &amp;rarr; 이더넷의 기본 원리와 상통한다. 일단 모두 뿌리고, 수신처 MAC 주소에 해당하는 기기만 이 신호를 받는 것.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 리피터 허브 내부&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;1159&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3zynL/btsJp8zTN0L/XyY8IrKKYWiIXKjEUoXk6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3zynL/btsJp8zTN0L/XyY8IrKKYWiIXKjEUoXk6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3zynL/btsJp8zTN0L/XyY8IrKKYWiIXKjEUoXk6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3zynL%2FbtsJp8zTN0L%2FXyY8IrKKYWiIXKjEUoXk6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;569&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;1159&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PHY &amp;amp; RJ-45(회로와 커넥터)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 커넥터의 안 쪽에 LAN의 PHY와 똑같은 역할을 하는 회로가 있다.&lt;/li&gt;
&lt;li&gt;이 PHY와 RJ-45를 거쳐서 제대로 송수신 하려면 단자를 교차시켜서, &amp;lsquo;송신 단자&amp;rsquo;에서 보낸 신호를 &amp;lsquo;수신 단자&amp;rsquo;에서 받을 수 있도록 해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MDI/MDI-X&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리피터 허브 끝의 커넥터에는 MDI/MDI-X와 같은 전환 스위치가 붙어있다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;MDI: RJ-45 커넥터와 송수신 회로를 직접 결선한 것&lt;/li&gt;
&lt;li&gt;MID-X: 교차하여 결선한 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;허브의 커넥터 부분은 보통 MDI-X이므로 허브끼리 접속할 때는 한쪽을 MDI로 설정해야 한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;만약 MDI가 없고 모두 MDI-X인 경우, 크로스케이블로 허브들을 접속한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;리피터 허브의 기본은 &lt;span style=&quot;color: #ee2323;&quot;&gt;신호를 그대로 뿌리는 것&lt;/span&gt;이다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리피터 허브의 회로에 신호가 들어오면 이 모든 신호를 연결된 커넥터 부분에 뿌려준다.&lt;/li&gt;
&lt;li&gt;이후, 커넥터에 온 신호는 모두 나가면서 리피터 허브에 접속한 전체 기기에 도달한다.&lt;/li&gt;
&lt;li&gt;신호를 수신한 기기는 맨 앞에 있는 MAC 헤더에 쓰여 있는 수신처 MAC 주소를 조사하여 자신이 수신처에 해당하면 이 신호를 받는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;STORY 2 | &lt;/span&gt;스위칭 허브의 패킷 중계 동작&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2-1) 스위칭 허브는 주소 테이블로 중계한다.&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위칭 허브도 PHY에서 커넥터까지 수신되는 부분은 앞의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;리피터 허브와 같다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;즉, 커넥터와 PHY 회로는 MDI-X로 연결되어 있고, 트위스트 페어 케이블에서 신호가 들어오면 PHY 회로의 수신 부분에 신호가 들어간다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이후, 신호는 공통의 신호 형식으로 변환되어 MAC 회로로 들어간다. 그리고 여기에서 디지털 데이터로 변환한 후, 패킷의 가장 끝에 있는 FCS와 비교하여 오류 유무를 검출한다. 문제가 없으면 이를 버퍼 메모리에 저장한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 스위칭 허브가 수신처 MAC 주소를 조사하는 방법&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;스위칭 허브의 포트&lt;/b&gt;(커넥터와 안쪽에 있는 회로 부분)는 수신처 MAC 주소를 검사하지 않고, 모든 패킷을 수신하여 버퍼 메모리에 저장하고 나서, MAC 주소표를 확인한다.&lt;/span&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;이 MAC 주소표에는 MAC 주소와 포트가 짝을 이루고 있다.&lt;/span&gt; 이를 통해 수신한 패킷을 어느 포트에서 송신하면 좋을지를 판단한다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브는 중계 장치니까 송신 패킷을 다시 어디로 보낼지 수신처 MAC 주소와 송신 포트를 저장해 두는 것.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 스위치 회로의 구조&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;신호선이 격자 모양으로 배치되고, 교점에 스위치가 있다. 이 스위치는 전자적으로 개폐 조절이 가능하다. 이를 통해 신호가 흐르는 대상을 제어한다. 그리고 입력측은 수신측 포트에, 출력층은 송신쪽 포트에 접속되어 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;포트 사이에 패킷을 운반할 때, 이 회로에 패킷의 신호를 흘린다. 신호의 교점에 있는 스위치는 각각 독립하여 움직이므로 신호가 중복되지 않으면 복수의 신호를 동시에 흘릴 수도 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 스위치 회로에서의 송신 동작 &amp;rarr; LAN 어댑터의 송신 동작과 같다.&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이더넷의 규칙에 따라 &lt;span style=&quot;color: #006dd7;&quot;&gt;먼저 신호가 흐르고 있는지 확인&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;소켓을 디지털 데이터에서 신호로 변환하여 송신&lt;/li&gt;
&lt;li&gt;송신 동작 중에 다른 기기가 보낸 신호가 수신측에 들어오면 패킷이 충돌하므로, 재밍 신호를 보낸 후 송신 동작을 중지하고 잠시 기다렸다가 다시 송신&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2-2) MAC 주소 테이블을 등록 및 갱신한다.&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 스위칭 허브는 패킷을 중계할 때 MAC 주소표의 내용을 갱신한다.&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;패킷 수신 시, 송신처 MAC 주소와 입력 포트 번호&lt;/li&gt;
&lt;li&gt;사용하지 않고 일정 시간이 지나면 삭제(노트북 들고 다른 방으로 이동하는 경우)&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rArr; MAC 주소는 스위칭 허브가 자체가 스스로 등록하거나 사라지므로, 수동으로 관리할 필요가 없다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2-3) 스위칭 허브의 예외적인 동작&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 스위칭 허브에 리피터 허브가 접속되어 있는 경우&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 경우는 송신할 패킷의 포트와 수신한 패킷의 포트가 같기 때문에, 패킷을 중계하지 않고 폐기한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ MAC 주소표에 수신처 MAC 주소와 일치하는 주소가 없는 경우&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;패킷이 한 번도 스위칭 허브에 도착하지 않음&lt;/li&gt;
&lt;li&gt;시간이 지나서 삭제된 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 어느 포트에서 송신해야 할지 판단할 수 없으므로, &lt;span style=&quot;color: #006dd7;&quot;&gt;수신 포트 이외의 전체 포트에서 송신한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 해도 결국 올바른 수신처만 패킷을 받음 + 두 번째부터는 기록이 되므로, 네트워크 혼잡도에 큰 영향 X&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2-4) 전이중 모드에서 송수신을 동시에 실행&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;전이중 모드: 송신과 수신을 동시에 할 수 있는 모드 &amp;rarr; 신호의 충돌을 검출하는 회로를 무효화&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 스위칭 허브는 전이중 모드에서 송수신을 동시에 실행 가능(리피터 허브는 안됨)&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이론상으로, 여러 대의 컴퓨터가 동시에 송신 동작을 개시하면 허브 내부에서 신호가 뒤섞여서 신호가 파괴된다. 하지만 리피터 허브는 전이중 모드가 필요 없는 게, 어차피 &amp;lsquo;트위스트 페어 케이블&amp;rsquo;로 구성되어 있어서 신호가 따로 흐른다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2-5) 최적의 전송 속도로 보내는 자동 조정&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자동 조정(auto negotiation):&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;접속한 상대가 전이중 모드를 지원하는지 검출하고 동작 모드를 자동으로 전환하는 기능이다. 동작 모드뿐만 아니라 상대의 전송 속도를 검출하여 이도 자동으로 전환한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;링크 펄스:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;데이터가 흐르지 않을 때 펄스형의 신호를 흘려서 상대가 올바르게 작동하는지, 케이블 이상 없는 지 등을 확인할 수 있다. (이더넷에 초록색 LED 확인 가능)&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 두 기기가 전원 켜고, HW 초기화가 끝난 다음 펄스 신호를 보내기 시작한다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;먼저 펄스 신호를 보내서 상대에게 신호가 도착한다. 이 도착한 펄스의 패턴을 읽고 상대가 어느 모드를 지원하는지 조사한다. 모드의 우선순위대로 조사하면서 자신과 상대 모두가 지원하는 동작 모드를 찾는다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2-6) 스위칭 허브는 복수의 중계 동작을 동시에 실행한다.&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스위칭 허브는 수신처 MAC 주소와 매핑된 포트 외에는 송신 동작을 실행하지 않으므로, 이 포트에서 데이터를 흘릴 수 있다.&lt;/li&gt;
&lt;li&gt;리피터 허브는 들어온 신호를 모든 포트에서 뿌리므로 동시에 두 개 이상의 신호가 들어왔을 때 패킷이 충돌하기 때문에 복수의 신호를 동시에 흘릴 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;STORY 3 | 라우터의 패킷 중계 동작&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3-1) 라우터의 기본&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;허브를 경유한 패킷은 결국 라우터에 도착하고, 라우터에서 다음 라우터로 중계된다. 중계할 때 table을 확인하고 해당 기기에 데이터를 보낸다는 점은 허브와 비슷하지만, 라우터에는 IP 개념이 추가된다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 라우터의 내부 구조는 중계 부분, 포트 부분 두 가지로 나뉜다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;중계 부분:&lt;/span&gt; 패킷의 중계 대상을 판단한다. &amp;rarr;&lt;span style=&quot;color: #000000; background-color: #f6e199;&quot;&gt; IP 담당 부분과 같다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;포트 부분:&lt;/span&gt; 패킷을 송수신한다. &amp;rarr; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;LAN 어댑터와 같다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;라우터의 포트 부분에 다양한 통신 기술의 HW를 장착하면, 이를 지원할 수 있다. 예를 들어서 무선 LAN용 HW를 장착하면 무선 LAN을 지원할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; [기본 동작] 포트 부분에서 패킷을 수신하고, 중계 부분에서 중계할 대상을 판단한다. 그리고 중계 대상측의 포트로 패킷을 옮기고, 포트 부분의 hw 규칙에 따라 패킷 송신 동작을 실행한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 스위칭 허브는 table의 MAC 주소, 라우터는 table의 IP 주소를 통해 중계한다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스위칭 허브는 table에 MAC 주소와 그에 매핑되는 포트를 저장했지만, 라우팅 테이블은 조금 더 복잡한 내용을 담고 있다.&lt;/li&gt;
&lt;li&gt;허브는 패킷을 중계함과 동시에 정보를 갱신하지만, 라우터는 중계하면서 정보를 갱신하거나 하는 등의 작업은 하지 않는다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;사람이 수동으로 정보 갱신&lt;/li&gt;
&lt;li&gt;라우팅 프로토콜(RIP, OSPF, BGP 등)을 통해 라우터들끼리 정보를 교환하고, 라우터 자체에서 라우팅 테이블에 등록&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1052&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgPYx6/btsJoEtpQ8Q/CoKc5anxQAMqVRMhQk8Rqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgPYx6/btsJoEtpQ8Q/CoKc5anxQAMqVRMhQk8Rqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgPYx6/btsJoEtpQ8Q/CoKc5anxQAMqVRMhQk8Rqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgPYx6%2FbtsJoEtpQ8Q%2FCoKc5anxQAMqVRMhQk8Rqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;421&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1052&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 라우팅 테이블 살펴보기&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;lsquo;주소 집약&amp;rsquo;&lt;/span&gt;을 통해 서브넷을 묶어서 한 개의 서브넷으로 간주하기&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;경로가 겹치면 그 부분만 일괄적으로 통합&lt;/span&gt;해서 해당 주소에만 패킷을 중계하느 것&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;한 개의 서브넷을 세분화하여 복수 개&lt;/span&gt;로 보이도록 하기&lt;/li&gt;
&lt;li&gt;호스트 번호 부분에 값이 들어있는 개별 컴퓨터를 나타내는 주소를 &amp;lsquo;수신처&amp;rsquo; 항목에 등록할 수 있다.&lt;/li&gt;
&lt;li&gt;게이트웨이, 인터페이스 - 패킷의 중계 대상&lt;/li&gt;
&lt;li&gt;매트릭 - 목적지가 가까운지, 먼지 나타냄&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3-4) 경로표를 검색하여 출력포트를 발견한다.&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;라우터는 패킷 수신 동작이 끝나면 맨 앞의 MAC 헤더를 폐기한다. 패킷을 수신하면 MAC 헤더의 역할이 끝나기 때문.. 폐기하고, &lt;span style=&quot;color: #006dd7;&quot;&gt;MAC 헤더 뒤에 있는 IP 헤더의 내용을 보고 패킷 중계 동작에 들어간다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;우선 수신한 패킷의 IP 주소와 라우팅 테이블의 수신처 부분을 비교한다. 이때, 32 비트 전부를 비교하는 것이 아니라&lt;span style=&quot;background-color: #ffffff; color: #006dd7;&quot;&gt; &amp;lsquo;넷마스크&amp;rsquo; 항목에 등록된 값에서 네트워크 번호 부분만 비교한다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;255.255.255.0&lt;/span&gt;&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이라는 넷마스크는 네트워크 부분이 24비트, 호스트 부분이 8비트니까 왼쪽부터 24비트 먼저 조사한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;만약 이 넷마스크를 조사해서 복수의 후보가 생기면&lt;/b&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;네트워크 번호의 비트 수가 가장 긴 것을 찾는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;네트워크 번호의 비트 수까지 동일하면, 메트릭 값으로 판단한다. 메트릭 값이 작을 수록 가까운 것이기에 작은 쪽으로 중계한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;만약 일치하는 행이 하나도 발견되지 않으면,&lt;/b&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;패킷을 폐기하고 ICMP 메시지를 송신처에게 보낸다.&lt;br /&gt;&lt;/span&gt;&amp;rarr; 스위칭 허브는 그냥 모든 포트에 흘렸지만..스위칭 허브는 많아봐야 수천대 정도의 네트워크지만, 라우터는 인터넷이기에 규모가 크다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3-5) 해당하는 경로가 없는 경우에 선택하는 기본 경로&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;넷마스크 항목이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;i&gt;0.0.0.0&lt;/i&gt;&lt;/span&gt;이라는 것은 비교 동작을 실행하지 않는다는 것이다. &amp;rarr; 패킷의 수신처 IP 주소와 라우팅 테이블 수신처 항목을 비교할 때 비트수가 0이라는 것이기 때문이다.&lt;span style=&quot;color: #333333; background-color: #f6e199;&quot;&gt; 즉, 모든 주소에 일치한다.&lt;/span&gt; 따라서 이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;0.0.0.0&lt;/span&gt;&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이라는 넷마스크는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;기본 경로/기본 게이트웨이&lt;/span&gt;를 나타낸다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;3-6) 패킷은 유효 기간이 있다.&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 라우터는 중계 대상을 찾고, 포트 부분으로 패킷을 옮기고 송신하기 전에 해야할 몇 가지 일이 있다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TTL(Time To Live, 생존기간)설정
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;라우터를 경유할 때마다 이 값을 1씩 줄이다가 0이되면 패킷을 폐기한다.&lt;/li&gt;
&lt;li&gt;패킷이 같은 장소를 무한대로 돌아다니는 경우를 방지한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3-7) 큰 패킷은 분할한다.&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 중계하는 패킷의 크기가 출력측의 &lt;span style=&quot;color: #006dd7;&quot;&gt;패킷 최대 길이&lt;/span&gt;를 초과하면 패킷을 그대로 송신할 수 없다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 경우에는 &lt;span style=&quot;color: #006dd7;&quot;&gt;IP 프로토콜에 규정된 Fragmentation을 통해 패킷을 분할&lt;/span&gt;하고, flag를 붙여서 중계한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;패킷 최대 길이는 포트의 종류에 따라 결정된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;출력측의 MTU를 조사하여 중계하려는 패킷을 송신할 수 있는지 체크한다.&lt;/li&gt;
&lt;li&gt;IP 헤더의 플래그 필드를 조사하여 분할해도 좋을지 확인한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분할이 불가능하면 패킷을 폐기하고, ICMP 메시지로 송신처에 통지한다.&lt;/li&gt;
&lt;li&gt;분할이 가능하면 출력측의 MTU에 맞춰서 앞에서부터 차례대로 분할한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분할할 때는 TCP 헤더 뒤 데이터부터 분할한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;분할한 데이터에 IP 헤더를 덧붙인다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3-8) 라우터의 송신 동작은 컴퓨터와 같다.&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 송신 동작은 출력 포트가 무엇이냐에 따라 다르다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이더넷이라면 이더넷 규칙에 따라, ADSL라면 ADSL 규칙에 따라 신호를 변환하여 송신한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 회사 등의 LAN에 사용하는 라우터라고 가정하자&amp;hellip;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;패킷 송신 동작의 기본은 프로토콜 스택의 IP 담당이 패킷을 보낼 때와 같다. &lt;span style=&quot;color: #006dd7;&quot;&gt;맨 앞부분에 MAC 헤더를 부여하고, 값을 설정하여 패킷을 완성시킨 후 전기 신호로 변환해서 보낸다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[수신처 MAC 주소] MAC 헤더에 정보를 쓰기 위해서 라우팅 테이블의 &amp;lsquo;게이트웨이&amp;rsquo; 항목에서 패킷을 건네줄 상대를 판단한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게이트웨이 부분이 공란이면 IP 헤더의 수신처가 건네줄 상대의 주소&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;[수신처 MAC 주소] 라우팅 테이블에서 상대의 IP를 결정했으면, ARP로 해당 IP 주소에서 MAC 주소를 조사하고, 결과를 수신처 MAC 주소로 설정한다.&lt;/li&gt;
&lt;li&gt;[송신처 MAC 주소] 출력측의 초트에 할당된 MAC 주소를 설정하고 16진수(0800)를 추가한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3-9) 라우터와 스위칭 허브의 관계&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 라우터(IP)는 패킷을 실제로 보내기 위해 허브(이더넷)에 의뢰해야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;라우터는 IP 기반, 허브는 이더넷을 기반으로 만들어졌다. 따라서 이 IP와 이더넷의 관계가 라우터와 허브의 관계다. &amp;rarr; 즉, 라우터는 패킷을 운반하는 것을 허브에 의뢰해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 라우터에서는 이더넷 기능을 내장한 기종이 있기에 순수 라우터와 순수 허브에 대해 이야기 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 이더넷 이외에 LAN/통신 회선에는 어떻게 의뢰할까?&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;똑같이 그들에게 의뢰한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt; STORY 4 | 라우터의 부가 기능&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4-1) 주소 변환으로 IP 주소를 효율적으로 이용한다.&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 라우터의 주소 변환 기능&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;인터넷이 발달하고 주소 부족 현상에 대비하기 위해 IP를 두 가지로 구분하여 사용&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;private&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;글로벌 주소에 포함된 범위 내에서 IP 주소를 만들어서 사용한다. 이건&lt;span style=&quot;color: #006dd7;&quot;&gt; 사내에서만 사용하는 것이므로 바깥에 있는 IP와 중복돼도 상관없다. &lt;/span&gt;단, 사내에서 중복은 피해야 한다.&lt;/li&gt;
&lt;li&gt;이러한 사내 IP는 외부 인터넷과 직접 패킷을 주고받지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;global&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인터넷과 직접 통신하는 부분이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4-2) 주소 변환의 기본 동작&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;패킷을 인터넷에 중계할 때 &lt;/b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;송신처의 IP 주소를 private &amp;rarr; global으로 변경한다. 포트도 마찬가지로 변경한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;그리고 이 &lt;b&gt;네 가지를 한 묶음&lt;/b&gt;으로하여 주소 변환 장치 내부 table에 기록한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;바뀐 IP, port로 외부 인터넷과 통신한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;응답을 받으면 주소 변환 장치가 글로벌 주소와 포트를 찾아서 private으로 다시 바꿔서 사내 네트워크에 패킷을 보낸다.&lt;/li&gt;
&lt;li&gt;접속 동작이 끝나고 연결이 끊기면 대응표에 적힌 ip, port를 삭제한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4-3) 포트 번호를 바꿔 쓰는 이유&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;IP를 수만 개의 포트에 대응&lt;/span&gt;하기 위해서. 즉, IP를 더 많이 쓰기 위해서다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4-4) 인터넷에서 회사로 액세스 한다.&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사내에서 외부 인터넷으로 패킷을 중계할 때&lt;/b&gt;는 송신처의 private, port가 등록되어 있지 않아도 중계할 수 있다. 어차피 글로벌 주소는 주소 변환 장치(라우터)에 할당되어 있고 포트는 적당히 비어 있는 것을 사용하면 되기 때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러나 인터넷에서 사내로 패킷을 중계할 때는 주소 전환 장치가 주소를 전환해주어야 하므로, table에 기록이 되어 있어야 한다. &amp;rarr; 외부로부터의 부정 침입을 방지하는 효과가 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4-5) 라우터의 패킷 필터링 기능&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;패킷을 중계할 때 MAC 헤더, IP 헤더, TCP 헤더에 기록되어 있는 내용을 조사하여 사전에 설정한 조건에 맞으면 패킷을 중계/폐기하는 것을 말한다.&lt;/p&gt;</description>
      <category>CS/Network</category>
      <category>CS</category>
      <category>network</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/295</guid>
      <comments>https://cobinding.tistory.com/295#entry295comment</comments>
      <pubDate>Tue, 3 Sep 2024 18:04:30 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 네트워크 원리(2) - TCP/IP의 데이터를 전기 신호로 만들어 보낸다</title>
      <link>https://cobinding.tistory.com/292</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;성공과 실패를 결정하는 1% 네트워크 원리를 공부하고 정리한 글이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Chapter 2 - 프로토콜 스택과 LAN 어댑터 탐험&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;1248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BBuR8/btsKgfF38uM/EYBuc8GB0wW4LNUSklHNv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BBuR8/btsKgfF38uM/EYBuc8GB0wW4LNUSklHNv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BBuR8/btsKgfF38uM/EYBuc8GB0wW4LNUSklHNv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBBuR8%2FbtsKgfF38uM%2FEYBuc8GB0wW4LNUSklHNv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;250&quot; height=&quot;298&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;1248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;STORY 1 | 소켓을 작성한다.&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;1-1) 프로토콜 스택의 내부 구조&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;995&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/colq3M/btsJgbxtvNM/qQK7Gfms4yJkEEHys99cFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/colq3M/btsJgbxtvNM/qQK7Gfms4yJkEEHys99cFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/colq3M/btsJgbxtvNM/qQK7Gfms4yJkEEHys99cFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcolq3M%2FbtsJgbxtvNM%2FqQK7Gfms4yJkEEHys99cFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;398&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;995&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;인터넷에서 데이터를 운반할 때는 데이터를 작게 나누어 패킷이라는 형태로 운반한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;패킷을 통신 상대까지 운반하는 것이 IP의 주 역할이다.&lt;/li&gt;
&lt;li&gt;IP 안에는 ICMP와 ARP라는 프로토콜을 다루는 부분이 포함되어 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ICMP: 패킷 운반 통제 및 제어용 메시지 통제&lt;/li&gt;
&lt;li&gt;ARP: 이더넷의 MAC 주소&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;1-2) 소켓의 실체는 통신 제어용 제어 정보&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;소켓은 개념적인 것이어서, 실체가 없다.&lt;/span&gt; 굳이 말하자면 제어 정보, 즉&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;제어 정보가 기록된 프로토콜 스택 내부의 메모리 영역&lt;/b&gt;이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제어 정보에는 통신 상대의 IP 주소, 포트 번호, 통신 동작의 상태 등이 있다.&lt;/li&gt;
&lt;li&gt;소켓에는 응답이 돌아오는지의 여부와 송신 동작 후의 경과 시간 등이 기록되어 있다. &amp;rarr; 이 정보를 보고 포기하거나 다시 보내는 동작을 실행해야 하기 때문&lt;/li&gt;
&lt;li&gt;netstat 명령어&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;1-3) 소켓을 호출했을 때의 동작&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;브라우저가 소켓이나 connect같은 소켓 라이브러리의 부품을 호출했을 때, 프로토콜 스택의 내부는 어떻게 움직일까?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저에서 gethostnyname같은 걸로 요청하면&amp;hellip;&lt;/li&gt;
&lt;li&gt;TCP/IP layer에서 소켓 생성하기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소켓의 제어 정보를 기록할 메모리 영역 확보하기&lt;/li&gt;
&lt;li&gt;소켓을 작성한 직후에는 송수신 동작이 시작되지 않은 상태이므로, 초기 상태임을 나타내는 정보 기록&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;소켓을 만들고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;디스크립터&lt;/b&gt;를 애플리케이션에 알려주기&lt;/li&gt;
&lt;li&gt;애플리케이션은 앞으로 통신할 때 이 디스크립터를 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;STORY 2 | 서버에 접속한다.&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2-1) 서버에 접속 == 소켓에 정보를 기록&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;소켓을 만든 직후는 아무것도 기록되어 있지 않다. 이 상태로는 송신 의뢰가 와도 제대로된 통신을 할 수가 없다. &amp;rarr; 소켓을 호출하여 만드는 것만으로는 프로토콜 스택에는 아무것도 전달되지 않기 때문에 IP 주소나 port 번호같은 내용을 프로토콜 스택에 알리는 동작(접속 동작)이 필요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;접속을 위해.. 즉, 통신을 위한 정보를 만들기 위해
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;통신 상대와 제어 정보를 주고받아 소켓에 필요한 정보를 기록한다. (ex. 클라이언트 측의 IP 주소나 포트 번호를 서버 측에 알리는 것)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;접속 동작에서 주고받는 제어 정보는 규칙이 정해져 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2-2) 맨 앞부분에 제어 정보를 기록한 헤더를 배치한다.&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnj88t/btsJfBJ7dN7/due1G70rnTSm86ZX3KsMPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnj88t/btsJfBJ7dN7/due1G70rnTSm86ZX3KsMPK/img.png&quot; data-alt=&quot;TCP 헤더 내용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnj88t/btsJfBJ7dN7/due1G70rnTSm86ZX3KsMPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcnj88t%2FbtsJfBJ7dN7%2Fdue1G70rnTSm86ZX3KsMPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;617&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1263&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TCP 헤더 내용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;통신 동작 전체에서 어떤 정보가 필요한지 검토하는 TCP 프로토콜의 사양으로 규정되어있다.&lt;/li&gt;
&lt;li&gt;이 항목들은 고정화되어 있어, 송수신/연결끊기/대화할 때마다 각 단계에서 이 제어 정보를 사용한다.&lt;/li&gt;
&lt;li&gt;즉, 데이터를 나눈 패킷마다, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;패킷의 가장 앞부분인 헤더에&lt;/span&gt; 이 내용이 부가되어 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제어 정보만 있는 패킷은 뒷부분 데이터가 없고 제어 정보가 적힌 헤더만 존재&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2-3) 접속 동작의 실체&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;애플리케이션이 socket 라이브러리의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;connect를 호출&lt;/b&gt;하는 것부터 시작된다.&lt;/p&gt;
&lt;pre id=&quot;code_1729696090893&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;connect(&amp;lt;디스크립터&amp;gt;, &amp;lt;서버 IP&amp;gt;, &amp;lt;서버 PORT&amp;gt;)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;접속 동작의 순서&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클라이언트의 TCP 담당 부분에 ip와 port 전달 &amp;rarr; 서버의 TCP 담당 부분에 전달
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;포트번호: 클라이언트 소켓의 수신처가 되는 서버측의 소켓을 지정할 수 있는 관문&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;포트를 통해 접속 소켓을 확실히 하고,&lt;/span&gt; 컨트롤 비트인 SYN = 1 세팅&lt;/li&gt;
&lt;li&gt;위와 같이 &lt;span style=&quot;color: #ee2323;&quot;&gt;TCP 헤더를 만들고 IP 담당 부분에 건넴&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;IP 담당 부분이 패킷 송신 동작 실행&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네트워크를 통해 패킷을 서버에 보냄&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;서버측의 IP 담당 부분이 TCP 담당 부분에 건네어,&lt;span style=&quot;color: #ee2323;&quot;&gt; 헤더 조사&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;수신처 소켓 즉, 포트 번호를 찾아서 필요한 정보를 기록하고 상태 변경(접속 동작 진행중)&lt;/li&gt;
&lt;li&gt;응답 &amp;hellip; 응답을 보낼 때도 마찬가지로, TCP 헤더 만들고, ACK = 1 세팅&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;응답을 받은 클라이언트&lt;/span&gt;는 또 다시 TCP 분석하고, SYN = 1 확인 후 소켓에&lt;span style=&quot;color: #ee2323;&quot;&gt; 접속 완료를 나타내는 제어 정보를 기록&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;서버에게 응답을 잘 받았다는 ACK = 1 반송&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 이 접속 동작이 끝나면 비로소 데이터를 송수신할 수 있는 상태가 된다.&lt;span style=&quot;background-color: #f6e199;&quot;&gt; 파이프와 같은 것으로 소켓이 연결되었다고 생각하면 된다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY 3 | 데이터를 송수신한다.&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버와 연결이 완료되면 애플리케이션 단에서 write 요청을 보낸다. 이때 프로토콜 스택은 송신 데이터의 내용을 알지 못한다. 데이터 길이만큼만 바이너리 데이터가 1바이트씩 차례로 나열되어 있다고 알고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3-1) 송수신 시, 내부 송신용 버퍼 메모리 활용&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로토콜 스택은 애플리케이션으로부터 받은 데이터를 일단 내부 송신용 버퍼 메모리에 저장한다.&amp;rArr; OS의 종류나 버전에 따라 버퍼의 수용 가능한 크기, 데이터를 보내는 시점이 다르다.&lt;/li&gt;
&lt;li&gt;&amp;rArr;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;송신 요청이 올 때마다 작은 소켓을 네트워크 상에 흘려보내면 네트워크 이용 효율이 저하&lt;/b&gt;되므로, 어느정도 데이터를 저장하고 나서 송수신 동작을 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3-2) 일반적으로 데이터를 보내는 시점은?&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;  2 가지 기준&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. MTU로 판단한다.&lt;/b&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MTU(Maximum Transmission Unit): 패킷 하나로 운반가능한 데이터 최대 길이&lt;/li&gt;
&lt;li&gt;MSS(Maximum Segment Size): MTU에서 TCP/IP의 헤더를 뺀 부분 &amp;rArr; 즉, 페이로드 부분&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvFTlG/btsJgBihmb6/tK1HBlkZv8mVkkEWI1XNKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvFTlG/btsJgBihmb6/tK1HBlkZv8mVkkEWI1XNKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvFTlG/btsJgBihmb6/tK1HBlkZv8mVkkEWI1XNKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvFTlG%2FbtsJgBihmb6%2FtK1HBlkZv8mVkkEWI1XNKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;350&quot; height=&quot;213&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 타이밍&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;애플리케이션의 송신 속도가 느려지는 경우 &amp;rarr; 프로토콜 스택 내부의 타이머 활용&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;*두 기준의 trade-off를 잘 생각해야 한다. MTU에 너무 집중하면 네트워크 이용효율이 높지만 서비스/통신 지연 가능성이 생기고, 2번을 기준으로 타이머를 너무 짧게 잡으면 느려짐은 없어도 네트워크 이용 효율이 좋지 않아진다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;rarr; 이에 대한 명확한 기준은 없고 OS/프로토콜 스택 개발자에 달려있는 상황이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;*타이밍 제어는 옵션으로 지정 가능하다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3-3) 데이터가 크면 분할해서 보내기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;849&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yAGrj/btsJfn6mUyN/Q2vEjNJ5gsl6m17K9kykFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yAGrj/btsJfn6mUyN/Q2vEjNJ5gsl6m17K9kykFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yAGrj/btsJfn6mUyN/Q2vEjNJ5gsl6m17K9kykFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyAGrj%2FbtsJfn6mUyN%2FQ2vEjNJ5gsl6m17K9kykFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;340&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;849&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;블로그나 게시판 등에서 긴 문장을 투고하는 등 한 개의 패킷에 다 담기지 않는 경우 &amp;rarr; 송신 버퍼에 저장된 데이터는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;MSS 길이를 초과하므로, 맨 앞부터 차례대로 MSS 크기에 맞게 분할하고 분할한 조각을 한 개씩 패킷에 넣어 송신한다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3-4) ACK를 통해 패킷이 도착했는지 확인&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;송신 측이 한 데이터를 N 바이트씩 나누어서 그 시작점을 시퀀스 넘버로 설저하고, 시퀀스 번호와 데이터 크기를 수신측에 같이 전송 &amp;rarr; 수신측은 송신측으로받은 시퀀스 넘버 단위로 데이터를 받았다는 OK 사인을 전송
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;rarr;&lt;/span&gt; seq: 1, 1460 byte&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;larr;&lt;/span&gt; ACK 1461&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;rarr;&lt;/span&gt; seq: 1461, 1460 byte&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;larr;&lt;/span&gt; ACK 1462&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그런데, TCP의 송수신 동작은 양방향이므로, 두 가지의 데이터 흐름이 있다. (&lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;rarr;&lt;/span&gt;,&lt;span style=&quot;color: #006dd7;&quot;&gt; &amp;larr;&lt;/span&gt;) 이에 대응하기 위해서 클라이언트 측에서도 ACK를 전송, 서버측에서도 시퀀스 넘거를 전송해준다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;635&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cv5wcF/btsJhCgcqeh/6R3K3vA1jWAfTzJkzBDxi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cv5wcF/btsJhCgcqeh/6R3K3vA1jWAfTzJkzBDxi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cv5wcF/btsJhCgcqeh/6R3K3vA1jWAfTzJkzBDxi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcv5wcF%2FbtsJhCgcqeh%2F6R3K3vA1jWAfTzJkzBDxi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1548&quot; height=&quot;635&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;635&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;먼저 시퀀스 넘버를 양쪽에서 산출하고, 접속 동작을 할 때 서로 통지한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&amp;rarr; 이때도 서로 ACK 반송&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;시퀀스 번호와 ACK 번호가 준비되면 데이터 송수신 동작에 들어간다.&lt;/li&gt;
&lt;li&gt;최초 클라이언트가 요청하면 서버에서 ACK를 반송하는 방식으로 데이터를 주고받는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&amp;rarr;&amp;nbsp;&lt;b&gt;TCP는 상대의 데이터 수신을 확인할 때까지 송신용 패킷을 버퍼 메모리에 저장해둔다.&lt;/b&gt;&amp;nbsp;&lt;/span&gt;만약 이에 대응하는 상대의 ACK가 오지 않으면 다시 이 데이터를 보내야 하기 때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; TCP의 이러한 통신 방식으로 인해, 송수신을 TCP에만 맡겨두면 LAN 어댑터, 라우터 등 어디에서도 오류 검출을 할 필요가 없다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;네트워크의 어디에서 오류가 발생하더라도 TCP의 통신 특성만으로 회복 처리를 취할 수 있기 때문이다. &lt;/b&gt;&lt;/span&gt;단, 도중에 케이블이 분리되거나 서버가 다운되는 등의 문제가 생기면 TCP가 아무리 데이터를 다시 보낸다고 해도 해결할 수 없다. 이럴 때에는 TCP가 몇 번 다시 보낸 후 회복의 전망이 없는 것으로 보고 데이터 송신 동작을 강제로 종료하고 애플리케이션에 오류를 통지한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3-5) ACK의 대기시간은?&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ACK의 반송이 지연되는 상황은 &lt;b&gt;보통 혼잡인 경우가 많으므로&lt;/b&gt;, 너무 자주 다시 데이터를 보내면 혼잡을 더 악화시킬 수 있다. 하지만 대기 시간을 너무 길게 잡으면 패킷을 다시 보내는 동작이 지연돼서 속도 저하의 원인이 될 수 있다. (&amp;rarr; 아까 내부 송신용 버퍼에 저장하고 데이터를 보내는 시점을 설정하는 것과 비슷한.. trade-off)&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동적으로 TCP 대기시간 변경이 가능하다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;ACK 번호가 돌아오는 시간을 기준으로 대기 시간을 판단한다. 데이터 송신 동작을 실행하고 있을 때 항상 ACK가 돌아오는 시간을 계측해 두고, 조정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3-6) 윈도우 제어 방식으로 패킷 송수신 효율 높이기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MoeAt/btsJgdWccYu/LD2om4MFFFRYZxzfAtcrrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MoeAt/btsJgdWccYu/LD2om4MFFFRYZxzfAtcrrk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;1236&quot; data-origin-width=&quot;1692&quot; data-widthpercent=&quot;49.99&quot; style=&quot;width: 49.4056%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MoeAt/btsJgdWccYu/LD2om4MFFFRYZxzfAtcrrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMoeAt%2FbtsJgdWccYu%2FLD2om4MFFFRYZxzfAtcrrk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1692&quot; height=&quot;1236&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qytol/btsJhgLdv6R/44gI56VnYw1QoVjNKfyQA1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qytol/btsJhgLdv6R/44gI56VnYw1QoVjNKfyQA1/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1697&quot; data-origin-height=&quot;1239&quot; data-filename=&quot;IMG_33B8067A2877-1.jpeg&quot; data-widthpercent=&quot;50.01&quot; style=&quot;width: 49.4316%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qytol/btsJhgLdv6R/44gI56VnYw1QoVjNKfyQA1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQytol%2FbtsJhgLdv6R%2F44gI56VnYw1QoVjNKfyQA1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1697&quot; height=&quot;1239&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수신 측의 능력을 초과할 만큼 데이터를 한 번에 쏟아내버리면?수신 가능한 데이터의 최대양을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;윈도우 크기&lt;/b&gt;라고 한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;감당 가능한 데이터의 양을 정해서 송신(클라이언트)측에 알려준다. 클라이언트는 데이터를 보낼 때 보낸 데이터의 크기와 용량을 비교하면서, 가능한 남은 영역이 0이 되면 송신을 중지한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;윈도우 통지 타이밍
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;송신측은 한 번 통지를 받으면 송신한 데이터와 비교하여 스스로 남은 값을 계산할 수 있다. 그러므로, 수신 측에서 애플리케이션에 데이터를 건네주고 수신 버퍼의 빈 영역이 늘어났을 때 송신측에 통지해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ACK와 윈도우 통지 패킷을 줄이고 효율적인 통신을 하는 방법
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;소켓을 바로 보내지 않고 잠시 기다린다. 이때 다음 통지 동작이 일어나면 양쪽을 상승 시켜서 함께 하나의 패킷으로 묶어 보낸다.&lt;/li&gt;
&lt;li&gt;ACK 번호 통지가 여러 번 연속해서 일어나면 최후의 것만 통지한다. (도중의 것 생략 가능)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;STORY 4 | 서버에서 연결을 끊어 소켓을 말소한다.&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;548&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNQVB3/btsJoqNBv5f/krrN8jXkRR7RA4MKAC5cTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNQVB3/btsJoqNBv5f/krrN8jXkRR7RA4MKAC5cTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNQVB3/btsJoqNBv5f/krrN8jXkRR7RA4MKAC5cTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNQVB3%2FbtsJoqNBv5f%2FkrrN8jXkRR7RA4MKAC5cTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;356&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;548&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서로 FIN, ACK를 주고 받는다. (데이터 전송이 끝난 측에서 먼저 FIN 전송)&lt;/li&gt;
&lt;li&gt;소켓이 말소되면 모든 정보가 없어진다. &amp;rarr; 이때 애플리케이션이 다른 소켓을 작성하면 말소된 포트 번호를 사용할 수 있기 때문에 마지막 FIN에 대한 ACK는 어느정도 시간을 두고 말소한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;STORY 5 | IP 패킷 송수신 동작&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;큰흐름&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;TCP 담당 부분에서 패킷을 바이너리로 전달받은 IP는 IP헤더, MAC 헤더 만들어서 이더넷에 보내고, 이더넷은 이 IP 패킷(디지털 데이터)를 전기 신호/빛으로 변환해서 케이블로 데이터를 흘려보낸다. 모든 소켓관련 내용은 위에서 다 해주기 때문에 이더넷은 모든 기기에 공통적으로 행동한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;5-1) 앞에서 프로토콜스택.. TCP로부터 의뢰받은 IP는 어떻게 동작할까?&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;송신처&lt;/b&gt;에서 소켓을 가장 가까운 중계처(라우터 등)로 전송하면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;소켓 헤더에 저장된 정보 + 중계처의 표&lt;/b&gt;(수신처/가까운 또다른 중계처의 방향에 대한 정보)를 통해 데이터를 전송한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;허브: 이더넷의 규칙에 따라 패킷 운반&lt;/li&gt;
&lt;li&gt;라우터: IP의 규칙에 따라 패킷 운반&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 실제로 송신처 컴퓨터부터 중간 중계장치들(라우터, 허브 등)을 거치는 과정&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;송신처에서&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;패킷이 가려는 서버의 IP 주소를 IP 헤더의 수신처에 기록&lt;/span&gt;한다.&lt;/li&gt;
&lt;li&gt;패킷의 목적지가 분명해지므로, IP는 이 수신처가 어느 방향에 있는지 + 라우터를 조사한다.&lt;/li&gt;
&lt;li&gt;이어서 다음 방향의 라우터 조사한다.&lt;/li&gt;
&lt;li&gt;다음 방향의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;라우터로 패킷을 보내도록 이더넷에 의뢰&lt;/span&gt;한다.&lt;/li&gt;
&lt;li&gt;다음&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;라우터에 할당된 이더넷의 주소(MAC 주소)&lt;/span&gt;를 이더넷이 알려주고, 이를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;MAC 헤더에 기록&lt;/span&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;라우터에는 IP용 표가 있으므로, 이것과 IP 헤더의 수신처를 결합해서 다음 중계지(라우터)를 결정한다.&lt;/li&gt;
&lt;li&gt;해당 라우터의 MAC 주소를 헤더에 붙여서 송신한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;5-2) 패킷 송수신 동작의 개요&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;■&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;패킷 송수신은 TCP 담당부분이 IP 담당 부분에 패킷을 의뢰하는 것부터 시작된다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP 담당 부분은 [TCP 헤더 + 데이터 조각]을 IP 부분에 건네준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;■&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;TCP 담당으로부터 송신 의뢰를 받은 IP 담당 부분은 추가적인 헤더를 붙여, 네트워크용 HW에 건네준다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IP 헤더: IP 프로토콜에 규정된 규칙에 따라 패킷을 목적지에 보낼 때 사용하는 제어 정보&lt;/li&gt;
&lt;li&gt;MAC 헤더: 이더넷 등의 LAN을 사용하여 가장 가까운 라우터까지 패킷을 운반할 때 사용하는 제어 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;■&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;네트워크 HW, 이더넷 등에서는 0과 1로 구성된 디지털 데이터를 받아서 전기 신호 상태로 바꾸어 케이블에 송출한다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;신호는 허브나 라우터 등의 중계 장치에 도착하고, 이 장치가 상대가 있는 곳까지 패킷을 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;  IP 패킷 송수신 동작은 패킷의 역할에 관계없이 모두 같다. IP 담당 부분은 TCP 담당으로부터 받은 데이터를 그냥 하나의 바이너리 데이터로 간주하고 내용물에 대해서는 전혀 관심이 없다. 당연히 TCP의 동작 단계도 신경쓰지 않는다. 어떤 형식이든 의뢰받은 내용물을 패킷의 모습으로 만들어 상대에게 송신 및 전달하기만 할 뿐이다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;5-3) IP 헤더&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bicywW/btsJniQsoni/aOn4v8XRwgyjYJ7rGjIb60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bicywW/btsJniQsoni/aOn4v8XRwgyjYJ7rGjIb60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bicywW/btsJniQsoni/aOn4v8XRwgyjYJ7rGjIb60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbicywW%2FbtsJniQsoni%2FaOn4v8XRwgyjYJ7rGjIb60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;551&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1378&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 중요한 것은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;수신처(목적지) IP 주소(&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;애플리케이션에서 통지된 통신 상대의 IP 주소)&lt;/span&gt;&lt;/b&gt;다. IP는 애플리케이션이 지정한 것이 잘못되었어도 그대로 송신한다.&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;애플리케이션 측 책임으로 간주&lt;/span&gt;하기 때문이다&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;송신처 IP 주소는 사실 컴퓨터에 할당되는 것이 아니라 LAN 어댑터에 할당&lt;/span&gt;되므로, 복수의 LAN 어댑터가 있을 경우 한 대의 컴퓨터에 할당된 IP가 여러 개다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;따라서 어느 IP 주소를 송신처 주소로 설정할 지 판단해야 한다. &amp;rarr; 이 판단은 패킷을 건네주는 상대의 라우터를 결정하는 것과 같다.&lt;/li&gt;
&lt;li&gt;상대 라우터가 결정되면 LAN 어댑터에서 패킷을 송신해야 하는지 결정되고, LAN이 결정되면 IP 주소가 결정되기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;■&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/b&gt;패킷을 건네주는 상대의 라우터를 판단하는 방법?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;라우터가 IP용 표를 사용하여 다음 라우터를 결정하는 것과 같다.&lt;/li&gt;
&lt;li&gt;route print 명령어
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;interface: 네트워크용 인터페이스&lt;/li&gt;
&lt;li&gt;Gateway: 다음 라우터의 IP 주소&lt;/li&gt;
&lt;li&gt;넷마스크: 기본 gateway&lt;/li&gt;
&lt;li&gt;프로토콜 번호: 어디에서 의뢰받은 것인지 설정(TCP: 06, UDP: 17)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;5-4) 이더넷용 MAC 헤더 - IP 헤더 만들고, MAC 헤더 만들어 붙이기&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;■&lt;/b&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;IP 헤더를 통해 목적지 주소를 알 수 있지만, 이더넷에는 TCP/IP 개념이 통용되지 않는다. 따라서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;이더넷 패킷을 운반&lt;/span&gt;하기 위해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;수신처를 판단할 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;MAC 헤더를 사용&lt;/span&gt;한다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;531&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duvDP4/btsJnHbdFGS/HVjiI5LQ79IKOjHqMvHmx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duvDP4/btsJnHbdFGS/HVjiI5LQ79IKOjHqMvHmx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duvDP4/btsJnHbdFGS/HVjiI5LQ79IKOjHqMvHmx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduvDP4%2FbtsJnHbdFGS%2FHVjiI5LQ79IKOjHqMvHmx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;260&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;송신처 MAC 주소:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;하나의 48비트, LAN 어댑터의 MAC 주소 설정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MAC 주소는 LAN을 제조할 때 그 안의 ROM에 기록한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;수신처 MAC 주소&lt;/b&gt;: 패킷을 건네주는 상대의 MAC 주소
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;패킷을 건네줄 상대의 주소는 경로표 &amp;lsquo;Gateway&amp;rsquo;에 기록되어 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이더 타입:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;패킷의 내용물이 무엇인지 나타냄
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이더타입까지가 MAC 헤더이고, 그 뒤에 이어지는 것은 패킷의 내용물&lt;/li&gt;
&lt;li&gt;이더넷의 내용물은 IP, ARP같은 프로토콜의 소켓이며, 각각에 대응하는 값이 규칙적으로 정해져 있으므로 여기에 값을 기록한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;5-5) ARP로 라우터의 MAC 주소 조사한다.&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;상대가 자신과 같은 네트워크에 존재하면 단순히&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;브로드캐스트&lt;/span&gt;로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;ldquo;OO라는 IP 주소 있는 분?&amp;rdquo;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이렇게 찾을 수 있다. 그럼&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&quot;저요~&quot;&lt;/span&gt;하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;상대방의 MAC 주소를 MAC 헤더에 설정하면 끝이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;■&lt;/b&gt;&lt;/b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;ARP 캐시&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;패킷을 보낼 때마다&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&quot;있으신 분 - 저요&quot;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;동작을 반복하면 ARP 패킷이 불어나기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;ARP 캐시&lt;/span&gt;라는 메모리 영역에 보존하여 재사용한다.&lt;/li&gt;
&lt;li&gt;ARP 캐시를 먼저 확인하고, 없는 경우에만 브로드캐스트한다.&lt;/li&gt;
&lt;li&gt;캐시에 저장된 내용은 일정 시간이 지나면 삭제된다. (오류 방지를 위해)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;■&lt;/b&gt;&lt;/b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;이러한 MAC 주소/헤더를 붙이는 것까지 IP 담당 부분의 역할이다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LAN 어댑터에 건네주기 전에 IP 담당 부분에서 패킷을 완성하면 LAN은 전송만 하면 되기 때문에 IP 쪽이 담당한다!&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;STORY 6 | 이더넷 패킷 송수신 동작&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;5-6) 이더넷의 기본&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1155&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHhHMn/btsJngymBSH/wmnRxevsPWmyCO1z8nvPK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHhHMn/btsJngymBSH/wmnRxevsPWmyCO1z8nvPK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHhHMn/btsJngymBSH/wmnRxevsPWmyCO1z8nvPK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHhHMn%2FbtsJngymBSH%2FwmnRxevsPWmyCO1z8nvPK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;1155&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1155&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MAC 헤더의 수신처 MAC 주소를 가진 상대에게 패킷을 전달한다.&lt;/li&gt;
&lt;li&gt;송신처 MAC 주소로 송신처를 나타낸다.&lt;/li&gt;
&lt;li&gt;이더 타입으로 패킷의 내용물을 나타낸다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;lsquo;이더넷&amp;rsquo;이라는 하나의 사양에 기초하여 동작&lt;/b&gt;하기 때문에 클라이언트, 라우터 등 상관없이 모든 기기에 공통적으로 적용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;5-7) IP로부터 받은 패킷(디지털 데이터)을 전기/빛의 신호로 변환하여 송신한다.&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;■&lt;/b&gt;&lt;/b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;IP 패킷으로 받은 디지털 데이터를 전기 신호로 변환하는 것은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;LAN 어댑터&lt;/span&gt;다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하지만 LAN 어댑터는 단독으로 일할 수 없고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;이를 제어하는&amp;nbsp;&lt;b&gt;LAN 드라이버 소프트웨어&lt;/b&gt;가 필요하다.&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(모든 HW의 공통.. 이를 제어하는 SW가 필요함)&lt;/li&gt;
&lt;li&gt;전원 공급 &amp;rarr; OS 시동 &amp;rarr; 드라이버가 어댑터 초기화(MAC 주소 설정) &amp;rarr; OK&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;IP한테 패킷을 받으면 LAN 어댑터의 버퍼 메모리에 복사 후, 패킷을 송신하도록 MAC 회로에 명령을 보낸다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;5-8) 패킷에 3개의 제어용 데이터를 추가한다.&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;프리앰블(preeamble)&lt;/b&gt;: 송신하는 패킷을 읽을 타이밍을 잡기 위한 것.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&amp;rarr;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;101110&amp;hellip; 이라는 비트 패턴을 신호로 바꾸면 파형이 일정한 모습을 갖는다. 수신측은 신호를 수신할 때 이 파형에서 타이밍을 잡는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스타트 프레임 딜리미터&lt;/b&gt;: 패킷의 시작을 나타내는 표시&lt;/li&gt;
&lt;li&gt;FCS(Frame Check Sequence): 운반하는 도중 파형이 흐트러져 데이터가 변한 경우, 이를 검출하기 위해서 사용한다. 계산의 바탕이 된 데이터 값이 1비트라도 변화하면 달라진 값을 취하도록 고안이 되어 있다. 패킷을 운반하는 도중 잡음 등의 영향으로 데이터가 변하면, 수신측에서 계산한 FCS가 송신측과 달라진다. 이 차이를 통해 전달 과정에서 문제가 발생했음을 파악한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;5-9) 허브를 향해 패킷을 송신한다.&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Apo58/btsJnhDZZ5s/P6JL4ZcK7jMxWCIxczhqp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Apo58/btsJnhDZZ5s/P6JL4ZcK7jMxWCIxczhqp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Apo58/btsJnhDZZ5s/P6JL4ZcK7jMxWCIxczhqp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FApo58%2FbtsJnhDZZ5s%2FP6JL4ZcK7jMxWCIxczhqp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;179&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 세 가지를 부가하면, 케이블에 송출하는 패킷이 완성된다. 드디어 신호를 송신할 수 있는 상태가 된다!&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;■&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;[반이중 모드] 이더넷 구조를 따라 패킷이 변환되어 케이블까지 흘러가는 과정&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;케이블에 다른 기기가 송신한 신호가 흐르고 있는지 조사하고, 끝날 때까지 기다린다. (신호 충돌 방지)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MAC 회로&lt;/b&gt;가 프리앰블의 맨 앞부터 1비트씩 차례로 전기 신호로 변환한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이때 디지털 데이터를 신호로 변환하는 속도가 전송 속도다. &amp;rarr;만약 1초 동안 10MB 디지털 데이터를 신호로 변환하여 보낸다면 전송률은 10MiB/초&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이후 변환된 전기 신호를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;PHY(MAU)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;신호 부분에 보낸다. 이 전기 신호를 다시 케이브렝 송출하는 형식으로 변환하여 송신한다. 즉,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;PHY&lt;/b&gt;는 MAC 회로가 송신한 전기 신호의 형식을 케이블 형시긍로 변환하기 위한 변환회로다!
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PHY는 송신 동작만 하는 것이 아니라 수신 신호선에서 신호가 흘러들어오는지 감시한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;*이더넷은 송신한 신호가 상대에게 완전히 도착했는지 확인하지 않는다. 이더넷은 사양에서 기기와 기기 사이를 연결하는 케이블 길이를 100m 이내로 정한다. 그래서 오류가 좀처럼 발생하지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■&lt;/b&gt;&amp;nbsp;&lt;b&gt;PHY는 송신 동작만 하는 것이 아니라 수신 신호선에서 신호가 흘러들어오는지 감시한다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이때, 리피터 허브(b)를 사용한 반이중 모드의 경우에는 서로의 신호가 뒤섞여서 분간할 수 없는 상태(충돌)가 된다.&lt;/li&gt;
&lt;li&gt;그러면 송신 동작을 중단하고 충돌 사실을 다른 기기에 알리는 재밍 신호를 흘린다. 재밍 신호를 흘리고 잠시 기다렸다가 다시 송신 동작을 시도한다.&lt;/li&gt;
&lt;li&gt;스위칭 허브(c)를 사용한 전이중모드는 송수신을 동시에 실행하면서 충돌은 일어나지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;5-10) 돌아온 패킷을 받는다.&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 리피터 허브를 이용한 반이중 동작의 이더넷에서의 동작 과정&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1대가 송신한 신호가 리피터 허브에 접속된 케이블 전부에 흘러간다.&lt;/li&gt;
&lt;li&gt;프리앰블, 스타트 프레임 딜리미터가 나오면 그 다음 비트부터&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;디지털 데이터로 변환&lt;/span&gt;한다. (그러니까 송신의 거꾸로)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;먼저 PHY회로에서 신호를 공통 형식으로 변환하여 MAC 회로에 보낸다.&lt;/li&gt;
&lt;li&gt;MAC 회로에서 신호를 맨앞부터 차례대로 디지털 데이터로 변환하여 버퍼 메모리에 저장한다.&lt;/li&gt;
&lt;li&gt;신호의 마지막에서 FCS 검사 ➡️ 잡음 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;FCS에 문제가 없으면 MAC 헤더의 나의 MAC 주소와 비교하여 자신에게 오는 것인지 판단한다.&lt;/li&gt;
&lt;li&gt;자신에게 오는 게 맞으면&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;인터럽트를 통해 컴퓨터 본체에 통지&lt;/span&gt;한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;■&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;인터럽트의 동작 과정&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LAN 어댑터가 확장 버스 슬롯 부분에 있는 인터럽트용 신호선에 신호 전송 &amp;rarr; 본체는 이 신호를 받고 CPU가 실행하고 있던 작업을 일시적으로 보류 &amp;rarr; OS 내부 kernel 모드 실행 &amp;rarr; LAN 드라이버 호출 &amp;rarr; LAN 드라이버가 LAN 어댑터를 제어하여 송수신 동작 실행&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;5-11) 서버의 응답 패킷을 IP에서 TCP로 넘긴다.&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ LAN은 TCP/IP(0800)임을 확인하고 IP 부분으로 올려 보낸다. IP 담당 부분은 헤더 부분부터 조사하여 문제가 없는지 확인한다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 수신처 IP 주소가 잘못 적혀 있다면, ICMP라는 메시지를 사용하여 통신 상대에게 오류를 통지한다. (181p의 다양한 메시지 종류)&lt;/li&gt;
&lt;li&gt;IP 헤더의 플래그를 확인해서 이 패킷이 만약 분할된 패킷(IP Fragmentation)이라면 IP 담당 부분 내부의 메모리에 일시적으로 보관한다. 그리고 같은 frag를 가진 패킷이 도착하기를 기다리고, 모든 패킷이 도착하면 각각의 패킷마다 붙어있는 offset 순서대로 원래의 모습으로 되돌린다. 이 되돌리는 동작을 리어셈블링이라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 리어셈블링이 끝난 패킷을 TCP 담당 부분에 전송한다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;송수신처의 ip, port를 통해 해당 소켓을 찾아 적절한 통신을 한다. (소켓에는 통신 진행 상태가 기록되어 있다.)&lt;/li&gt;
&lt;li&gt;만약 연결 끊기(FIN같은) 패킷이라면 애플리케이션에게 통지하거나 응답 패킷을 반송한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>CS/Network</category>
      <category>CS</category>
      <category>network</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/292</guid>
      <comments>https://cobinding.tistory.com/292#entry292comment</comments>
      <pubDate>Tue, 27 Aug 2024 10:38:26 +0900</pubDate>
    </item>
    <item>
      <title>[CloudType] AWS에서 cloudtype으로 DB 마이그레이션하기</title>
      <link>https://cobinding.tistory.com/291</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cobinding.tistory.com/290&quot;&gt;앞편&lt;/a&gt;에서 cloudtype의 MariaDB를 생성해주었다. 이에 이어서 AWS MySQL로부터 cloudtype MariaDB로 마이그레이션을 함으로써 비용을 절약해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 cloudtype &lt;b&gt;프로젝트 전체 설정&lt;b&gt;(나의 경우 koala)에서 TCP 허용이 되어있는지 확인하자.&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2372&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FUYdL/btsI1uv0Fvp/EoKnjQu19ElER5MySuNWBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FUYdL/btsI1uv0Fvp/EoKnjQu19ElER5MySuNWBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FUYdL/btsI1uv0Fvp/EoKnjQu19ElER5MySuNWBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFUYdL%2FbtsI1uv0Fvp%2FEoKnjQu19ElER5MySuNWBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;185&quot; data-origin-width=&quot;2372&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) RDS 외부 접근을 위한 작업&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 마이그레이션을 하려면 AWS RDS에 직접 내 로컬에서 접근이 가능해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-1) 마스터 암호 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB &lt;b&gt;수정&lt;/b&gt;을 통해 마스터 암호를 설정하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1574&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjZIRp/btsI02zZqvx/Ykhqo7Fq5K3nfUNJEwWzA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjZIRp/btsI02zZqvx/Ykhqo7Fq5K3nfUNJEwWzA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjZIRp/btsI02zZqvx/Ykhqo7Fq5K3nfUNJEwWzA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjZIRp%2FbtsI02zZqvx%2FYkhqo7Fq5K3nfUNJEwWzA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;324&quot; data-origin-width=&quot;1574&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-2) 퍼블릭 액세스 허용&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;624&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xzpEm/btsI1xTOrZE/QDyfkf2ej2vGyJYUAHMAS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xzpEm/btsI1xTOrZE/QDyfkf2ej2vGyJYUAHMAS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xzpEm/btsI1xTOrZE/QDyfkf2ej2vGyJYUAHMAS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxzpEm%2FbtsI1xTOrZE%2FQDyfkf2ej2vGyJYUAHMAS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;253&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;624&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-3) 보안그룹이 설정되어 있다면 인바운드에 나의 IP 등록&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB에 보안그룹이 설정되어있다면, 나의 IP가 접근 가능하도록 정보를 등록해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2276&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nacvu/btsI0hrmWAi/M9dl221JQvCKvgeFwXz0i1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nacvu/btsI0hrmWAi/M9dl221JQvCKvgeFwXz0i1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nacvu/btsI0hrmWAi/M9dl221JQvCKvgeFwXz0i1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnacvu%2FbtsI0hrmWAi%2FM9dl221JQvCKvgeFwXz0i1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;135&quot; data-origin-width=&quot;2276&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 로컬 터미널에서 명령어를 통해 AWS RDS에 접근하기&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1723271404490&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mysql -h [endpoint] -u [user명] -P [port번호] -p&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 명령어를 입력해서 AWS RDS에 접근한다. 엔드포인트와 유저명은&lt;b&gt; AWS RDS 콘솔의 속성&lt;/b&gt;에서 확인할 수 있다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ren0d/btsI0LSMCa2/NYkjCKe735xLmK2WAKfeJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ren0d/btsI0LSMCa2/NYkjCKe735xLmK2WAKfeJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ren0d/btsI0LSMCa2/NYkjCKe735xLmK2WAKfeJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fren0d%2FbtsI0LSMCa2%2FNYkjCKe735xLmK2WAKfeJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1902&quot; height=&quot;488&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 설정한 마스터 암호를 입력하고 접속하면 위와 같이 접근 성공 메시지와 mysql&amp;gt; 이라는 터미널이 뜬다. 이렇게 되면 접근 성공이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;span style=&quot;color: #ee2323;&quot;&gt;SHOW databases;&lt;/span&gt; 명령어를 통해 &lt;b&gt;내가 접근할 database 이름을 확인해두자.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;569&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctzabA/btsI1F5mpns/AsQqAT0h0ccuPW3bOe3iK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctzabA/btsI1F5mpns/AsQqAT0h0ccuPW3bOe3iK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctzabA/btsI1F5mpns/AsQqAT0h0ccuPW3bOe3iK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctzabA%2FbtsI1F5mpns%2FAsQqAT0h0ccuPW3bOe3iK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;220&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;569&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-1) &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;mysqldump 명령어를 통해 데이터 백업하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;DB 연결 및 정보를 확인한 뒤, exit로 종료하고 나서 mysqldump 명령어를 통해 DB의 정보를 백업한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1723274514799&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mysqldump --set-gtid-purged=OFF -u [사용자명] -p[비밀번호] --host=[AWS RDS 엔드포인트] [데이터베이스명] &amp;gt; [원하는 파일명].sql&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;이 과정에서 다양한 에러가 발생할 수 있는데, 확인해볼 점은 다음과 같다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;하나의 철자라도 틀리면 접근할 수 없으니 여기서 꼼꼼히 할 수 있도록 주의하자..!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;보안그룹 인바운드 규칙(1번 참고)&lt;/li&gt;
&lt;li&gt;AWS DB 정보(RDS명, DB명, 마스터유저, 마스터비밀번호 등)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;로컬에서 mysql이 실행중&lt;/span&gt;이라면 확인 후, 이 프로세스를 죽이고 명령어를 실행할 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;또 주의할 점&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.3rdeyesys.com/docs/database/cloud-db-mysql/db-restore-error-1227-troubleshotting/&quot;&gt;이 문서&lt;/a&gt;&lt;span style=&quot;color: #000000;&quot;&gt;에서 잘 알 수 있는데, mysqldump 명령어를 사용할 때,&lt;/span&gt; &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;--set-gtid-purged=OFF&lt;/span&gt; 옵션을 빼먹지 않도록 주의하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;필자는 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;ERROR 1227 (42000)&lt;span style=&quot;color: #000000;&quot;&gt;를 마주하고나서야 알게되었다. 보통의 mysql db 복구는 GTID를 사용하지 않기 때문에 백업 단계에서 꼭 이 옵션을 추가해주어야 한다!&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GTID를 간단하게 말하자면 MySQL 복제에서 서버의 각 트랜잭션을 구분하는 고유한 식별자로, 백업 과정에 쓰이는 기능이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 명령어를 실행하면 내 로컬 컴퓨터 루트 경로에 sql 파일이 하나 생겨있음을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRiJci/btsIZYZUwZC/1Q1ARfymE3JC0k3BvDQXv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRiJci/btsIZYZUwZC/1Q1ARfymE3JC0k3BvDQXv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRiJci/btsIZYZUwZC/1Q1ARfymE3JC0k3BvDQXv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRiJci%2FbtsIZYZUwZC%2F1Q1ARfymE3JC0k3BvDQXv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;156&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) cloudtype MariaDB에 접속하기&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS에 접근했을 때와 거의 동일하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1723275105310&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mysql -h [endpoint] -u [user명] -P [port번호] -p&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b932vc/btsI04q1Wui/PRNYKvKokNBxp0L1T6AlS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b932vc/btsI04q1Wui/PRNYKvKokNBxp0L1T6AlS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b932vc/btsI04q1Wui/PRNYKvKokNBxp0L1T6AlS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb932vc%2FbtsI04q1Wui%2FPRNYKvKokNBxp0L1T6AlS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;191&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 데이터베이스를 &lt;span style=&quot;color: #ee2323;&quot;&gt;use [database명]; &lt;span style=&quot;color: #333333;&quot;&gt;으로 ON 해주고,&lt;/span&gt; source [로컬경로/백업파일명.sql] &lt;span style=&quot;color: #333333;&quot;&gt;명령어를 통해 백업 파일을 실행해주자.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;span style=&quot;color: #ee2323;&quot;&gt;ERROR&amp;nbsp;1273&amp;nbsp;(HY000):&amp;nbsp;Unknown&amp;nbsp;collation:&amp;nbsp;'utf8mb4_0900_ai_ci'&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;90&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYW8HR/btsI06I8iVl/GReMJc8rLPub4wSYpczfvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYW8HR/btsI06I8iVl/GReMJc8rLPub4wSYpczfvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYW8HR/btsI06I8iVl/GReMJc8rLPub4wSYpczfvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYW8HR%2FbtsI06I8iVl%2FGReMJc8rLPub4wSYpczfvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1822&quot; height=&quot;90&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;90&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MySQL Workbench&lt;/b&gt;에서 해당 부분을 모두 &lt;span style=&quot;color: #006dd7;&quot;&gt;'utf8m64_general_ci'&lt;/span&gt;로 변경해준다. 워크벤치의 Replace 기능을 활용하면 훨씬 수월하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4M1rz/btsI0rAyTfB/SUmla6lscQ3MYoRiSd1rm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4M1rz/btsI0rAyTfB/SUmla6lscQ3MYoRiSd1rm1/img.png&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;652&quot; data-is-animation=&quot;false&quot; width=&quot;400&quot; height=&quot;338&quot; data-widthpercent=&quot;57.02&quot; style=&quot;width: 56.360362%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4M1rz/btsI0rAyTfB/SUmla6lscQ3MYoRiSd1rm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4M1rz%2FbtsI0rAyTfB%2FSUmla6lscQ3MYoRiSd1rm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;772&quot; height=&quot;652&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d0WnqQ/btsI1gdIJKy/7bOdS7WtHbrAjPp9GkQa1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d0WnqQ/btsI1gdIJKy/7bOdS7WtHbrAjPp9GkQa1k/img.png&quot; data-origin-width=&quot;796&quot; data-origin-height=&quot;892&quot; data-is-animation=&quot;false&quot; width=&quot;400&quot; height=&quot;448&quot; data-widthpercent=&quot;42.98&quot; style=&quot;width: 42.476848%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d0WnqQ/btsI1gdIJKy/7bOdS7WtHbrAjPp9GkQa1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd0WnqQ%2FbtsI1gdIJKy%2F7bOdS7WtHbrAjPp9GkQa1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;796&quot; height=&quot;892&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리문이 뜨고 데이터들이 잘 담긴 모습을 확인할 수 있다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) DataGrip, IntelliJ에서 데이터와 API 확인해보기&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이후에 IntelliJ의 MySQL 의존성을 모두 MariaDB로 변경해준다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;환경변수도 바꿔주고,&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; API 몇 개 테스트 한 뒤 서버에 적용하면 끝..!  &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2802&quot; data-origin-height=&quot;1468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tlcJW/btsI1mZi5qQ/aNSjXhyBmrTMkaOLeiUWqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tlcJW/btsI1mZi5qQ/aNSjXhyBmrTMkaOLeiUWqK/img.png&quot; data-alt=&quot;배포까지 완료&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tlcJW/btsI1mZi5qQ/aNSjXhyBmrTMkaOLeiUWqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtlcJW%2FbtsI1mZi5qQ%2FaNSjXhyBmrTMkaOLeiUWqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;210&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2802&quot; data-origin-height=&quot;1468&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;배포까지 완료&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cloudtype이 아직 활발히 쓰이진 않아서 다양한 자료를 찾기 어려웠다. (GCP를 쓸 때 하도 당해서 익숙하다..~)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꽤나 까다로웠던 마이그레이션 덕분에 4배 정도는 비용을 아낄 수 있게 되어 다행이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가))&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1752&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pxZQ8/btsI0CviI12/wj0lujAjKT9OgavukVYFM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pxZQ8/btsI0CviI12/wj0lujAjKT9OgavukVYFM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pxZQ8/btsI0CviI12/wj0lujAjKT9OgavukVYFM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpxZQ8%2FbtsI0CviI12%2Fwj0lujAjKT9OgavukVYFM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;229&quot; data-origin-width=&quot;1752&quot; data-origin-height=&quot;728&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 예상 비용을 확인해봤더니, RDS 없애기 + VM 인스턴스 스펙 낮추기로 비용이 확연히 줄었다!&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/CloudType</category>
      <category>AWS</category>
      <category>cloudtype</category>
      <category>DB</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/291</guid>
      <comments>https://cobinding.tistory.com/291#entry291comment</comments>
      <pubDate>Sat, 10 Aug 2024 17:58:33 +0900</pubDate>
    </item>
    <item>
      <title>[CloudType] cloudtype으로 DB 호스팅 비용 줄이기</title>
      <link>https://cobinding.tistory.com/290</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;koala 프로젝트를 하다가 첫 난관(?)이 생겼다..! 바로 서버 비용이 없다는 것&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cobinding.tistory.com/287&quot;&gt;이 설계&lt;/a&gt;에 맞춰서 클라우드 환경을 구축하기 전에, &lt;i&gt;&quot;이렇게 가면 비용이 꽤 들 것같은데 우리 얼마 쓰고 있지?&quot;&lt;/i&gt; 확인하는 것에서부터 서버 비용에 대한 걱정이 시작되었다.. ㅋㅋ &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2318&quot; data-origin-height=&quot;1450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/besILI/btsIZ8NIYum/ltmorcqekFvgSoBz46kVy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/besILI/btsIZ8NIYum/ltmorcqekFvgSoBz46kVy0/img.png&quot; data-alt=&quot;RDS에서 가장 많은 비용이 발생하고 있었고, 프로젝트X를 수강하지 않는 이상 지원을 받기 힘들기에.. 어떻게든 방법을 찾아야 했다!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/besILI/btsIZ8NIYum/ltmorcqekFvgSoBz46kVy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbesILI%2FbtsIZ8NIYum%2FltmorcqekFvgSoBz46kVy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;344&quot; data-origin-width=&quot;2318&quot; data-origin-height=&quot;1450&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RDS에서 가장 많은 비용이 발생하고 있었고, 프로젝트X를 수강하지 않는 이상 지원을 받기 힘들기에.. 어떻게든 방법을 찾아야 했다!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;아예 cloudtype 서비스로 옮겨보자!&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 첫 시도에서는 &lt;b&gt;AWS를 이용하지 않고 아예 cloudtype을 사용하자&lt;/b&gt;는 생각이었다. &lt;span style=&quot;color: #006dd7;&quot;&gt;하지만 cloudtype을 구축하는 과정에서 만난 문제들을 해결하지 못했고,&lt;/span&gt; &lt;b&gt;가장 비용이 많이 드는 RDS만이라도 cloudtype을 이용하자는 결론을&lt;/b&gt; 도출하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;1022&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtlkMT/btsIZWfJVHR/kJFBKVRDKbIVbuobtbX8EK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtlkMT/btsIZWfJVHR/kJFBKVRDKbIVbuobtbX8EK/img.png&quot; data-alt=&quot;난 아직 한참 멀었구나..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtlkMT/btsIZWfJVHR/kJFBKVRDKbIVbuobtbX8EK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtlkMT%2FbtsIZWfJVHR%2FkJFBKVRDKbIVbuobtbX8EK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;250&quot; height=&quot;252&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;1022&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;난 아직 한참 멀었구나..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cloudtype은 비교적 합리적인 비용으로 웹사이트/데이터베이스 등을 호스팅할 수 있는 국내 서비스다. 디스코드 채널에서 실시간으로 문제 해결도 공유 가능한 아주 친절한 서비스다..! 가격도 저렴하고 무료 호스팅까지 가능하니, 나처럼 돈이 없는데 프로젝트를 호스팅해야 한다면 추천한다.!!  &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2872&quot; data-origin-height=&quot;1400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boOrRR/btsIZmfdx0N/KkOje1oFnKTzB0KfuO9ivk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boOrRR/btsIZmfdx0N/KkOje1oFnKTzB0KfuO9ivk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boOrRR/btsIZmfdx0N/KkOje1oFnKTzB0KfuO9ivk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboOrRR%2FbtsIZmfdx0N%2FKkOje1oFnKTzB0KfuO9ivk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;268&quot; data-origin-width=&quot;2872&quot; data-origin-height=&quot;1400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mariadb 사용하기&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서론은 끝마치고, cloudtype에서 RDS 호스팅을 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2028&quot; data-origin-height=&quot;1034&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8QnPu/btsIZx1F5YI/kHxmSfQlGKgMw0gXal8Lo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8QnPu/btsIZx1F5YI/kHxmSfQlGKgMw0gXal8Lo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8QnPu/btsIZx1F5YI/kHxmSfQlGKgMw0gXal8Lo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8QnPu%2FbtsIZx1F5YI%2FkHxmSfQlGKgMw0gXal8Lo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;357&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2028&quot; data-origin-height=&quot;1034&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 나의 경우 이미 초기 세팅은 모두 마친 상태다. 또한 test 서버와 연동될 mariaDB가 이미 하나 실행되고 있는 상태다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 세팅이라고 해도 사실 별 거 없다. 여러 서비스를 호스팅할 프로젝트(나의 경우 화면상 보이는 koala)를 하나 생성해주고 워크스페이스처럼 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로덕션 환경용 DB를 생성해주기 위해 &lt;span style=&quot;color: #006dd7;&quot;&gt;화면상 오른쪽 하늘색 화살표&lt;/span&gt;를 클릭해서 DB를 생성해주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;1378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kDKMC/btsIYYTbqkv/fbOG6j8u4FG29kqNckf930/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kDKMC/btsIYYTbqkv/fbOG6j8u4FG29kqNckf930/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kDKMC/btsIYYTbqkv/fbOG6j8u4FG29kqNckf930/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkDKMC%2FbtsIYYTbqkv%2FfbOG6j8u4FG29kqNckf930%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;415&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;1378&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이렇게 다양한 옵션들을 확인할 수 있다! 여기서 MariaDB를 검색해주자. 참고로, Dockerfile로 올릴 수 있는 도커 템플릿도 있고 SpringBoot 템플릿도 있고, Github 저장소와 바로 연동하여 배포도 할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1396&quot; data-origin-height=&quot;1128&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btASGb/btsI0zEe5bR/U8j5qISv77Ey0VwiQLp2TK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btASGb/btsI0zEe5bR/U8j5qISv77Ey0VwiQLp2TK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btASGb/btsI0zEe5bR/U8j5qISv77Ey0VwiQLp2TK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtASGb%2FbtsI0zEe5bR%2FU8j5qISv77Ey0VwiQLp2TK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;444&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1396&quot; data-origin-height=&quot;1128&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB를 검색해서 클릭하면, DB 세팅 화면이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yMY7f/btsIZ8tDnuR/aghd1imBu72klqnCQAkZ51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yMY7f/btsIZ8tDnuR/aghd1imBu72klqnCQAkZ51/img.png&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;1188&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;51.4&quot; style=&quot;width: 50.807208%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yMY7f/btsIZ8tDnuR/aghd1imBu72klqnCQAkZ51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyMY7f%2FbtsIZ8tDnuR%2Faghd1imBu72klqnCQAkZ51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1330&quot; height=&quot;1188&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UyueX/btsI0tK4n9M/MNPu5Ryzgk5TI2S49sVpo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UyueX/btsI0tK4n9M/MNPu5Ryzgk5TI2S49sVpo0/img.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;1200&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;48.6&quot; style=&quot;width: 48.030002%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UyueX/btsI0tK4n9M/MNPu5Ryzgk5TI2S49sVpo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUyueX%2FbtsI0tK4n9M%2FMNPu5Ryzgk5TI2S49sVpo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1270&quot; height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB 설정과 버전, 패스워드를 입력한다. 필자는 &lt;b&gt;안전한 운영을 위해 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;더 많은 옵션을 이용해서&lt;/span&gt;username, password까지 추가&lt;/b&gt;하였다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;822&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1fRKf/btsI0Fj8qgx/8qmHKkEIbn4Bn93wCdVJK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1fRKf/btsI0Fj8qgx/8qmHKkEIbn4Bn93wCdVJK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1fRKf/btsI0Fj8qgx/8qmHKkEIbn4Bn93wCdVJK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1fRKf%2FbtsI0Fj8qgx%2F8qmHKkEIbn4Bn93wCdVJK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;365&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;822&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 성능을 선택해준다. cloudtype은 무료료 제공하는 CPU와 메모리 수준이 있다. 만약 대용량 데이터를 담아야 하거나, 성능이 매우 좋은 DB를 써야 한다면 구독 플랜을 업그레이드 시켜주면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1780&quot; data-origin-height=&quot;918&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uzC2T/btsIZdpabTn/nVbiRR3r0P0HStCoL1H5kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uzC2T/btsIZdpabTn/nVbiRR3r0P0HStCoL1H5kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uzC2T/btsIZdpabTn/nVbiRR3r0P0HStCoL1H5kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuzC2T%2FbtsIZdpabTn%2FnVbiRR3r0P0HStCoL1H5kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;335&quot; data-origin-width=&quot;1780&quot; data-origin-height=&quot;918&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 설정을 마치고 DB의 워크로드를 확인해보면 정상적으로 실행 중임을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅에서는 이 DB를 직접 AWS 서버와 연결해서 운영하는 내용에 대해 담아보면 좋을 것같다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로 인해 서버 비용을 확실히 줄일 수 있어서 다행이다!! 길이 없으면 길을 만들며 간다는 말이 떠오른다. 런칭까지 아주 파이팅이다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/CloudType</category>
      <category>AWS</category>
      <category>cloudtype</category>
      <category>DB</category>
      <category>DevOps</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/290</guid>
      <comments>https://cobinding.tistory.com/290#entry290comment</comments>
      <pubDate>Fri, 9 Aug 2024 14:53:05 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 네트워크 원리(1) 웹 브라우저가 메시지를 만든다.</title>
      <link>https://cobinding.tistory.com/289</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;성공과 실패를 결정하는 1% 네트워크 원리를 공부하고 정리한 글이다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;1248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xtqJK/btsKg6n46G2/kZEOiInJa1KUtQvO6q0Cjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xtqJK/btsKg6n46G2/kZEOiInJa1KUtQvO6q0Cjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xtqJK/btsKg6n46G2/kZEOiInJa1KUtQvO6q0Cjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxtqJK%2FbtsKg6n46G2%2FkZEOiInJa1KUtQvO6q0Cjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;250&quot; height=&quot;298&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;1248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;STORY 1 |&amp;nbsp; HTTP 리퀘스트 메시지를 작성한다.&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1) URL 입력과 해독, HTTP 메시지 주고 받기&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;URL(Uniform Resource Locator)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URL은 사실 http: 뿐만 아니라 ftp:, file:, &lt;a href=&quot;mailto:xn--2o2b&quot;&gt;mailto:&lt;/a&gt;로 시작하는 것 등 여러가지가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;보통 우리가 브라우저를 사용할 때는 &lt;b&gt;웹 서버에 접근하는 클라이언트&lt;/b&gt;로 사용하지만, 브라우저의 기능은 다양하기 때문이다. 파일을 다운로드/업로드하는 FTP의 클라이언트 기능이나 메일 전송 클라이언트 기능도 가지고 있다.&lt;/li&gt;
&lt;li&gt;브라우저는 몇 개의 클라이언트 기능을 겸비한 복합적인 클라이언트 소프트웨어다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;각종 URL 형식 예시와 공통점&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFf9UD/btsKhPeKBw5/eQvUcZ00yGNZ34F46rEdr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFf9UD/btsKhPeKBw5/eQvUcZ00yGNZ34F46rEdr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFf9UD/btsKhPeKBw5/eQvUcZ00yGNZ34F46rEdr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFf9UD%2FbtsKhPeKBw5%2FeQvUcZ00yGNZ34F46rEdr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;450&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;가장 앞부분에서 브라우저에 액세스하는 방법을 나타낸다. ex) http: ftp: news:..&lt;/li&gt;
&lt;li&gt;맨 앞부분의 액세스하는 방법(프로토콜)에 따라 뒤 형식이 달라진다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2) 브라우저는 먼저 URL을 해독한다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는 웹 서버에 보내는 &lt;b&gt;Request Message를 작성하기 위해서 URL을 먼저 해독한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; URL 형식으로부터 어떤 통신인지(프로토콜)를 알 수 있고, 이에 따른 정보들이 존재하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; URL 해독을 통해 클라이언트의 접근 위치를 파악한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(a) URL의 요소&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;&amp;lt;http://csstudy.com/network/login.html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http: 데이터 출처에 접근하는 방법 즉, 프로토콜&lt;/li&gt;
&lt;li&gt;// : 나중에 이어지는 문자열이 서버의 이름임을 나타냄&lt;/li&gt;
&lt;li&gt;/디렉토리명/+ &amp;hellip; +/파일명: 데이터 출처의 경로&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;login.html은 파일명으로 생략 가능하다.&lt;/span&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://csstudy.com/network&quot;&gt;http://csstudy.com/network&lt;/a&gt; 이런식으로 작성하면 되는데, URL 끝 부분이 /로 끝나는 것은 뒤에 파일이 생략되어 있다는 뜻이다.&lt;br /&gt;&lt;br /&gt;파일명을 생략할 때는 미리 서버측에서 &amp;lsquo;index.html&amp;rsquo;, &amp;lsquo;default.html&amp;rsquo; 등과 같은 파일명을 설정해둔다. 젤 뒤의 / 마저 생략하는 경우도 있다. 하지만 이렇게 디렉토리명까지 생략해버리면 무엇을 요청하는지 알기 힘드므로, 지나친 생략일 수 있다. 경로명이 아무것도 없는 경우에는 루트 디렉토리의 아래에 미리 설정된 파일명의 파일로 접근하는 방식으로 인정되고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;86&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0jGG0/btsKg7N4yS2/KZ63pkd0TXRISGKQfK7NtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0jGG0/btsKg7N4yS2/KZ63pkd0TXRISGKQfK7NtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0jGG0/btsKg7N4yS2/KZ63pkd0TXRISGKQfK7NtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0jGG0%2FbtsKg7N4yS2%2FKZ63pkd0TXRISGKQfK7NtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;45&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;86&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.3) HTTP&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;URL 해독 후, HTTP 프로토콜을 사용하여 웹 서버에 접근한다.&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트는 서버측에 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&amp;lsquo;무엇을(URI)&amp;rsquo; &amp;lsquo;어떻게(HTTP method)&amp;rsquo;&lt;/span&gt; 하겠다는 요청을 보낸다.&lt;br /&gt;URI에 나타낸 데이터를 읽고싶다거나, 클라이언트 측에서 입력한 데이터를 URI로 나타낸 프로그램에 전달하고 싶다는 식의 동작이 대표적이다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;요청을 받은 서버는 그 속에 쓰여있는 내용을 해독한다. 그리고 URI와 메시지를 조사하여 요구에 따라 동작하고, 결과 데이터를 응답 메시지에 저장한다. 응답 메시지의 맨 앞부분에는 실행 결과가 정상 종료인지 이상이 있는지를 나타내는 &lt;b&gt;상태 코드&lt;/b&gt;가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;HTTP Request Message&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP는 쓰는 방법이 정해져 있으므로, 이에 맞게 브라우저가 Request Message를 제작한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wUPSx/btsKgseft2w/fYKbp2VAzXTZxaHkuikyJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wUPSx/btsKgseft2w/fYKbp2VAzXTZxaHkuikyJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wUPSx/btsKgseft2w/fYKbp2VAzXTZxaHkuikyJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwUPSx%2FbtsKgseft2w%2FfYKbp2VAzXTZxaHkuikyJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;316&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;메소드, URI, HTTP 버전&amp;nbsp;&lt;/li&gt;
&lt;li&gt;메시지 헤더&lt;br /&gt;&lt;br /&gt;- 리퀘스트 메시지에 쓰는 URI는 한 개만으로 결정되어 있으므로 파일을 한 번에 한 개씩만 읽을 수 있기 때문에 파일을 따로따로 읽어야 한다.&lt;br /&gt;- 첫 번째 행에서 주어진 내용보다 더 자세한 정보가 필요한 경우에 2행부터 &lt;b&gt;메시지 헤더&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;를 사용한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;HTTP Response Message&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Request에 대한 결과를 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;응답 상태 코드와 문구를&lt;/li&gt;
&lt;li&gt;응답 메시지가 돌아오면 데이터를 추출한 후 화면에 표시한다.&lt;br /&gt;&lt;br /&gt;-&amp;nbsp; 서버 측은 단순히 &lt;span style=&quot;color: #006dd7;&quot;&gt;한 개의 리퀘스트에 대해 한 개의 응답만 돌려 보낸다. &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;필요한 파일을 읽고 레이아웃을 정하여 화면에 표시하는 상태로 전체 동작을 조정하는 것은 결국 브라우저의 역할이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY 2 | 웹 서버의 IP 주소를 DNS 서버에 조회한다.&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1) 도메인 - IP 매핑&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 메시지를 만들면 OS에 의뢰하여 웹 서버에게 송신한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저는 URL을 해독하거나 HTTP 메시지를 만들지만, 직접 전송하고 네트워크에 송출하는 기능은 없다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;OS에 송신 의뢰를 할 때에는&lt;span style=&quot;background-color: #f6e199;&quot;&gt; 도메인명이 아니라 IP 주소로 상대를 지정한다.&lt;/span&gt; 따라서 HTTP 메시지를 만드는 동작의 다음은 도메인명을 통해 IP 주소를 조사하는 것이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2) IP 주소&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP/IP 네트워크는 작은 서브넷을 라우터로 연결하여 전체 네트워크를 완성한다. 쉽게 말해서 서브넷은 아파트 주소의 동, 호수는 ip 주소라고 생각하면 된다. 택배 받는 사람 기입란에 접근 대상의 주소를 기입하여 데이터를 보낸다. 그러면 라우터가 받는 사람을 보고 이것이 어떤 호수에 있는지 조사하여 그곳으로 데이터를 중계한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IP는 주소와 같은 것이므로 다른 곳에 같은 값이 할당되면 안 된다. &amp;rarr;&lt;/li&gt;
&lt;li&gt;OO동 XX호의 OO동은 서브넷 번호 즉, 네트워크 번호다. XX호는 호스트 번호로, 이 둘을 합쳐서 IP 주소라고 한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;IP 주소 = 네트워크 주소 + 호스트 주소&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E5KUv/btsKgbjvmye/A9Tw2tDFL9ew0vl4OlqzXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E5KUv/btsKgbjvmye/A9Tw2tDFL9ew0vl4OlqzXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E5KUv/btsKgbjvmye/A9Tw2tDFL9ew0vl4OlqzXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE5KUv%2FbtsKgbjvmye%2FA9Tw2tDFL9ew0vl4OlqzXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;500&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;송신측이 메시지를 보내면, 서브넷 안에 있는 허브가 데이터를 운반한다.&lt;/li&gt;
&lt;li&gt;송신측에서 가장 가까운 라우터까지 운반된다.&lt;/li&gt;
&lt;li&gt;그리고 라우터가 이 메시지 상대를 확인하여 다음 라우터를 판단하고, 거기에 보내도록 지시한다.&lt;/li&gt;
&lt;li&gt;이후 다시 서브넷의 허브가 라우터까지 메시지를 보낸다.&lt;/li&gt;
&lt;li&gt;이 동작을 반복하면 최종적으로 상대의 데이터가 도착한다. (TCP/IP의 기본 개념)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;IP 주소 표기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(a) IP 주소 본체의 표기 방법&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;10.11.12.13&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(b) IP 주소 본체와 같은 방법으로 네트워크를 표기하는 방법&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;10.11.12.13/255.255.255.0(넷마스크)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(b) 네트워크 번호의 비트 수로 넷마스크를 표기하는 방법&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;10.11.12.13/24&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(c) 서브넷을 나타내는 주소&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;10.11.12.0/24
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 부분이 모두 0이면 서브넷 자체를, 1이면 서브넷 전체에 대한 브로드캐스트를 나타낸다.&lt;/li&gt;
&lt;li&gt;브로드캐스트 주소 &amp;rarr; 네트워크 내의 모든 주소에 데이터를 전달할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;넷마스크&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a.a.a.a처럼 &lt;b&gt;8비트씩 점으로 구분하여 10진수로 표기하는 것&lt;/b&gt;이 일반적인 IP 주소다. 이것만으로는 어느 부분이 네트워크 번호인지, 호스트 번호인지 알 수 없다. &amp;rarr; IP 주소의 규칙에서는 네트워크 번호와 호스트 번호의 두 가지를 합쳐서 32비트로 한다는 것만 결정되어 있을 뿐, 내역은 결정되어 있지 않다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크를 구축할 때 사용자가 직접 내역을 결정할 수 있다.&lt;/li&gt;
&lt;li&gt;이 내역을 나타내는 정보를 IP 주소에 덧붙이는데, 이 정보를 넷마스크라고 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1으로 표기된 것이 네트워크 번호, 0으로 표기된 것이 호스트 번호&lt;/li&gt;
&lt;li&gt;&lt;b&gt;1111111 / 111111&lt;/b&gt; / 0000000 / 00000&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3) 도메인명과 IP 주소를 구분하여 사용하는 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 사용자가 IP 주소를 일일이 외우는 것에 대한 어려움은 공감이 가는데&amp;hellip; 그럼 OS 측에도 도메인명을 알려준 다음, 상대를 지정하고 통신하면 안 될까?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행 효율이라는 관점에서 좋은 방법이 아니다. 인터넷 내부에는 다수의 규칙이 있고, 그것들이 연대하여 데이터를 운반한다.&lt;/li&gt;
&lt;li&gt;IP는 32비트, 즉 4 바이트에 해당하는 개수밖에 없지만, 도메인명은 적어도 수십 바이트부터 최대 255바이트나 있다. 이 문자를 취급하기 위해서 그만큼 라우터가 부하되어 데이터 운반 속도에 직접적인 영향을 미칠 것이다. 도메인 &amp;rarr; DNS 서버 &amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 그렇다면 고성능 라우터를 쓰면 해결되지 않을까?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한계가 있다. 한계로 인해 방대한 양의 데이터가 인터넷 내부에 정체되어 있을 수 있다.&lt;/li&gt;
&lt;li&gt;데이터 양도 기술의 발전에 못지 않은 속도로 증가하고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 사람은 이름을 사용하고, 라우터는 IP 주소를 사용한다는 방법이 고안되었고, 현재 이 방법이 정착되어 있다. 이름만 알면 IP 주소를 알 수 있다거나, IP 주소를 알면 이름을 알 수 있다는 원리를 사용하여 양쪽의 차이를 해소하는 것이 DNS의 원리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STORY 3 | 전세계의 DNS 서버가 연대한다.&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3.1) DNS 리졸버&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핵심은 IP 주소를 조사하기 위해 &lt;b&gt;&amp;rdquo;DNS에 질의를 한다.&amp;rdquo;&lt;/b&gt;는 것이다. 이를 위해 DNS 서버에 조회 요청을 날리고, 그에 대한 응답 메시지를 받는다. 결국 &lt;span style=&quot;color: #006dd7;&quot;&gt;DNS 서버에 대해 클라이언트로 동작한다.&lt;/span&gt; 이 DNS 클라이언트를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;DNS 리졸버&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;DNS 리졸버(or 단순히 리졸버):&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;가장 가까운 DNS 서버에 IP 주소를 질의한다. 즉, DNS 서버에 클라이언트로 동작한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네임 리졸루션(name resolution) :&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;DNS 원리를 사용하여 IP 주소를 조사하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3.2) 소켓 라이브러리와 리졸버&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;소켓 라이브러리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리졸버는 소켓 라이브러리에 내장되어 있는 기능이다. 소켓 라이브러리는 &lt;b&gt;OS가 네트워크 기능을 할 때 필요한 작업&lt;/b&gt;들을 모아놓은 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1306&quot; data-origin-height=&quot;662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cssEu1/btsIZc3X5WN/KgGY13zsrNWWNtxKKkRPv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cssEu1/btsIZc3X5WN/KgGY13zsrNWWNtxKKkRPv1/img.png&quot; data-alt=&quot;모든 개발자를 위한 HTTP 웹 기본 지식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cssEu1/btsIZc3X5WN/KgGY13zsrNWWNtxKKkRPv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcssEu1%2FbtsIZc3X5WN%2FKgGY13zsrNWWNtxKKkRPv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;279&quot; data-origin-width=&quot;1306&quot; data-origin-height=&quot;662&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모든 개발자를 위한 HTTP 웹 기본 지식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt; 브라우저는 URL을 해독하거나 HTTP 메시지를 만들지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;직접 전송하고 네트워크에 송출하는 기능은 없기 때문에&lt;/b&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;OS에 이를 의뢰하게 된다&lt;/span&gt;. 따라서 위 이미지처럼 &lt;u&gt;애플리케이션 단에서&lt;/u&gt; 소켓 라이브러리와 같은 기능을 호출함으로써 송수신을 부탁하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3) 리졸버 호출과 위치&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bimxfH/btsIZcJHWjG/DqguGgwKQ2D7mOfgfQYJm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bimxfH/btsIZcJHWjG/DqguGgwKQ2D7mOfgfQYJm1/img.png&quot; data-alt=&quot;https://yeonyeon.tistory.com/243&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bimxfH/btsIZcJHWjG/DqguGgwKQ2D7mOfgfQYJm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbimxfH%2FbtsIZcJHWjG%2FDqguGgwKQ2D7mOfgfQYJm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;211&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://yeonyeon.tistory.com/243&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소켓 라이브러리 내에 구현된 리졸버가 OS에게 실행을 의뢰하고, OS는 이 요청 세미지 대로 송수신을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세부 내용은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.4) 리졸버 - OS 내부 작동&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플리케이션 안에 &lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt;&amp;lt;메모리 영역&amp;gt; = gethostbyname(&amp;rdquo;www.cobinding.tistory.com&amp;rdquo;)&lt;/span&gt;과 같이 쓰면 리졸버가 호출되어, DNS 서버에 대한 IP 주소 조회 동작을 실행할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;952&quot; data-origin-height=&quot;1386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MPdkB/btsIZwVa9IP/f0l6h7hmJXcvAT9frRSy9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MPdkB/btsIZwVa9IP/f0l6h7hmJXcvAT9frRSy9K/img.png&quot; data-alt=&quot;이 화살표를 하나하나 따라가보면 이해에 도움된다! 풀어쓰면 아래와 같다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MPdkB/btsIZwVa9IP/f0l6h7hmJXcvAT9frRSy9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMPdkB%2FbtsIZwVa9IP%2Ff0l6h7hmJXcvAT9frRSy9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;655&quot; data-origin-width=&quot;952&quot; data-origin-height=&quot;1386&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이 화살표를 하나하나 따라가보면 이해에 도움된다! 풀어쓰면 아래와 같다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;브라우저와 같은 &lt;span style=&quot;color: #006dd7;&quot;&gt;애플리케이션이 리졸버(socket 라이브러리)를 호출&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;리졸버가 OS에게&lt;/span&gt; DNS 서버에 조회 메시지를 보내야 한다는 메시지 생성해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;프로토콜 스택 호출&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;프로토콜 스택: OS 내부에 내장된 네트워크 제어용 SW&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;OS가 리졸버에게 메시지 받아서 네트워크 영역의 송수신 담당&lt;/li&gt;
&lt;li&gt;LAN 어댑터를 통해서 메시지가 DNS 서버를 향해 송신&lt;/li&gt;
&lt;li&gt;DNS 서버의 응답이 클라이언트에게 위 과정을 거꾸로 거쳐서 반송&lt;/li&gt;
&lt;li&gt;리졸버는 OS에게 응답 메시지를 받아서, IP 주소를 추출하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;lt;메모리 영역&amp;gt;에 저장하고 애플리케이션에게 이 IP 주소를 건네줌.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;*DNS 서버에 메시지를 송신할 때 IP 주소는 필요 없을까?&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 컴퓨터에 미리 TCP/IP 설정 하나하나가 되어있다. 리졸버는 여기에서 설정된 DNS 서버의 IP 주소에 조회 메시지를 보낸다!&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;STORY 4 | 프로토콜 스택에 메시지 송신을 의뢰한다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IP 주소를 조사하고 난 후, 웹 서버에 메시지를 송신하도록 &lt;b&gt;프로토콜 스택&lt;/b&gt;에 의뢰한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;프로토콜 스택:&lt;/b&gt; 말 그대로 여러 계층이 쌓여있는, 계층 구조로 이루어진 프로토콜을 말한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1) 프로토콜 스택에 메시지 송신을 의뢰하는 방법&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 주소를 조회했을 때처럼, Socket 라이브러리의 함수를 사용하되, 결정된 순서대로 호출한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Socket 라이브러리에서는 프로토콜 스택을 호출한다.&lt;/li&gt;
&lt;li&gt;컴퓨터 사이에는 파이프같은 것을 통해 데이터를 양방향으로 주고 받는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;772&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coRBQI/btsKg62IVxP/BWprNl4UrAvaaNAYf37jz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coRBQI/btsKg62IVxP/BWprNl4UrAvaaNAYf37jz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coRBQI/btsKg62IVxP/BWprNl4UrAvaaNAYf37jz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoRBQI%2FbtsKg62IVxP%2FBWprNl4UrAvaaNAYf37jz0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;306&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;772&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;두 컴퓨터가 연결되는 순서&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;먼저 서버측에서 소켓(데이터 출입구) 생성&lt;/li&gt;
&lt;li&gt;소켓에 클라이언트가 연결 요청을 하도록 대기&lt;/li&gt;
&lt;li&gt;클라이언트에서 소켓(데이터 출입구) 생성, 파이프를 늘려 서버 측의 소켓에 연결 요청 &amp;rarr; IP 주소와 포트 번호 등을 가지고 connect 함수 호출&lt;/li&gt;
&lt;li&gt;양쪽의 소켓이 연결되어 송수신 준비 완료&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1314&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6X4FX/btsKhe0u93b/J6YVGe0UPifOFOGzgqNBo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6X4FX/btsKhe0u93b/J6YVGe0UPifOFOGzgqNBo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6X4FX/btsKhe0u93b/J6YVGe0UPifOFOGzgqNBo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6X4FX%2FbtsKhe0u93b%2FJ6YVGe0UPifOFOGzgqNBo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;326&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1314&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;디스크립터:&lt;/b&gt; 소켓을 생성할 때 반환되는 정수 값 &amp;rarr; 애플리케이션에서 소켓을 식별하기 위해 사용&lt;/li&gt;
&lt;li&gt;IP 주소: 액세스 대상 서버의 ip 주소&lt;/li&gt;
&lt;li&gt;포트 번호: IP 주소를 통해 어느 컴퓨터인지 알아냈으면,
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;프로토콜 스택:&lt;/b&gt; 말 그대로 여러 계층이 쌓여있는, 계층 구조로 이루어진 프로토콜을 말한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>CS/Network</category>
      <category>CS</category>
      <category>network</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/289</guid>
      <comments>https://cobinding.tistory.com/289#entry289comment</comments>
      <pubDate>Thu, 8 Aug 2024 20:36:21 +0900</pubDate>
    </item>
    <item>
      <title>[python] 파이썬으로 진법변환하기</title>
      <link>https://cobinding.tistory.com/288</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 듯하면서도 구현을 할 때마다 까먹는 부분이 생겨서 이번 포스팅을 통해 확실하게 잡아보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) &lt;span style=&quot;color: #006dd7;&quot;&gt;파이썬 함수를 통해&lt;/span&gt; 진법 변환하기&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;십진수를 이진수로, 이진수를 십진수로 파이썬 함수를 통해 변환하는 것은 상당히 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-1) 십진수를 이진수로 변환하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;십진수를 이진수로 변환할 때는 &lt;b&gt;bin&lt;/b&gt;이라는 함수를 사용한다. 이 함수는 정수를&lt;b&gt; &quot;0b&quot;가 붙은 이진수로 변환&lt;/b&gt;해준다. &lt;a href=&quot;https://docs.python.org/3/library/functions.html#bin&quot;&gt;(문서 참조)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1723045616678&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Built-in Functions&quot; data-og-description=&quot;The Python interpreter has a number of functions and types built into it that are always available. They are listed here in alphabetical order.,,,, Built-in Functions,,, A, abs(), aiter(), all(), a...&quot; data-og-host=&quot;docs.python.org&quot; data-og-source-url=&quot;https://docs.python.org/3/library/functions.html#bin&quot; data-og-url=&quot;https://docs.python.org/3/library/functions.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uR82q/hyWKFx4HOw/LoI0C1i3XJuqKKe4oOS9Y0/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200&quot;&gt;&lt;a href=&quot;https://docs.python.org/3/library/functions.html#bin&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.python.org/3/library/functions.html#bin&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uR82q/hyWKFx4HOw/LoI0C1i3XJuqKKe4oOS9Y0/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Built-in Functions&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The Python interpreter has a number of functions and types built into it that are always available. They are listed here in alphabetical order.,,,, Built-in Functions,,, A, abs(), aiter(), all(), a...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.python.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1723043749183&quot; class=&quot;maxima&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;number = 10
binary_number = bin(number) # 0b1010&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-2) 이진수를 십진수로 변환하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이진수를 십진수로 변환할 때는 우리가 흔히 쓰는 &lt;b&gt;int&lt;/b&gt; 함수를 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;int 함수에 첫 번째 인자로 들어갈 수 있는 자료형은 문자형임을 주의하자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1723044227979&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;binary_number = 1010
number = int(str(binary_number), 2) # 10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) &lt;span style=&quot;color: #006dd7;&quot;&gt;알고리즘을 통해 직접&lt;/span&gt; 진법 변환하기&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-1) 십진수를 N진수로 변환하기&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;743&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKXOwd/btsIYzjYT5J/qi3oFkGzWXVsHlinZ1bef0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKXOwd/btsIYzjYT5J/qi3oFkGzWXVsHlinZ1bef0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKXOwd/btsIYzjYT5J/qi3oFkGzWXVsHlinZ1bef0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKXOwd%2FbtsIYzjYT5J%2Fqi3oFkGzWXVsHlinZ1bef0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;318&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;743&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 보통 &lt;b&gt;이진수를 십진수로 변환할 때&lt;/b&gt; 위와 같은 과정을 거친다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2라는 숫자로 자릿수를 나타낸 것이므로, 각 자릿수에 해당하는 숫자와 진수를 곱하여 이를 모두 더하면 십진수를 구할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1723045435474&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def to_decimal(binary_number):
    number = 0
    n = len(binary_number)-1
    for i in range(n, -1, -1):
        number += 2**(n-i) * binary_number[i]
    
    return number&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-3) 십진수를 N진수로 변환하기&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;659&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CWBTk/btsIYY4OCc4/C5fe2jEl5DXmQKuO9jD4BK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CWBTk/btsIYY4OCc4/C5fe2jEl5DXmQKuO9jD4BK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CWBTk/btsIYY4OCc4/C5fe2jEl5DXmQKuO9jD4BK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCWBTk%2FbtsIYY4OCc4%2FC5fe2jEl5DXmQKuO9jD4BK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;249&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;659&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 십진수가 있을 때 N진수로 변환하고 싶다면 위와 같은 계산 과정을 거친다. 이를 코드로 표현해주면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1723046401181&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def to_binary(decimal_number):
    binary_list = []
    while decimal_number != 0:
        binary_list.append(decimal_number%2)
        decimal_number //= 2

    # 연산한 값을 reverse하여 이진수 도출
    re_binary_list = []
    for i in range(len(binary_list)-1, -1, -1):
        re_binary_list.append(binary_list[i])
    
    return re_binary_list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.codetree.ai/missions/5/problems/decimal-and-binary-number-2/description&quot;&gt;코드트리에서 이 문제&lt;/a&gt;를 풀면서 정리해보았다! 앞으로 까먹지 말고 필요할 때 잘 쓰자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>언어/PYTHON</category>
      <category>Problem Solving</category>
      <category>구현</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/288</guid>
      <comments>https://cobinding.tistory.com/288#entry288comment</comments>
      <pubDate>Thu, 8 Aug 2024 01:01:40 +0900</pubDate>
    </item>
    <item>
      <title>[DevOps] 멀티모듈 프로젝트 CI/CD 적용 - 설계 구상 편</title>
      <link>https://cobinding.tistory.com/287</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 운영 환경 인프라를 구축하기 위해 설계를 팀에 공유해보기로 하였다. 사실 3달 정도 전에 Github Actions + Elastic Beanstalk으로 실습을 해보았으나, 실패했던 경험이 있기에 다시 돌아와서 설계 과정을 세세히 기록해본다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;또한, 이전에는 사용해보고 싶었던 기술(Jenkins)과 추천 받은 기술로 구성하였다면, 이번엔 &lt;b&gt;온전히 내 논리와 지식을 활용하여, 배포를 하자마자 사용자가 생길 우리 서버.. &lt;/b&gt;의 아키텍처를 구상해보려고 한다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 이전 시도 복기와 현재 상황&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나에게 가장 까다롭게 다가오는 것은 &lt;b&gt;멀티 모듈 설계와 AWS 사용&lt;/b&gt;이다. 이 까다로운 문제를 하나씩 풀어 나가는 과정을 작성해보자..!&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-1) 모듈 구조 파악하기&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1890&quot; data-origin-height=&quot;1244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckqzxr/btsKSQTvLRY/oABE6tXjpCPtT3KubbaxiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckqzxr/btsKSQTvLRY/oABE6tXjpCPtT3KubbaxiK/img.png&quot; data-alt=&quot;코어 모듈이 라이브러리 형식으로 하위 프로젝트 2개에 사용되고 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckqzxr/btsKSQTvLRY/oABE6tXjpCPtT3KubbaxiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fckqzxr%2FbtsKSQTvLRY%2FoABE6tXjpCPtT3KubbaxiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;461&quot; data-origin-width=&quot;1890&quot; data-origin-height=&quot;1244&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;코어 모듈이 라이브러리 형식으로 하위 프로젝트 2개에 사용되고 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;core(공통 라이브러리)&lt;/li&gt;
&lt;li&gt;admin(관리자 모듈)&lt;/li&gt;
&lt;li&gt;user(사용자 모듈)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-2) 인턴십에 수행했던 PoC를 떠올려 보자.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티 모듈 구조의 CI/CD를 설계 및 구축, 운영한 경험이 있다.&lt;br /&gt;&lt;b&gt;멀티 모듈&lt;/b&gt;을 자동으로 빌드 및 배포하기 위해서는 다음과 같은 동작 순서를 따른다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;공통 라이브러리 모듈을 빌드 및 저장소에 push&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;하위 모듈들이&lt;b&gt; 저장소에 있는 공통 라이브러리를 의존하여&lt;/b&gt; 빌드 및 실행&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;GCP&lt;/b&gt;에서는 이 과정을 &lt;b&gt;Cloud Build와 Artifact Registry&lt;/b&gt;로 수행이 가능하다. 정확히 말하자면 70% 정도만 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그 이유는 Instance Group으로 생성되는 인스턴스들이 Registry의 파일을 가져와 자동으로 배포하려면,&lt;br /&gt;&lt;b&gt;Terraform이나 엔서블 등과 같은 도구를 통해 컨트롤&lt;/b&gt; 해야한다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/cobinding/gcp-docker-ci-cd&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #0070d1;&quot;&gt;해당 레포&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;에서 필자가 수행한 PoC를 확인할 수 있다. MVN, NPM, Docker에 대한 자료가 있으니, 참고하길 바란다.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;GitHub - cobinding/gcp-docker-ci-cd: [DevOps] Docker와 GCP로 CI/CD 구축&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;[DevOps] Docker와 GCP로 CI/CD 구축. Contribute to cobinding/gcp-docker-ci-cd development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/cobinding/gcp-docker-ci-cd&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/LaXYV/hyWCF5V6oL/156C8BfHzzmhT76KC2ZER0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot; data-og-url=&quot;https://github.com/cobinding/gcp-docker-ci-cd&quot;&gt;&lt;a href=&quot;https://github.com/cobinding/gcp-docker-ci-cd&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/cobinding/gcp-docker-ci-cd&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/LaXYV/hyWCF5V6oL/156C8BfHzzmhT76KC2ZER0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - cobinding/gcp-docker-ci-cd: [DevOps] Docker와 GCP로 CI/CD 구축&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[DevOps] Docker와 GCP로 CI/CD 구축. Contribute to cobinding/gcp-docker-ci-cd development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-3) 현재 프로젝트는 AWS를 사용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 경험을 살려서 멀티 모듈 CI/CD 구조를 어림잡아 생각해보면 &lt;br /&gt;&lt;u&gt;① 빌드 자동화 도구&lt;/u&gt; &lt;u&gt;② 공통 라이브러리 저장소&lt;/u&gt; &lt;u&gt;③ 배포 자동화 도구&lt;/u&gt; 이 세 가지가 필요하다.&lt;br /&gt;이에 상응하는 &lt;b&gt;AWS 기술은 &lt;u&gt;codebuild, s3, code deploy&lt;/u&gt; 등이 있다.&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 멀티모듈 CI/CD를 컨트롤할 github actions 이해&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-1) 기존에 사용했을 때 실패했던 이유&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개요에서 밝혔듯, 이 기술을 이미 써본 적이 있지만, 당시에 제대로 구축을 못 했었다. &lt;b&gt;실패했던 이유는&lt;/b&gt; 회사에서 PoC를 진행했을 땐 이미 서버, 인프라 구조가 이미 설계 및 구축되어 있었기에 이에 맞춰서 CI/CD에만 집중할 수 있었다. 그런데 지금 프로젝트에서는&lt;b&gt; 인프라 자체를 스스로 설계 및 구상해야 해서 혼동한 점이 많았다. (멀티 모듈 인프라는 직접 설계 및 구축 해보는 것이 이번 프로젝트에서 처음이었다..  ) &lt;/b&gt;&amp;nbsp;그래서 공통 라이브러리를 의존하는 부분이나 개별 인스턴스에 github actions와 EB를 세팅해야 하는 부분 등을 놓쳤었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;결론적으로 기술 이해도 부족/약간의 조급함ㅎ이 문제였고 백엔드 팀장님과 날밤까며 계속 시도를 했지만 원인 파악도 제대로 하지 못하고 실패를 했었다..   다시 돌아간다면 나의 경험을 떠올리면서 이 프로젝트에 적용을 어떻게 할지 침착하게 구상 및 설계했을 것같다..!&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;868&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSC6NP/btsIHEOfYVK/JBB81gNFCcGK5owK0P17Xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSC6NP/btsIHEOfYVK/JBB81gNFCcGK5owK0P17Xk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSC6NP/btsIHEOfYVK/JBB81gNFCcGK5owK0P17Xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSC6NP%2FbtsIHEOfYVK%2FJBB81gNFCcGK5owK0P17Xk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;340&quot; height=&quot;269&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;868&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-2) github actions 조건문 분기로&amp;nbsp;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;변동사항이 있는 모듈만 빌드하기!&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼, 위 사고의 과정을 따라 프로젝트에 적용해야할 사항들을 작성해보자.&lt;br /&gt;멀티 모듈 CI/CD를 위해 &lt;b&gt;요구되는 사항과 해결 방법&lt;/b&gt;은 다음과 같다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;1. 한 모듈이 수정되면 &lt;u&gt;&lt;b&gt;해당 모듈만 빌드 및 배포&lt;/b&gt;&lt;/u&gt;되어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;한 모듈이 수정될 때 모든 모듈이 같이 빌드 및 배포한다면 아무리 자동화가 되어있다하더라도 비효율적이다. 변동 사항 없이 잘 돌아가고 있는 모듈까지 재빌드 및 재실행하기보다, 변동사항이 생긴 모듈만 빌드 및 배포되도록 설정하자.&lt;/li&gt;
&lt;li&gt;github actions는 yaml 파일처럼 동작 과정을 작성할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;예시 코드&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;name: CI/CD Pipeline

on:
  push:
    branches:
      - main
    paths:
      - 'common/**'  # common 모듈에 대한 변경
      - 'admin/**'   # admin 모듈에 대한 변경
      - 'user/**'    # user 모듈에 대한 변경

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up JDK
        uses: actions/setup-java@v2
        with:
          java-version: '11'  # 필요한 Java 버전으로 변경

      - name: Build and test
        run: |
          if [[ $(git diff --name-only HEAD^ HEAD) =~ ^common/ ]]; then
            cd common
            ./gradlew build
            cd ..
          fi
          if [[ $(git diff --name-only HEAD^ HEAD) =~ ^admin/ ]]; then
            cd admin
            ./gradlew build
            cd ..
          fi
          if [[ $(git diff --name-only HEAD^ HEAD) =~ ^user/ ]]; then
            cd user
            ./gradlew build
            cd ..
          fi

      - name: Deploy to Elastic Beanstalk
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_REGION: 'your-aws-region'
        run: |
          if [[ $(git diff --name-only HEAD^ HEAD) =~ ^common/ ]]; then
            cd common
            ./gradlew assemble
            zip -r application.zip build/libs/*.jar
            eb init your-app-name --region $AWS_REGION
            eb deploy
            cd ..
          fi
          if [[ $(git diff --name-only HEAD^ HEAD) =~ ^admin/ ]]; then
            cd admin
            ./gradlew assemble
            zip -r application.zip build/libs/*.jar
            eb init your-app-name --region $AWS_REGION
            eb deploy
            cd ..
          fi
          if [[ $(git diff --name-only HEAD^ HEAD) =~ ^user/ ]]; then
            cd user
            ./gradlew assemble
            zip -r application.zip build/libs/*.jar
            eb init your-app-name --region $AWS_REGION
            eb deploy
            cd ..
          fi&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;2. 하위 모듈들은 각각의 EB를 가지면서 이 공통 라이브러리를 항상 의존해야한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;이는 EB의 .ebextensions 파일로 컨트롤 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) 결론 및 정리&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-1) Github Actions&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;하나의 github actions가 여러 EB를 포인팅하고 있는 구조&lt;/b&gt;를 갖는다. 이 구조에서 하위 모듈들이 &lt;span style=&quot;color: #006dd7;&quot;&gt;독립적으로 빌드될 수 있도록&lt;/span&gt; 조건분기를 실행파일에 설정한다.&amp;nbsp;&lt;br /&gt;* 여기서는 빌드 과정만 신경 쓴다!&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #000000;&quot;&gt;&lt;b&gt;이벤트 기반 트리거&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;color: #000000;&quot;&gt;특정 이벤트(나의 경우 merge)에 반응하여 자동으로 워크플로우를 실행한다.&lt;/li&gt;
&lt;li style=&quot;color: #000000;&quot;&gt;on&lt;span&gt;&amp;nbsp;&lt;/span&gt;키워드를 사용하여 어떤 이벤트가 발생할 때 워크플로우가 실행될지를 정의한다. ➡️ 설계에서 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;color: #000000;&quot;&gt;&lt;b&gt;워크플로우 정의&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;color: #000000;&quot;&gt;YAML 형식으로 워크플로우를 정의합니다. 각 워크플로우는 여러 개의 작업(Job)으로 구성될 수 있다.&lt;/li&gt;
&lt;li style=&quot;color: #000000;&quot;&gt;각 작업은 특정 환경에서 실행되며, 다양한 명령어와 스크립트를 실행할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작업 및 단계&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;:&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;각 작업은 여러 단계를 포함 (예를 들어 코드 체크아웃, 의존성 설치, 테스트 실행, 빌드 생성 등)을 포함할 수 있다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;하위 모듈들은 저장소에 저장된&lt;s&gt; 공통 라이브러리의 의존성을 설치하고&lt;/s&gt; 빌드를 생성한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;caret-color: #006dd7;&quot;&gt;하위 모듈들은 이 과정을 통해 &lt;b&gt;각각의 모듈을 자신의 변경사항이 바뀌었을 때만 빌드하는 것에 집중&lt;/b&gt;한다. &lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;caret-color: #006dd7;&quot;&gt;하위 모듈이 공통 라이브러리를 의존하는 것은 EB의 .ebextensions에서 진행한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3-2) Elastic Beanstalk&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;배포 패키지&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;color: #000000;&quot;&gt;Elastic Beanstalk에 배포할 애플리케이션은 ZIP 파일 형식의 배포 패키지로 준비되어야 한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;EB 배포를 커스텀하기 위해 .ebextensions/ 에 옵션을 작성한&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;다.&amp;nbsp;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;ebextenstions: EB를 실행할 때 최초로 실행되는 파일&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;하위 모듈은 저장소에 저장된 공통 라이브러리 의존&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;caret-color: #006dd7;&quot;&gt;공통 모듈은 빌드된 파일을 저장소에 push&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;caret-color: #006dd7;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;다음 편에서는 직접 설계한 것과 설정 파일들을 다뤄보겠다. 쉽지 않은 길이 예상된다... 그래도 아자아자  &lt;/p&gt;</description>
      <category>DevOps/DevOps</category>
      <category>AWS</category>
      <category>DevOps</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/287</guid>
      <comments>https://cobinding.tistory.com/287#entry287comment</comments>
      <pubDate>Mon, 22 Jul 2024 20:02:41 +0900</pubDate>
    </item>
    <item>
      <title>[백준/1547] 공 | 두 수의 전환</title>
      <link>https://cobinding.tistory.com/286</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1547&quot;&gt;공&lt;/a&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;알고리즘&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘을 공부해본 사람이라면 한 번쯤은 겪었을 법한 &lt;b&gt;두 수의 전환&lt;/b&gt;을 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;772&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pVhO0/btsInNEgyTs/mMqfrV3Hd6oDUzTfXrDXJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pVhO0/btsInNEgyTs/mMqfrV3Hd6oDUzTfXrDXJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pVhO0/btsInNEgyTs/mMqfrV3Hd6oDUzTfXrDXJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpVhO0%2FbtsInNEgyTs%2FmMqfrV3Hd6oDUzTfXrDXJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;250&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;772&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에서 주의해야 할 점은 &lt;span style=&quot;color: #ee2323;&quot;&gt;공의 번호와 인덱스를 달리 생각해야 한다&lt;/span&gt;는 것이다. 예를 들어, 1번 공과 3번 공의 위치를 바꿀 때 배열의 인덱스는 그대로고 1번 공과 3번 공의 위치만 즉, 인덱스 번호 바뀌는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;791&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YwEg4/btsIovCTsoG/CAihN9TiYjJi8XgS18RMPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YwEg4/btsIovCTsoG/CAihN9TiYjJi8XgS18RMPk/img.png&quot; data-alt=&quot;ㅡ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YwEg4/btsIovCTsoG/CAihN9TiYjJi8XgS18RMPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYwEg4%2FbtsIovCTsoG%2FCAihN9TiYjJi8XgS18RMPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;246&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;791&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ㅡ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 인덱스로만 다룰 순 없고&lt;b&gt; for문으로 공의 번호가 저장된 배열을 돌면서 입력으로 주어지는 x,y의 위치를 찾아야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720108680707&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for _ in range(m):
    x,y=map(int,input().split())

    # x 위치 먼저 찾기
    for i in range(len(arr)):
        if arr[i]==x:
            # x가 있는 위치의 값을 tmp_x 변수에 저장해 둠.
            tmp_x=arr[i]
        
            # 이후 y의 위치 찾기
            for j in range(len(arr)):
                # x,y가 있는 위치의 값을 서로 reverse. 이때, tmp_x 변수에 저장해둔 값 사용
                if arr[j]==y:
                    arr[i]=arr[j]
                    arr[j]=tmp_x
                    flag=True
                    break
                    
            # x,y의 위치 찾기가 끝났다면 루프를 종료
            if flag:break&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;입력으로 주어진 x,y 즉 서로 바뀌어야 하는 값의 위치를 먼저 찾는다.&lt;/li&gt;
&lt;li&gt;위의 그림으로 나타낸 두 수의 전환 알고리즘을 적용하여 값을 서로 바꿔준다.&lt;/li&gt;
&lt;li&gt;값 변경이 완료되면 반복문을 종료한다. 이때 flag를 사용해서 바깥 반복문도 종료해준다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전체코드&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;pre id=&quot;code_1720108715693&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys;input=sys.stdin.readline

m=int(input())
arr=[0,1,2,3]
tmp_x=0
flag=False

for _ in range(m):
    x,y=map(int,input().split())

    # x 위치 먼저 찾기
    for i in range(len(arr)):
        if arr[i]==x:
            # x가 있는 위치의 값을 tmp_x 변수에 저장해 둠.
            tmp_x=arr[i]
        
            # 이후 y의 위치 찾기
            for j in range(len(arr)):
                # x,y가 있는 위치의 값을 서로 reverse. 이때, tmp_x 변수에 저장해둔 값 사용
                if arr[j]==y:
                    arr[i]=arr[j]
                    arr[j]=tmp_x
                    flag=True
                    break
                    
            # x,y의 위치 찾기가 끝났다면 루프를 종료
            if flag:break
    
print(arr[1])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;알게된 점&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어려운 문제는 아닌데 예상보다 시간이 걸렸다. 이유는 문제를 풀면서 &quot;어 이 간단한게 왜 안되지?&quot;라는 생각에 사로잡혔기 때문. 즉시 객관화를 하고 천천히 시도해서 다행히 잘 풀었다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 풀면서 느낀/앞으로도 유지해야 할 &lt;b&gt;좋은 태도!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;1. 큰 문제를 작은 문제 단위로 나누어서 생각&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-ke-size=&quot;size16&quot;&gt;예를 들어, 이 문제에서 두수의 전환이라는 큰 흐름은 잘 잡았었다. 그런데 미세한 부분(값의 위치를 찾는다, 변경은 ~지점에서 한다.) 이런 걸 놓쳤다. 하나씩 1,2,3... #1, #2, #3... 이런식으로 작은 단위로 나누고 차차 풀어나가는 태도를 갖자.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;2. 내가 생각한 게 코드로 이어지지 않을 수 있다는 객관화&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;3. 너무 과몰입하면 산으로 가니까 문제를 풀다가도 한 템포 떨어져서 보기&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/BOJ&amp;amp;Programmers</category>
      <category>Problem Solving</category>
      <category>구현</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/286</guid>
      <comments>https://cobinding.tistory.com/286#entry286comment</comments>
      <pubDate>Fri, 5 Jul 2024 01:03:14 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 내가 만든 웹 서비스 WireShark로 패킷 분석하기 - 7계층 분석</title>
      <link>https://cobinding.tistory.com/285</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cobinding.tistory.com/284&quot; target=&quot;_self&quot;&gt;&lt;span&gt;내가 만든 웹 서비스 WireShark로 패킷 분석하기 1편&lt;/span&gt;&lt;/a&gt;에 이어서, 2편에서는 본격적인 WireShark 패킷 내용을 살펴보자.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 서버는 로컬 컴퓨터에서 spring boot를 IntelliJ로 실행하고, 포트포워딩하여 15001 포트로 클라이언트가 접속할 수 있도록 세팅되어있다.&lt;/li&gt;
&lt;li&gt;서버 위주의 패킷을 분석하고, 특정 상황에서는 서버와 클라이언트 양측의 패킷을 각각 분석한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;유저 시나리오는 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클라이언트의 접속(채팅방 이용자)&lt;/li&gt;
&lt;li&gt;유저 1의 채팅방 만들기&lt;/li&gt;
&lt;li&gt;유저 2의 채팅방 접속(다대다 채팅 가능)&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;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;S1# 클라이언트(채팅방 이용자)의 메인 화면 접속&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2584&quot; data-origin-height=&quot;1258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ut0sR/btsH9uxs8G5/ZSTHdHiK4zC7RudFmhIrhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ut0sR/btsH9uxs8G5/ZSTHdHiK4zC7RudFmhIrhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ut0sR/btsH9uxs8G5/ZSTHdHiK4zC7RudFmhIrhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fut0sR%2FbtsH9uxs8G5%2FZSTHdHiK4zC7RudFmhIrhK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;316&quot; data-origin-width=&quot;2584&quot; data-origin-height=&quot;1258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;[서버 측 패킷]&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2856&quot; data-origin-height=&quot;910&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBIMnp/btsIdWmNTTt/oIhiCmZHgyz5rap6bqaGe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBIMnp/btsIdWmNTTt/oIhiCmZHgyz5rap6bqaGe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBIMnp/btsIdWmNTTt/oIhiCmZHgyz5rap6bqaGe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBIMnp%2FbtsIdWmNTTt%2FoIhiCmZHgyz5rap6bqaGe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2856&quot; height=&quot;910&quot; data-origin-width=&quot;2856&quot; data-origin-height=&quot;910&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;두 클라이언트(51115, 51116)의 요청과 서버의 응답에 대한 패킷 전송과정을 확인할 수 있다. 가장 처음 요청을 한 &lt;b&gt;클라이언트 1&lt;/b&gt;의 TCP 연결 요청은 &lt;span style=&quot;color: #ef5369;&quot;&gt;서버측의 [SYN, ACK]를 받지 못해,&lt;/span&gt; 서버 측에서 지속적으로 &lt;span style=&quot;color: #ef5369;&quot;&gt;TCP Retransmission&lt;/span&gt;을 실행하는 것을 확인할 수 있다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TCP Retransmission&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 &lt;b&gt;RDT 통신&lt;/b&gt;을 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;RDT(Reliable Data Transfer)란?&lt;/b&gt; 불안정한 네트워크 환경에서도 안정적으로 데이터를 전송하는 기술로, 신뢰성 있는 데이터 전송을 의미한다. &lt;a href=&quot;https://en.wikipedia.org/wiki/User_Datagram_Protocol&quot; target=&quot;_self&quot;&gt;&lt;span&gt;UDP&lt;/span&gt;&lt;/a&gt;와 뚜렷하게 비교되는 TCP 특징 중 하나다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;TCP는 RDT 통신을 위해서 &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Retransmission&lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;매커니즘을 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;TCP는 데이터 수신이 잘 되었는지 확인하기 위한 ACK라는 일종의 flag를 사용한다.&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt; 통신을 하는 과정에서 ACK 패킷을 받지 못하면 일정 시간 내에 해당 패킷을 다시 전송한다.&lt;/span&gt; 불확실한 네트워크 상황에서 데이터 손실을 방지하기 위한 RDT 통신 기술이다!&lt;/li&gt;
&lt;li&gt;이러한 Retransmission과 관련하여&amp;nbsp; timer, Dupicate ACK 등을 알아야 하지만, TCP 관련 포스팅에서 상세하게 다루도록 한다. 패킷 분석에서 서버 측의 SYN, ACK를 받지 못해 Retransmission을 지속적으로 실행하는 것을 확인하는 것이 핵심이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;RST(Reset) 패킷&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결을 즉시 종료하기 위한 패킷이다. 비정상적이거나 예기치 않은 상황에서 연결을 재설정하거나 아예 끊기 위한 기술이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 패킷 캡처내용에서 클라이언트 1(51115)의 연결 요청에 대해 Retransmission을 하다가, 제대로 연결이 되지 않자 RST로 끊고 511129라는 클라이언트로 재연결을 하는 것을 확인할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TCP 3way-handshaking과 HTTP Request/Response&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2856&quot; data-origin-height=&quot;456&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ndv1t/btsIchTyfrv/XJlNFMAgWVlqrj6ezxKdTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ndv1t/btsIchTyfrv/XJlNFMAgWVlqrj6ezxKdTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ndv1t/btsIchTyfrv/XJlNFMAgWVlqrj6ezxKdTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fndv1t%2FbtsIchTyfrv%2FXJlNFMAgWVlqrj6ezxKdTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2856&quot; height=&quot;456&quot; data-origin-width=&quot;2856&quot; data-origin-height=&quot;456&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;한 번에 연결이 잘 된 &lt;b&gt;클라이언트 2(51116)&lt;/b&gt;의 경우, 3 Way handshaking이 잘 되었고 TCP 연결 이후 HTTP 응답﹒요청을 주고 받는 걸 패킷 캡처를 통해 확인하였다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;GET /HTTP/1.1 살펴보기&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;[APPLICATION Layer]&lt;/b&gt; 해당 패킷은 HTTP의 버전 1.1을 사용함을 알 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[TRANSPORT Layer]&lt;/b&gt; TCP 프로토콜을 사용하며 src/dst port는 각각 (51116, 8080)이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[NETWORK/LINK Layer]&lt;/b&gt; 상세 내역을 보면&lt;span style=&quot;color: #006dd7;&quot;&gt; IPv4 버전&lt;/span&gt;을 이용하고, &lt;span style=&quot;color: #006dd7;&quot;&gt;L2 계층에서는 Ethenet을 사용&lt;/span&gt;하며 &lt;span style=&quot;color: #006dd7;&quot;&gt;src/dst MAC 주소&lt;/span&gt;를 각각(04:09:&amp;hellip;, 64:79:&amp;hellip;) 확인할 수 있다.&lt;span style=&quot;color: #006dd7;&quot;&gt; IP Fragmentation은 사용되지 않았다. (flag를 통해 확인)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2350&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NiKJ3/btsIdhkHqAb/ckiXdK1z1m275a9qJjUiK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NiKJ3/btsIdhkHqAb/ckiXdK1z1m275a9qJjUiK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NiKJ3/btsIdhkHqAb/ckiXdK1z1m275a9qJjUiK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNiKJ3%2FbtsIdhkHqAb%2FckiXdK1z1m275a9qJjUiK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2350&quot; height=&quot;698&quot; data-origin-width=&quot;2350&quot; data-origin-height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;HTTP/1.1 304란?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 패킷에서 눈 여겨볼 점은 바로 아래에서 두 번째의&lt;b&gt; HTTP 상태코드 304 번이다.(No. 20052 패킷) &lt;/b&gt;이는 요청된 자원에 대한 리디렉션, &lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;lsquo;Not Modified&amp;rsquo;&lt;/span&gt;를 뜻합니다. &lt;b&gt;요청 값에 대해 변경된 사항이 없으므로 캐시되어있는 자원을 이용하겠다는 뜻이다.&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;실제로 패킷을 캡처하기 전에 팀원들과 테스트를 진행했었다. 이 내용들이 WireShark에 반영되는 것이 신기했다. 만약 이 캐시를 지우려면 서버를 재시작 하거나, chrome 쿠키 삭제 기능을 활용할 수 있다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;[HTTP 304 패킷 상세 내용]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xJ9t1/btsIdRy7d3E/4ObAVo7gf8wzMg8u2vz1KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xJ9t1/btsIdRy7d3E/4ObAVo7gf8wzMg8u2vz1KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xJ9t1/btsIdRy7d3E/4ObAVo7gf8wzMg8u2vz1KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxJ9t1%2FbtsIdRy7d3E%2F4ObAVo7gf8wzMg8u2vz1KK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;510&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;S2# 채팅방 만들기&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[서버 측 패킷]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2766&quot; data-origin-height=&quot;1130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vdfUA/btsIdcRhRR0/NOMzgtNYrQmoXnB4bDmGyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vdfUA/btsIdcRhRR0/NOMzgtNYrQmoXnB4bDmGyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vdfUA/btsIdcRhRR0/NOMzgtNYrQmoXnB4bDmGyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvdfUA%2FbtsIdcRhRR0%2FNOMzgtNYrQmoXnB4bDmGyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2766&quot; height=&quot;1130&quot; data-origin-width=&quot;2766&quot; data-origin-height=&quot;1130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;채팅방을 생성할 때 GET, POST 요청을 진행하고 endpoint를 반환받는 모습을 확인할 수 있다. 실제로 서버 측 코드로 구현한 것과 같이 요청이 주고 받는 모습이 캡처되었다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eRdpYO/btsH90QcA1p/5MECkGTsMd5pfYFoRMS1r1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eRdpYO/btsH90QcA1p/5MECkGTsMd5pfYFoRMS1r1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eRdpYO/btsH90QcA1p/5MECkGTsMd5pfYFoRMS1r1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeRdpYO%2FbtsH90QcA1p%2F5MECkGTsMd5pfYFoRMS1r1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;206&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;같은 /chat/create 경로로 GET, POST 요청이 진행된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GET: view 표시&lt;/li&gt;
&lt;li&gt;POST: view redirect 및 chatRoom DB 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;S3# 채팅방 접속 및 웹소켓 연결(STOMP)&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2062&quot; data-origin-height=&quot;1732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XA8V0/btsIbQWbfhy/5T5VlSDjn3NZW42sfV4Vm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XA8V0/btsIbQWbfhy/5T5VlSDjn3NZW42sfV4Vm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XA8V0/btsIbQWbfhy/5T5VlSDjn3NZW42sfV4Vm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXA8V0%2FbtsIbQWbfhy%2F5T5VlSDjn3NZW42sfV4Vm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;546&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2062&quot; data-origin-height=&quot;1732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;[클라이언트 측 패킷]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2632&quot; data-origin-height=&quot;936&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SAHKx/btsId2G9Io1/r0iuf3vVkUA4632MKVAuGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SAHKx/btsId2G9Io1/r0iuf3vVkUA4632MKVAuGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SAHKx/btsId2G9Io1/r0iuf3vVkUA4632MKVAuGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSAHKx%2FbtsId2G9Io1%2Fr0iuf3vVkUA4632MKVAuGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2632&quot; height=&quot;936&quot; data-origin-width=&quot;2632&quot; data-origin-height=&quot;936&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2466&quot; data-origin-height=&quot;110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgGrWW/btsIcPI0Irq/kqbK3PW15OdpmDs8zyFgv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgGrWW/btsIcPI0Irq/kqbK3PW15OdpmDs8zyFgv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgGrWW/btsIcPI0Irq/kqbK3PW15OdpmDs8zyFgv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgGrWW%2FbtsIcPI0Irq%2FkqbK3PW15OdpmDs8zyFgv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2466&quot; height=&quot;110&quot; data-origin-width=&quot;2466&quot; data-origin-height=&quot;110&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;위 패킷 중 이 세가지 부분이 &lt;b&gt;WebSocket(STOMP)&lt;/b&gt; 통신의 연결 시작 부분이다. &lt;span style=&quot;color: #ef5369;&quot;&gt;웹소켓은 처음 연결 방법에서 HTTP를 사용한다&lt;/span&gt;.(1편에 잘 나와있음) 하나씩 살펴보자.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;HTTP/1.1 304란?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[패킷 상세 내용]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2076&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3pFPq/btsIcQulpBe/jzHfpt8zlMVYrNkMm7Q7CK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3pFPq/btsIcQulpBe/jzHfpt8zlMVYrNkMm7Q7CK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3pFPq/btsIcQulpBe/jzHfpt8zlMVYrNkMm7Q7CK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3pFPq%2FbtsIcQulpBe%2FjzHfpt8zlMVYrNkMm7Q7CK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2076&quot; height=&quot;544&quot; data-origin-width=&quot;2076&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Upgrade-Insecure-Requests:&lt;/b&gt; HTTP 요청을 자동으로 HTTPS로 리다이렉트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Accept-Encoding&lt;/b&gt;: 콘텐츠 인코딩 방식은 gzip, deflate 압축을 지원한다는 뜻이다. 이러한 방식은 데이터 전송량을 줄여서 네트워크 효율을 높이는 방식이다. &lt;b&gt;실제로 서버 프로그래밍을 할 때도 대용량 데이터를 통신할 때는 JSON 데이터를 gzip으로 주고 받은 경험이 있어서 이를 직접 확인 해보는 것이 흥미로웠다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Accept-Language&lt;/b&gt;: 클라이언트가 선호하는 언어는 &amp;ldquo;ko&amp;rdquo; 즉, 한국어다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;이러한 헤더 정보를 통해 서버는 클라이언트에게 적합한 응답을 전송하고, 소통한다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;WebSocket 연결 설정 및 HTTP 101&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2574&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfYeFV/btsIcV951q9/1tRMh4L57ln2vAJ1kBQrF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfYeFV/btsIcV951q9/1tRMh4L57ln2vAJ1kBQrF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfYeFV/btsIcV951q9/1tRMh4L57ln2vAJ1kBQrF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfYeFV%2FbtsIcV951q9%2F1tRMh4L57ln2vAJ1kBQrF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2574&quot; height=&quot;332&quot; data-origin-width=&quot;2574&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;HTTP 101는 클라이언트가&lt;span style=&quot;color: #d44c47;&quot;&gt; 서버에게 프로토콜 전환을 요청&lt;/span&gt;했으며, 서버는 이를 승인한다는 상태 코드다. 이 패킷을 살펴보면 요청 헤더 부분에 &lt;b&gt;WebSocket과 관련한 stomp 프로토콜 정보가 있&lt;/b&gt;음을 확인할 수 있다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;[패킷 상세 내용]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkCD2L/btsIeflXAir/n44NT3MDfzKFgoKFC9bsd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkCD2L/btsIeflXAir/n44NT3MDfzKFgoKFC9bsd0/img.png&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;478&quot; data-is-animation=&quot;false&quot; style=&quot;width: 70.55%; margin-right: 10px;&quot; data-widthpercent=&quot;71.38&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkCD2L/btsIeflXAir/n44NT3MDfzKFgoKFC9bsd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkCD2L%2FbtsIeflXAir%2Fn44NT3MDfzKFgoKFC9bsd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1450&quot; height=&quot;478&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLqT8D/btsH8CQO72Y/zkkq0nlIoDY6BtBNyjiTUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLqT8D/btsH8CQO72Y/zkkq0nlIoDY6BtBNyjiTUk/img.png&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;860&quot; style=&quot;width: 28.2872%;&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;28.62&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLqT8D/btsH8CQO72Y/zkkq0nlIoDY6BtBNyjiTUk/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLqT8D%2FbtsH8CQO72Y%2Fzkkq0nlIoDY6BtBNyjiTUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1046&quot; height=&quot;860&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;서버 구현부에서 사용한 &lt;span style=&quot;color: #d44c47;&quot;&gt;@EnableWebScoketMessageBroker &lt;/span&gt;&lt;span style=&quot;color: #37352f;&quot;&gt;애노테이션은 내장된 STOMP 메시징 브로커를 활성화한다. 이렇게 적용된 통신 방식이 WireShark에서도 반영되는 것을 확인할 수 있었다.&lt;/span&gt;&lt;span style=&quot;color: #37352f;&quot;&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #37352f;&quot;&gt;또한, 패킷을 통해 &lt;/span&gt;&lt;span style=&quot;color: #37352f;&quot;&gt;&lt;u&gt;구현 내부 상세&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;color: #37352f;&quot;&gt;를&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #37352f;&quot;&gt;알 수 있었다. 바로 &lt;/span&gt;&lt;span style=&quot;color: #37352f;&quot;&gt;&lt;b&gt;WebSocket 연결 확장 기능&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #37352f;&quot;&gt;이다. 위 패킷 상세 내용을 살펴보면,&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;permessage-deflate&lt;/b&gt;는 &lt;span style=&quot;color: #ef5369;&quot;&gt;WebSocket 메시지 데이터를 압축하여 전송&lt;/span&gt;할 수 있게 하여, 네트워크 대역폭을 효율적으로 사용할 수 있도록 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;client_max_window_bits=15&lt;/b&gt; 라는 필드로 최대 압축창 크기를 지정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;[WebSocket 패킷 상세 내용]&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blF49N/btsH8GFx73U/7C2WNvxGJjYOOuEde3rsu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blF49N/btsH8GFx73U/7C2WNvxGJjYOOuEde3rsu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blF49N/btsH8GFx73U/7C2WNvxGJjYOOuEde3rsu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblF49N%2FbtsH8GFx73U%2F7C2WNvxGJjYOOuEde3rsu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;337&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FIN: 채팅의 마지막 부분이다&lt;/li&gt;
&lt;li&gt;MASK: WebSocket 프로토콜은 보안상의 이유로, 클라이언트에서 보내는 모든 데이터 프레임에 마스킹을 적용하도록 규정하고 있다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;중간자 공격(Man-in-the-Middle Attack) 방지&lt;/li&gt;
&lt;li&gt;4byte 길이의 임의의 값으로, 데이터프레임에 XOR 연산을 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #37352f;&quot;&gt;&lt;b&gt;[추가 Layer 분석 내용]&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1714&quot; data-origin-height=&quot;726&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rmQGf/btsIcWOGPsr/Dd4AzsouwE8yJMRWuTl3r1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rmQGf/btsIcWOGPsr/Dd4AzsouwE8yJMRWuTl3r1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rmQGf/btsIcWOGPsr/Dd4AzsouwE8yJMRWuTl3r1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrmQGf%2FbtsIcWOGPsr%2FDd4AzsouwE8yJMRWuTl3r1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;318&quot; data-origin-width=&quot;1714&quot; data-origin-height=&quot;726&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;해당 부분 또한 각 계층에서 사용되는 정보는 위 상황과 비슷했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;L2 계층에서는 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Ethernet II&lt;/b&gt;&lt;/span&gt;를 사용하고 있으며, &lt;b&gt;src/dst MAC 정보는 ac:49:db:dd:f1:ae와 88:3c:1c:ed:0b:40&lt;/b&gt;이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Network Layer 프로토콜은 Ip이며 IPv4 버전을 사용&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;클라이언트와 서버의 주소가 src/dst 주소인 192.168.219.105, 58.123.180.186으로 나타난다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;IP Fragmentation은 사용되지 않으며 Offset 역시 0이다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;전송 계층의 프로토콜은 TCP이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Checksum이 올바르게 되어 [correct] [Good] 등의 표시를 확인할 수 있으며, 문제 없이 통신이 진행되었다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;S3# 채팅하기&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2336&quot; data-origin-height=&quot;1554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AoiCE/btsJxjaqona/RJWwXVCzrgkkpapACkJK5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AoiCE/btsJxjaqona/RJWwXVCzrgkkpapACkJK5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AoiCE/btsJxjaqona/RJWwXVCzrgkkpapACkJK5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAoiCE%2FbtsJxjaqona%2FRJWwXVCzrgkkpapACkJK5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2336&quot; height=&quot;1554&quot; data-origin-width=&quot;2336&quot; data-origin-height=&quot;1554&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2604&quot; data-origin-height=&quot;514&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyA6MU/btsJxykT9sk/sWTXMEqEmeLbjttCnnvwp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyA6MU/btsJxykT9sk/sWTXMEqEmeLbjttCnnvwp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyA6MU/btsJxykT9sk/sWTXMEqEmeLbjttCnnvwp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyA6MU%2FbtsJxykT9sk%2FsWTXMEqEmeLbjttCnnvwp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2604&quot; height=&quot;514&quot; data-origin-width=&quot;2604&quot; data-origin-height=&quot;514&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebSocket 연결이 생성된 이후부터는 TCP 통신을 통해 안정적으로 채팅이 오가는 것을 확인할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;S5# 이메일 전송하기&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mFXbW/btsIaD8jrpa/gVoupkI9lB2Le5gDok5Am1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mFXbW/btsIaD8jrpa/gVoupkI9lB2Le5gDok5Am1/img.png&quot; data-origin-width=&quot;2886&quot; data-origin-height=&quot;1794&quot; style=&quot;width: 46.3385%; margin-right: 10px;&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;46.88&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mFXbW/btsIaD8jrpa/gVoupkI9lB2Le5gDok5Am1/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmFXbW%2FbtsIaD8jrpa%2FgVoupkI9lB2Le5gDok5Am1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2886&quot; height=&quot;1794&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1QSXv/btsH8HqV6ns/JXp9gKkZK9cIeuygvDESbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1QSXv/btsH8HqV6ns/JXp9gKkZK9cIeuygvDESbK/img.png&quot; data-origin-width=&quot;2198&quot; data-origin-height=&quot;1206&quot; style=&quot;width: 52.4987%;&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;53.12&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1QSXv/btsH8HqV6ns/JXp9gKkZK9cIeuygvDESbK/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1QSXv%2FbtsH8HqV6ns%2FJXp9gKkZK9cIeuygvDESbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2198&quot; height=&quot;1206&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;해당 API는 Google의 SMTP를 활용한 spring boot로 구현하였다. 따라서 구현한 사람의 gmail 계정과 앱 비밀번호를 사용하였기에, 보낸 사람(나)이 위와 같이 등록되어있다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;[클라이언트 측 패킷]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2422&quot; data-origin-height=&quot;304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MIMRT/btsIdTRchxj/OndYUKcJ9ra7X28gU3RAC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MIMRT/btsIdTRchxj/OndYUKcJ9ra7X28gU3RAC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MIMRT/btsIdTRchxj/OndYUKcJ9ra7X28gU3RAC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMIMRT%2FbtsIdTRchxj%2FOndYUKcJ9ra7X28gU3RAC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2422&quot; height=&quot;304&quot; data-origin-width=&quot;2422&quot; data-origin-height=&quot;304&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;메일 전송을 위해서 클라이언트가 서버로 POST 요청을 보내는 것이 확인된다. ➡️ 간단하게 이메일 주소만 입력하면 해당 이메일로 소개메일을 보내는 것을 구현했기에, 브라우저에 입력된 이메일 주소를 POST로 서버로 전송하도록 구현하였다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beNTG2/btsH83tCKT2/ZKik9zU2jYaib5KeEnBEu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beNTG2/btsH83tCKT2/ZKik9zU2jYaib5KeEnBEu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beNTG2/btsH83tCKT2/ZKik9zU2jYaib5KeEnBEu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeNTG2%2FbtsH83tCKT2%2FZKik9zU2jYaib5KeEnBEu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;70&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;116&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;POST 요청 패킷을 확인해보면, 이메일 전송 값이 인코딩을 통해 헤더에 담긴 모습을 확인할 수 있다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TCP ACKed unseen segment&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 패킷이 정상적으로 송수신되었지만 WireShark 상에서 패킷을 찾을 수 없는 경우에 발생한다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1760&quot; data-origin-height=&quot;992&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0Dcrn/btsH8LfFG0N/1ByiKSXepyu8zVo7nLKfgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0Dcrn/btsH8LfFG0N/1ByiKSXepyu8zVo7nLKfgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0Dcrn/btsH8LfFG0N/1ByiKSXepyu8zVo7nLKfgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0Dcrn%2FbtsH8LfFG0N%2F1ByiKSXepyu8zVo7nLKfgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;423&quot; data-origin-width=&quot;1760&quot; data-origin-height=&quot;992&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;패킷을 자세히 살펴 보면 ACK에 대한 누락임을 알 수 있고, 경고창을 함께 보여준다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;[서버 측 패킷]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2448&quot; data-origin-height=&quot;274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RAOZe/btsIdi40Ow4/GXwFQNZVpeoKoxm9d8eFaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RAOZe/btsIdi40Ow4/GXwFQNZVpeoKoxm9d8eFaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RAOZe/btsIdi40Ow4/GXwFQNZVpeoKoxm9d8eFaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRAOZe%2FbtsIdi40Ow4%2FGXwFQNZVpeoKoxm9d8eFaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2448&quot; height=&quot;274&quot; data-origin-width=&quot;2448&quot; data-origin-height=&quot;274&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;패킷을 통해 해당 API의 동작 과정을 알 수 있다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서버측에서는 메일 전송 POST 요청을 받고 SMTP 프로토콜을 사용하여 자신의 이름을 알린다. (No.2038 패킷)&lt;/li&gt;
&lt;li&gt;서버는 클라이언트에게 SMTP 기능을 지원하는 것을 알린다. (No.2047 패킷)&lt;/li&gt;
&lt;li&gt;이후, 서버와 클라이언트는 보안 연결을 설정하고, google 서버가 메일을 전송한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;[EHLO 상세 패킷]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1978&quot; data-origin-height=&quot;884&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HtI3o/btsIdVVMuzA/mAt1hvxiQMyGPoYx2aUYn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HtI3o/btsIdVVMuzA/mAt1hvxiQMyGPoYx2aUYn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HtI3o/btsIdVVMuzA/mAt1hvxiQMyGPoYx2aUYn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHtI3o%2FbtsIdVVMuzA%2FmAt1hvxiQMyGPoYx2aUYn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;290&quot; data-origin-width=&quot;1978&quot; data-origin-height=&quot;884&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;SMTP 전송 프로토콜은 TCP를 이용&lt;/b&gt;하므로, 첫 번째 단계에서 클라이언트와 서버 간 연결을 시작한다.&lt;br /&gt;이때, 클라이언트는 특화된 Hello 명령인&lt;span style=&quot;color: #ef5369;&quot;&gt; HELP or EHLO&lt;/span&gt;를 사용한다.&lt;span style=&quot;color: #ef5369;&quot;&gt;&amp;nbsp;위 패킷에서는 EHLO를 사용했다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2672&quot; data-origin-height=&quot;1112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxXoGy/btsIdFeADiC/SgpMIpJFtbU1LolpR6wsi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxXoGy/btsIdFeADiC/SgpMIpJFtbU1LolpR6wsi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxXoGy/btsIdFeADiC/SgpMIpJFtbU1LolpR6wsi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxXoGy%2FbtsIdFeADiC%2FSgpMIpJFtbU1LolpR6wsi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2672&quot; height=&quot;1112&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2672&quot; data-origin-height=&quot;1112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;이후 메일 서버와 요청 서버(웹 애플리케이션이 돌아가고 있는 곳)가 TLS 통신을 진행한다.&lt;br /&gt;HTTPS/TLS 관련 패킷 분석은 &lt;a href=&quot;https://garnet-waxflower-dcc.notion.site/Task-2-HTTP-TCP-94ca94fe5e794f0bbfde96cf9c2c2865?pvs=4&quot; target=&quot;_self&quot;&gt;&lt;span&gt;과제 1 제출물&lt;/span&gt;&lt;/a&gt;을 통해 확인할 수 있습니다.  &lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;HTTP에 관해...&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;서버 API를 짤 때, 항상 고민되었던 것이&lt;b&gt; 어떤 HTTP method를 사용할 것인가?&lt;/b&gt;였습니다. &lt;br /&gt;&amp;nbsp;&lt;br /&gt;각 HTTP 요청/응답 메시지를 세부적으로 살펴보면서, 클라이언트와 서버 간의 통신 과정을 실제로 확인할 수 있었습니다. HTTP 메서드, 헤더, 페이로드 등 네트워크 통신의 핵심 요소들이 어떻게 동작하는지 깊이 있게 이해할 수 있었고, HTTP에 한층 가까워졌습니다.&lt;br /&gt;&lt;br /&gt;평소에 Serialization과 관련하여 공부해야겠다 생각을 자주 했었는데, &lt;b&gt;이번 기회를 통해 HTTP 헤더를 직접적으로 분석할 수 있어, 통신에 대한 이해가 깊어짐을 체감했습니다.&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;네트워크 지식&lt;/h4&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark 분석을 통해 &lt;b&gt;네트워크 계층부터 애플리케이션 계층까지 전체적인 통신 과정을 확인&lt;/b&gt;할 수 있었습니다. 직접 패킷 분석을 하면서 TCP/IP 스택의 동작 원리, 패킷 구조, 흐름 제어 등 네트워크 기술에 대한 이해도를 크게 향상할 수 있었습니다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Network</category>
      <category>CS</category>
      <category>network</category>
      <category>Wireshark</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/285</guid>
      <comments>https://cobinding.tistory.com/285#entry285comment</comments>
      <pubDate>Sun, 23 Jun 2024 17:25:23 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 내가 만든 웹 서비스 WireShark로 패킷 분석하기 - Spring WebSocket &amp;amp; STOMP</title>
      <link>https://cobinding.tistory.com/284</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;box-sizing: border-box; font-size: 15px; *** rgb(254, 137, 67): 관련글의 테두리색 입니다. ***font-family: Arial, 돋움, Dotum, AppleGothic, sans-serif; padding: 10px; margin: 0px; line-height: 1.5; background-color: #ffffff; border: 2px solid #4f84c4;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1편 - 소켓 통신 프로젝트 기능 구현 소개&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2편 -&amp;nbsp; 패킷 분석 내용 소개&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WireShark 패킷 분석을 위한 애플리케이션인 만큼, 소켓 통신을 구현하여 서버와 클라이언트, 클라이언트끼리의 통신이 잘 보이는 &lt;span data-token-index=&quot;1&quot;&gt;채팅 서비스&lt;/span&gt;를 구현하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소켓 통신은 &lt;a href=&quot;https://github.com/LikelionKau&quot;&gt;KAU 멋쟁이 사자처럼 고기의 소켓통신 세션&lt;/a&gt;을 참고하였고, SMTP 프로토콜 통신을 캡처하기 위해 이메일 전송을 추가하였다.소스코드는 &lt;a href=&quot;https://github.com/KAU-socket-project/socket-springboot&quot;&gt;해당 깃허브&lt;/a&gt;에서 확인 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 구현은 크게 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WebSocket 통신&lt;/li&gt;
&lt;li&gt;구글 SMTP 서버를 통한 메일 발송&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;WebSocket 통신 구현에 사용된 기술&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;크게 3가지로 분류된다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- WebSocket(HTTP와 비교)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- STOMP&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- Message Broker&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;WebSocket이란? HTTP와 다른 점은 뭘까?&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP는 클라이언트가 서버에 요청을 보내고, 서버가 응답을 하는&lt;b&gt; &lt;span data-token-index=&quot;1&quot;&gt;요청-응답 방식&lt;/span&gt;&lt;/b&gt;의 프로토콜이다. 이 프로토콜은 connectionless라는 특징을 통해 자원 낭비를 방지하고 효율적인 통신을 한다. 하지만 이러한 비연결성이 오히려 자원의 손실을 야기하는 경우도 있다. 바로 채팅, 주식, 게임 등 &lt;span style=&quot;color: #d44c47;&quot; data-token-index=&quot;1&quot;&gt;실시간 성이 반영되어야 하는 경우다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;Application Layer에서 HTTP 프로토콜을 사용하면 실시간 통신에 어려움이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단방향 통신으로 서버가 먼저 클라이언트에게 요청을 할 수 없다.&lt;/li&gt;
&lt;li&gt;요청 없이는 응답을 할 수 없다. 즉, 양방향 실시간 통신이 불가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;HTTP로 실시간 통신이 아예 불가능한 것은 아니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; HTTP/2&lt;/b&gt;부터는 Stream이라는 기능으로 서버와 클라이언트가 동시에 데이터를 주고 받는 실시간 양방향 통신이 가능하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HTTP Long Polling&lt;/b&gt; 방식을 사용하면, 클라이언트가 서버에 대해 지속적으로 연결을 유지한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이와 같은 극복점이 있다고 하더라도 HTTP는 주로 요청-응답 패턴에 맞추어 설계되어 있다. 따라서 이 프로토콜을 사용할 경우 오버헤드나 요청 헤더가 과도하게 주고받아지는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;816&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ba8t25/btsH8yHGdXl/tXKH1nQHDAkk9VkENjaU21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ba8t25/btsH8yHGdXl/tXKH1nQHDAkk9VkENjaU21/img.png&quot; data-alt=&quot;WebSocket도 초기에는 HTTP로 HandShaking한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ba8t25/btsH8yHGdXl/tXKH1nQHDAkk9VkENjaU21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fba8t25%2FbtsH8yHGdXl%2FtXKH1nQHDAkk9VkENjaU21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;310&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;816&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;WebSocket도 초기에는 HTTP로 HandShaking한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면, &lt;b&gt;Web Socket&lt;/b&gt;은 실시간 양방향 데이터 교환을 위한 프로토콜로, HTTP의 비연결성에서 벗어나 &lt;b&gt;연결 상태를 유지함으로써&lt;/b&gt; 데이터를 교환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 패킷 분석 내용의 일부를 보면 , 요청 헤더를 통해 WebSocket으로 업그레이드하는 것을 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;[WebSocket 요청 패킷 일부 발췌]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNFFKV/btsH8GSXDrv/ZioWg30mkTqL7wxu4zr0OK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNFFKV/btsH8GSXDrv/ZioWg30mkTqL7wxu4zr0OK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNFFKV/btsH8GSXDrv/ZioWg30mkTqL7wxu4zr0OK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNFFKV%2FbtsH8GSXDrv%2FZioWg30mkTqL7wxu4zr0OK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;214&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;478&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;STOMP(Simple Text Oriented Messaging) 프로토콜&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;STOMP는 &lt;/b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;사람이 읽고 쓰기 쉬운 형태의&amp;nbsp;텍스트 기반&lt;/span&gt;&amp;nbsp;프로토콜로, 메시지를 전송하고 수신하기 위한 프로토콜이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;STOMP 메시지는 프레임 단위로 구성되며, 프레임은 헤더와 바디로 이루어져 있다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;프레임은 크게 CONNECT, SEND, SUBSCRIBE, UNSUBSCRIBE 유형으로 나뉜다.&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;동작과정은 &lt;b&gt;연결(CONNECT)- 구독(SUBSCRIBE) - 메시지 발행(SEND) - 전달(MESSAGE) - 구독 취소(UNSUBSCRIBE) - 연결 종료&lt;/b&gt;다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;WebSocket과 함께&lt;/b&gt; 사용하여 &lt;b&gt;메시지의 전송, 구독, 취소 등의 작업을 구조화된 방식으로 처리&lt;/b&gt;할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;만약 이 프로토콜을 정의 및 사용하지 않으면 &lt;u&gt;어떤 형식으로 어떤 데이터를 교환할 것인지&lt;/u&gt; 모두 구현해야 하는 번거로움이 생긴다.  &lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 프로젝트에서는&lt;span style=&quot;color: #006dd7;&quot;&gt; JavaScript의 STOMP 클라이언트&lt;/span&gt; 즉, &lt;a href=&quot;https://github.com/stomp-js/stompjs&quot;&gt;@stomp/stompjs&lt;/a&gt; 라이브러리를 사용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719075564101&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const stompClient = new StompJs.Client({
    brokerURL: 'ws://localhost:8080/endpoint'
});

stompClient.activate();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드처럼 StompJs.Client 객체를 생성하고, brokerURL로 WebSocket 서버의 주소를 생성한 뒤, STOMP 클라이언트를 활성화하면 WebSocket 서버와의 연결을 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719075641417&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function publish() {
    const destination = &quot;/pub/chat/&quot; + roomId;
    const text = document.getElementById(&quot;text&quot;).value;
    document.getElementById(&quot;text&quot;).value = &quot;&quot;;

    const message = {
        senderName: myName,
        text: text,
        createdTime: new Date()
    };

    stompClient.publish({
        destination: destination,
        body: JSON.stringify(message)
    });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 발행을 위해 &lt;span style=&quot;color: #006dd7;&quot;&gt;stompClient.publish&lt;/span&gt; 메서드를 사용하여 메시지 형식을 지정하고, JSON 형식으로 변환하여 발행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후, showChat 함수를 통해 수신된 메시지를 HTML 요쇼로 생성하여 채팅 로그에 표시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Message Broker&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebSocket 메시지 브로커를 구성하기 위해 Java Spring의 @EnableWebSocketMessageBroker 애너테이션과 WebSocket 인터페이스를 사용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719076215355&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        // 구독 경로 설정: 클라이언트로부터 서버로 메시지를 전달
        registry.enableSimpleBroker(&quot;/sub&quot;);
        // 발행 경로 설정: 서버로부터 클라이언트로 메시지를 전달
        registry.setApplicationDestinationPrefixes(&quot;/pub&quot;);
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // WebSocket 엔드포인트 설정
        registry.addEndpoint(&quot;/endpoint&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring WebSocket 설정을 통해 엔드포인트('/endpoint')를 등록하고, pub/sub 경로를 메시지 브로커가 처리하도록 한다. 클라이언트 측은 JS로 STOMP 클라이언트를 사용하여 WebSocket 연결을 설정하고, 해당 경로로 메시지를 발행 및 구독한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식으로 클라이언트와 서버는 WebSocket을 통해 연결되며, 클라이언트는 STOMP 프로토콜을 사용하여 메시지 브로커와 통신한다. &lt;b&gt;클라이언트는 메시지를 발행하여 서버로 전송하고, 서버는 메시지 브로커를 통해 해당 경로를 구독하고 있는 다른 클라이언트에게 메시지를 전달한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도가 가장 핵심적인 내용이고, 이메일 전송 구현은 해당 프로젝트의 핵심 기능이 아니기에 설명을 보태지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2편에서 구현된 내용을 WireShark로 캡처하고, 패킷을 분석한 내용에 대해 소개하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Network</category>
      <category>network</category>
      <category>Wireshark</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/284</guid>
      <comments>https://cobinding.tistory.com/284#entry284comment</comments>
      <pubDate>Sun, 23 Jun 2024 02:21:41 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] Cloud Watch Agent 설정하기 | Configuration validation second phase failed 해결</title>
      <link>https://cobinding.tistory.com/283</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진행하는 프로젝트의 로그 작업을 위해 CloudWatch를 설정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CloudWatch를 통해 로깅하기 위해서&lt;/b&gt;는 &lt;b&gt;로그를 측정할 Agent를 따로 설치해야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 문서에서는 IAM 관련 설정은 생략한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CloudWatch Agent 다운로드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AmazonCloudWatch/latest/monitoring/download-cloudwatch-agent-commandline.html&quot;&gt;해당 문서&lt;/a&gt;에서 서버 OS에 맞는 명령어를 찾아, 설치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자는 ubuntu 서버를 사용 중이므로, 다음과 같은 과정을 통해 설치하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. EC2에 agent 다운로드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1717395078718&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 패키지 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1717395090568&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo dpkg -i -E ./amazon-cloudwatch-agent.deb&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Agent config 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 경로에 있는 wizard를 통해 Agent의 config를 설정한다. (이 과정이 생각보다 길다. 당황하지 말고 설정하자.  )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Agent wizard 실행&lt;/h4&gt;
&lt;pre id=&quot;code_1717395376361&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd /opt/aws/amazon-cloudwatch-agent/bin/
sudo ./amazon-cloudwatch-agent-config-wizard&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1717395231454&quot; class=&quot;asciidoc&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;root@ip-10-0-1-29:~# sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard
================================================================
= Welcome to the Amazon CloudWatch Agent Configuration Manager =
=                                                              =
= CloudWatch Agent allows you to collect metrics and logs from =
= your host and send them to CloudWatch. Additional CloudWatch =
= charges may apply.                                           =
================================================================
On which OS are you planning to use the agent?
1. linux
2. windows
3. darwin
default choice: [1]:

Trying to fetch the default region based on ec2 metadata...
I! imds retry client will retry 1 timesAre you using EC2 or On-Premises hosts?
1. EC2
2. On-Premises
default choice: [1]:

......&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 하나하나 설정하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세부내용에 관해서는 &lt;a href=&quot;https://bigco-growth-diary.tistory.com/40&quot;&gt;BigCo 님의 블로그&lt;/a&gt;에 잘 나와있다. 필자의 경우 default 값을 위주로 설정해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Agent 실행&amp;nbsp;&lt;/h3&gt;
&lt;pre id=&quot;code_1717395828604&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;wizard를 통해 설정을 하고, Agent를 실행하면 CloudWatch Agent 구성과 시작 내용을 보여준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이 과정에서 쭉쭉 잘 되다가, &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Configuration validation second phase failed&lt;/span&gt;&lt;/b&gt; 에러가 발생했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IJqSw/btsHLW2ZBeQ/L76ACrpDHHccLbKSlQnvL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IJqSw/btsHLW2ZBeQ/L76ACrpDHHccLbKSlQnvL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IJqSw/btsHLW2ZBeQ/L76ACrpDHHccLbKSlQnvL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIJqSw%2FbtsHLW2ZBeQ%2FL76ACrpDHHccLbKSlQnvL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1708&quot; height=&quot;808&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;808&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1717399018973&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get install collectd&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 메시지 내용처럼 &lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt;/usr/share/collected/types.db&lt;/span&gt; 가 없다는 것인데, &lt;b&gt;collectd&lt;/b&gt;라는 것을 통해 해결할 수 있다.( &lt;a href=&quot;https://velog.io/@skyjoon34/AWS-CloudWatch%EB%A5%BC-%ED%86%B5%ED%95%B4-%EC%A7%80%ED%91%9C-%EB%A1%9C%EA%B7%B8-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81%ED%95%98%EA%B8%B0-%EC%84%A4%EC%A0%95&quot;&gt;algorijun 님의 블로그에서 본 해결책)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;798&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ce8o3B/btsHLsBgSar/kn8726m5n9o2Epw8T2AtQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ce8o3B/btsHLsBgSar/kn8726m5n9o2Epw8T2AtQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ce8o3B/btsHLsBgSar/kn8726m5n9o2Epw8T2AtQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fce8o3B%2FbtsHLsBgSar%2Fkn8726m5n9o2Epw8T2AtQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;465&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;798&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1886&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBDEJT/btsHMPIPVae/CGjP6V7JakgedgAc7gPVR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBDEJT/btsHMPIPVae/CGjP6V7JakgedgAc7gPVR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBDEJT/btsHMPIPVae/CGjP6V7JakgedgAc7gPVR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBDEJT%2FbtsHMPIPVae%2FCGjP6V7JakgedgAc7gPVR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1886&quot; height=&quot;680&quot; data-origin-width=&quot;1886&quot; data-origin-height=&quot;680&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히 실행이 잘 되었다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;지표 확인&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buNyWs/btsHLHdTVwq/yAkcIZzR8Ok7slgm6dFlHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buNyWs/btsHLHdTVwq/yAkcIZzR8Ok7slgm6dFlHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buNyWs/btsHLHdTVwq/yAkcIZzR8Ok7slgm6dFlHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuNyWs%2FbtsHLHdTVwq%2FyAkcIZzR8Ok7slgm6dFlHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;408&quot; height=&quot;174&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 지표가 잘 생성된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Agent 관련 명령어&lt;/h3&gt;
&lt;pre id=&quot;code_1717396225977&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ps로 동작 중인 agent 확인
ps aux | grep amazon-cloudwatch-agent

# 정지
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -m ec2 -a stop

# 시작
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -m ec2 -a start&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AWS</category>
      <category>DevOps</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/283</guid>
      <comments>https://cobinding.tistory.com/283#entry283comment</comments>
      <pubDate>Mon, 3 Jun 2024 15:30:35 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] AWS 하나의 LB로 여러 서비스 운영하기</title>
      <link>https://cobinding.tistory.com/282</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cobinding.tistory.com/281&quot;&gt;지난 포스팅&lt;/a&gt;에서 하나의 로드밸런서로 여러 서브도메인, 서비스를 운영하는 것을 GCP를 통해 구축해보았다. (GCP를 사용하지 않더라도 앞의 글을 읽는 것을 추천한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1716292928186&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[DevOps] 하나의 LB로 여러 서비스 운영하기&quot; data-og-description=&quot;개요로드 밸런서는 운영관리 입장에서 상당히 많은 편의성을 제공한다.그래서 그런지 규모가 작은 프로젝트 즉, &amp;quot;부하 분산&amp;quot;이 필요없는 프로젝트임에도 불구하고 정말 많은 사람들이 로드 밸&quot; data-og-host=&quot;cobinding.tistory.com&quot; data-og-source-url=&quot;https://cobinding.tistory.com/281&quot; data-og-url=&quot;https://cobinding.tistory.com/281&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cUDAl7/hyV56jv6bs/IH1qMdNxjukFYOdhd8kkDK/img.jpg?width=600&amp;amp;height=442&amp;amp;face=0_0_600_442,https://scrap.kakaocdn.net/dn/ByaFM/hyV6cqrl0M/6F8QtMAQXfxEiTlFn6lDRK/img.jpg?width=600&amp;amp;height=442&amp;amp;face=0_0_600_442,https://scrap.kakaocdn.net/dn/81DkN/hyV9O2xj5s/rJyeF8tp932oWnj5wlIwp0/img.png?width=1052&amp;amp;height=431&amp;amp;face=0_0_1052_431&quot;&gt;&lt;a href=&quot;https://cobinding.tistory.com/281&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cobinding.tistory.com/281&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cUDAl7/hyV56jv6bs/IH1qMdNxjukFYOdhd8kkDK/img.jpg?width=600&amp;amp;height=442&amp;amp;face=0_0_600_442,https://scrap.kakaocdn.net/dn/ByaFM/hyV6cqrl0M/6F8QtMAQXfxEiTlFn6lDRK/img.jpg?width=600&amp;amp;height=442&amp;amp;face=0_0_600_442,https://scrap.kakaocdn.net/dn/81DkN/hyV9O2xj5s/rJyeF8tp932oWnj5wlIwp0/img.png?width=1052&amp;amp;height=431&amp;amp;face=0_0_1052_431');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[DevOps] 하나의 LB로 여러 서비스 운영하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;개요로드 밸런서는 운영관리 입장에서 상당히 많은 편의성을 제공한다.그래서 그런지 규모가 작은 프로젝트 즉, &quot;부하 분산&quot;이 필요없는 프로젝트임에도 불구하고 정말 많은 사람들이 로드 밸&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cobinding.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 AWS의 로드밸런서와 라우팅 설정을 통해서 하나의 LB로 서브도메인을 구분하는 것을 세팅해보도록 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS로 구축할 웹사이트는 user와 admin이 구별되어있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt; user-main.example.com&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt; admin-main.example.com&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 가지 서브도메인을 하나의 LB에 설정해보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ALB 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Application 로드밸런서를 만들어준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 전달하고자 하는 대상그룹으로 리스너를 설정해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1583&quot; data-origin-height=&quot;357&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9Yzqp/btsHxh6QhrT/FnH22JKrHfKUErkJ0jkvc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9Yzqp/btsHxh6QhrT/FnH22JKrHfKUErkJ0jkvc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9Yzqp/btsHxh6QhrT/FnH22JKrHfKUErkJ0jkvc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9Yzqp%2FbtsHxh6QhrT%2FFnH22JKrHfKUErkJ0jkvc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1583&quot; height=&quot;357&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1583&quot; data-origin-height=&quot;357&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 가지 서브도메인 user, admin 중 admin을 기본 서버로 설정하여, 처음 LB를 만들면 admin에 대한 작업과 규칙이 정의되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;리스너와 대상규칙&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;리스너(Listner)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트로부터 트래픽을 수신하기 위해 설정하는 프로토콜 및 포트다. Application LB을 통해 웹 서비스를 운영하기 때문에 HTTP와 80 포트를 기본적으로 설정한다. 추가적으로 HTTPS 요청을 받기 위해 HTTPS와 443 포트를 설정해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 AWS의 ALB에서는 다음과 같은 프토토콜, 포트를 지원한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로토콜: HTTP, HTTPS&lt;/li&gt;
&lt;li&gt;포트: 1-65535&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;대상규칙(Target Group)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대상규칙은 리스너를 통해 들어온 트래픽을 EC2 인스턴스와 같은 대상으로 라우팅하는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;➡️ 이 두 가지 개념을 활용해서 하나의 LB로 여러 서브 도메인을 운영할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리스너 규칙에 서브 도메인 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 이미지에서 보이는&lt;span style=&quot;color: #0593d3;&quot;&gt; &lt;b&gt;HTTP:80&lt;/b&gt;&lt;/span&gt; 리스너에 규칙을 추가해준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스너 규칙은 다양한 조건을 추가할 수 있는데, 서브 도메인을 활용하여 트래픽을 분산할 것이므로 &lt;b&gt;호스트 헤더&lt;/b&gt;를 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, 추가하는 규칙은 user에 대한 것이다! 앞서 admin을 default로 가져가기로 정의했기 때문이다. 만약 여러 서브도메인을 설정하고자 한다면 default 서브 도메인을 하나 정하고 이 과정을 각각의 서브 도메인에 반복 설정하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hax6f/btsHwPpnMd8/xdQuKTVJ4ED9s8iJ37KfJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hax6f/btsHwPpnMd8/xdQuKTVJ4ED9s8iJ37KfJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hax6f/btsHwPpnMd8/xdQuKTVJ4ED9s8iJ37KfJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHax6f%2FbtsHwPpnMd8%2FxdQuKTVJ4ED9s8iJ37KfJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;372&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;806&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IKdYH/btsHys7mrBR/MsPiAZeH90AWOA67h5fkEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IKdYH/btsHys7mrBR/MsPiAZeH90AWOA67h5fkEk/img.png&quot; data-alt=&quot;추가할 라우팅 조건인 user 서브도메인 작성하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IKdYH/btsHys7mrBR/MsPiAZeH90AWOA67h5fkEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIKdYH%2FbtsHys7mrBR%2FMsPiAZeH90AWOA67h5fkEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;377&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;추가할 라우팅 조건인 user 서브도메인 작성하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;1232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LEfHs/btsHxQ8HoUI/iEKdvMpzA8DdFCqs4idjk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LEfHs/btsHxQ8HoUI/iEKdvMpzA8DdFCqs4idjk0/img.png&quot; data-alt=&quot;규칙에 대한 작업 정의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LEfHs/btsHxQ8HoUI/iEKdvMpzA8DdFCqs4idjk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLEfHs%2FbtsHxQ8HoUI%2FiEKdvMpzA8DdFCqs4idjk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;567&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;1232&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;규칙에 대한 작업 정의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 마지막으로 위에서 추가한 규칙에 어떤 작업을 부여할 것인지 정의한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;2256&quot; data-origin-height=&quot;554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IRtM4/btsHv99JLlF/31EyNjcsoHPkJlIp416WWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IRtM4/btsHv99JLlF/31EyNjcsoHPkJlIp416WWk/img.png&quot; data-alt=&quot;추가된 화면 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IRtM4/btsHv99JLlF/31EyNjcsoHPkJlIp416WWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIRtM4%2FbtsHv99JLlF%2F31EyNjcsoHPkJlIp416WWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2256&quot; height=&quot;554&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;2256&quot; data-origin-height=&quot;554&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;추가된 화면 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 리스너 규칙에 기본값으로는 admin, 추가된 조건으로는 HTTP 호스트 헤더가 &lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt; user-main.example.com &lt;/span&gt;인 대상을 user-tg가 있음을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;➡️ 리스너에 HTTP:80 뿐만 아니라 HTTPS:443을 만들어주었기에 HTTPS도 이 과정을 똑같이 진행해준다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Route 53 서브 도메인 설정 및 로드 밸런서 연결하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 설정된 로드 밸런서를 각각의 도메인과 연결해준다. 이는 AWS의 Route53을 통해 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정은 가비아에서 도메인을 구매와 기본 설정이 되어있는 상태에서 진행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2018&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QNniB/btsHywIGVuy/JHDXb1KvWD5YqgjjJb9Vp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QNniB/btsHywIGVuy/JHDXb1KvWD5YqgjjJb9Vp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QNniB/btsHywIGVuy/JHDXb1KvWD5YqgjjJb9Vp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQNniB%2FbtsHywIGVuy%2FJHDXb1KvWD5YqgjjJb9Vp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2018&quot; height=&quot;152&quot; data-origin-width=&quot;2018&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 로드밸런서와 서브도메인의 연결은 레코드 생성의 별칭 - 대상으로 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2044&quot; data-origin-height=&quot;1258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PZiof/btsHwwDFrS1/8Ww5OZolqfUTPPwjJ2FrsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PZiof/btsHwwDFrS1/8Ww5OZolqfUTPPwjJ2FrsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PZiof/btsHwwDFrS1/8Ww5OZolqfUTPPwjJ2FrsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPZiof%2FbtsHwwDFrS1%2F8Ww5OZolqfUTPPwjJ2FrsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2044&quot; height=&quot;1258&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2044&quot; data-origin-height=&quot;1258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결과 확인&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;626&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvIeal/btsHwb7zyjz/WmgPFTtMMMpEO3kR8Rs9fK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvIeal/btsHwb7zyjz/WmgPFTtMMMpEO3kR8Rs9fK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvIeal/btsHwb7zyjz/WmgPFTtMMMpEO3kR8Rs9fK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvIeal%2FbtsHwb7zyjz%2FWmgPFTtMMMpEO3kR8Rs9fK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1672&quot; height=&quot;626&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;626&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 서브도메인으로 각각의 서비스에 접근해보면 하나의 LB를 통해 접근이 가능한 것을 확인할 수 있다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;route53에 정의한 후 시간이 걸리니 잘 안되는 경우엔 좀 더 기다려보자..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 세운 가설을 증명하고, 이를 프로젝트에 활용한 좋은 경험이었다. 또한 AWS, GCP를 모두 비교하면서 사용할 수 있어서 좋았다.  &lt;/p&gt;</description>
      <category>DevOps/AWS</category>
      <category>AWS</category>
      <category>DevOps</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/282</guid>
      <comments>https://cobinding.tistory.com/282#entry282comment</comments>
      <pubDate>Wed, 22 May 2024 11:08:15 +0900</pubDate>
    </item>
    <item>
      <title>[GCP] GCP 하나의 LB로 여러 서비스 운영하기</title>
      <link>https://cobinding.tistory.com/281</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드 밸런서는 운영관리 입장에서 상당히 많은 편의성을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 그런지 규모가 작은 프로젝트 즉, &quot;부하 분산&quot;이 필요없는 프로젝트임에도 불구하고 정말 많은 사람들이 로드 밸런서를 활용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 나의 경우, 작은 프로젝트에 LB를 쓰는게 &lt;b&gt;그 기술의&amp;nbsp;취지에 맞지 않고, 그렇기에 많은 자원이 낭비가 된다&lt;/b&gt;는 생각에 쓰기가 싫었다. (뭔가 괜히 패배하는 기분이랄까..?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서부터 도르마무 시작이었다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;EfcLDDAkyqgqz3gKLDPEsiJ7o6YeeNFXzstProc9RBLPWQ2GrEi1tK9WRcnaBxYeiPSvq6LJkTxzEYsNav3wdgx2sCTY2.jpeg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGTTrA/btsHntzuAJj/48AT688D0ZpOkmiNYbHW2k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGTTrA/btsHntzuAJj/48AT688D0ZpOkmiNYbHW2k/img.jpg&quot; data-alt=&quot;이게 그 당시의 나를 설명하는 짤....&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGTTrA/btsHntzuAJj/48AT688D0ZpOkmiNYbHW2k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGTTrA%2FbtsHntzuAJj%2F48AT688D0ZpOkmiNYbHW2k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;332&quot; data-filename=&quot;EfcLDDAkyqgqz3gKLDPEsiJ7o6YeeNFXzstProc9RBLPWQ2GrEi1tK9WRcnaBxYeiPSvq6LJkTxzEYsNav3wdgx2sCTY2.jpeg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이게 그 당시의 나를 설명하는 짤....&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;하나의 서버에 여러 포트를 두고, 이 각각의 포트에&lt;/b&gt; SSL을 적용하는 테스크를 진행했는데, 사실 이건 로드 밸런서로 뚝딱뚝딱 1-2시간이면 할 수 있는 업무다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;그런데 위와 같은 이유로 로드밸런서를 사용하기 싫었고, 일일이 포트별 SSL 설치라는 수작업을 하기로 했다.&lt;/span&gt; ➡️ 심지어 이 수작업을 하는 기술들을 한국에서도 많이 사용하고 있었고(certbot) 의심없이 시작을 했지만, &lt;u&gt;결국 해내지 못했다!&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 패배를 인정하고 로드밸런서를 각각 서버마다 만들어서 쓰게되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 서버를 운영하던 중... 운 좋게 회사에서 비슷한 솔루션(?)과 같은 업무가 주어져서 이 삽질에 대해 다시 생각해볼 기회가 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;솔루션은 로드밸런서 하나로 여러 서버를 운영해보는 것이었다.&lt;/b&gt; 처음엔 이 아키텍처가 위와 같은 이유로 이해가 안됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;로드 밸런서는 부하를 분산하는 것인데, 다른 여러 서비스(멀티 모듈)로 분산을 설정 해버리면 어떢함???&lt;/b&gt; 기술에 맞지 않아! 이런 생각이 나를 지배했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이 아키텍처 설계의 의도를 생각해보면, 그냥 있는 기술을 나의 상황에 맞게 잘 설계한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시중에 나온 기술을 어떻게 응용할 수 있냐에 따라 구축할 수 있는 건 무한했고, 기술을 잘 사용하기 위해서는 &lt;b&gt;유연성과 응용력이 정말 필요한 소양&lt;/b&gt;이라는 깨달음을 얻었다. 정석대로 하는 것이 반드시 좋은 결과로 도출되는 건 아니다! 나의 꼼꼼함에 유연성을 첨가하자...&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 서브 도메인을 하나의 로드 밸런서 라우팅을 통해 이용한다. (로드밸런서가&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;주는&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;설정&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;편의가&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;크니까)&lt;/li&gt;
&lt;li&gt;주의할 점
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;트래픽이 급격하게 많이 몰리면 당연히 이런 구조는 당연히 장애가 발생한다. (부하 분산을 하나의 서비스에 대해 하는 것이 아니니까) 따라서 &lt;b&gt;테스트 서버 등과 같이 비용 절감이 필요하되, 로드 밸런서의 편의성이 필요한 곳에만 한정하여 사용한다.&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크게 구조는 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G0pkQ/btsHl84iGMs/JJZSK9KDxUqtgkueIGeEN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G0pkQ/btsHl84iGMs/JJZSK9KDxUqtgkueIGeEN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G0pkQ/btsHl84iGMs/JJZSK9KDxUqtgkueIGeEN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG0pkQ%2FbtsHl84iGMs%2FJJZSK9KDxUqtgkueIGeEN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;391&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용할 로드밸런서 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 아키텍처처럼, default 서버를 제외하고는 라우팅을 해서 사용할 거기에 로드 밸런서 하나를 설정해준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;서브 도메인 지정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하는 도메인 서비스에 A 레코드로 각각의 테스트 서버 A, B, C 서브 도메인을 생성한다. 이때, 각각에 로드밸런서 외부 IP를 등록해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;955&quot; data-origin-height=&quot;111&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brqUha/btsHmBk4aSM/cDPffQJ7zarPKqvRB615sK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brqUha/btsHmBk4aSM/cDPffQJ7zarPKqvRB615sK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brqUha/btsHmBk4aSM/cDPffQJ7zarPKqvRB615sK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrqUha%2FbtsHmBk4aSM%2FcDPffQJ7zarPKqvRB615sK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;955&quot; height=&quot;111&quot; data-origin-width=&quot;955&quot; data-origin-height=&quot;111&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로드밸런서 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자는 GCP 서비스로 이 테스크를 진행했다. (사이드 프로젝트는 AWS로 사용하고 있어서 주요 API 개발이 끝나면 AWS 클라우드에서도 POC를 해볼 예정이다. POC가 완성되면 과정을 추가하겠다.  )&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;911&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G5jIb/btsHn4MNUpk/8JhcOw0KSPJYfNo5IW52Rk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G5jIb/btsHn4MNUpk/8JhcOw0KSPJYfNo5IW52Rk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G5jIb/btsHn4MNUpk/8JhcOw0KSPJYfNo5IW52Rk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG5jIb%2FbtsHn4MNUpk%2F8JhcOw0KSPJYfNo5IW52Rk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;570&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;911&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 것들을 설정해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ssl 인증서를 로드밸런서 없이 여러 서버에 한번에 적용해보면, 로드밸런서가 정말 편한 서비스임을 알 수 있다......&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;431&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9Wuex/btsHngmJhxb/UmHh9xOK5dFp36BKsith5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9Wuex/btsHngmJhxb/UmHh9xOK5dFp36BKsith5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9Wuex/btsHngmJhxb/UmHh9xOK5dFp36BKsith5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9Wuex%2FbtsHngmJhxb%2FUmHh9xOK5dFp36BKsith5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1052&quot; height=&quot;431&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;431&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 백엔드 버킷은 총 3개이므로, 3 개를 모두 추가한다. 이후 라우팅 규칙에서 이를 구분한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y0gU8/btsHnqQknVB/Ri7y0c7KaeZc8RxCoPeGn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y0gU8/btsHnqQknVB/Ri7y0c7KaeZc8RxCoPeGn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y0gU8/btsHnqQknVB/Ri7y0c7KaeZc8RxCoPeGn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy0gU8%2FbtsHnqQknVB%2FRi7y0c7KaeZc8RxCoPeGn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1282&quot; height=&quot;549&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;549&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 &lt;b&gt;호스트 및 경로 규칙 추가&lt;/b&gt;를 누르고, 라우팅할 각각의 서브 도메인과 서버를 연결해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결과 확인&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;902&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqGII5/btsHnxaIvwd/jm06DjKiM8icSsD0DjLzH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqGII5/btsHnxaIvwd/jm06DjKiM8icSsD0DjLzH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqGII5/btsHnxaIvwd/jm06DjKiM8icSsD0DjLzH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqGII5%2FbtsHnxaIvwd%2Fjm06DjKiM8icSsD0DjLzH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;462&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;902&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 서브 도메인으로 접속이 잘 되는 것을 확인하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 어떤 가설을 세워서 검증하는 과정 자체가 재밌었고, 깨달음이 많은 작업이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/GCP</category>
      <category>DevOps</category>
      <category>GCP</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/281</guid>
      <comments>https://cobinding.tistory.com/281#entry281comment</comments>
      <pubDate>Mon, 13 May 2024 11:53:08 +0900</pubDate>
    </item>
    <item>
      <title>[BackEnd] API의 멱등성을 고려하여 개발하기</title>
      <link>https://cobinding.tistory.com/279</link>
      <description>&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신규로 도입한 어드민 기능에 문제가 발생했다. 아래 두 가지가 부족했기 때문이었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;API의 멱등성&lt;/li&gt;
&lt;li&gt;사용자 관점에서의 테스트&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 포스팅에서는 API의 멱등성에 대해 상세히 다루고, 앞으로 의식하여 적용할 수 있도록 해볼 것이다!&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;멱등성(Idempotency)&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;멱등성은 수학 용어로, &lt;u&gt;여러 번 연산을 적용해도 결과는 달라지지 않는 성질&lt;/u&gt;을 말한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;한자도 &lt;b&gt;덮을 멱, 무리 등&lt;/b&gt;인데&lt;b&gt; 무리(swarm)의 효과를 덮어버린다!&lt;/b&gt;&amp;nbsp;라는 뜻이다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;멱등성의 대표적 예다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;절댓값 함수&lt;/li&gt;
&lt;li&gt;1을 계속 곱하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;HTTP의 멱등성&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;기본적으로 HTTP 메소드의 &lt;b&gt;&lt;span style=&quot;color: #eb5757;&quot;&gt;GET, PUT, DELETE &lt;/span&gt;와 같은 메서드는 멱등&lt;/b&gt;하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어, 여러 번 호출해도 GET 은 항상 같은 결과를 보여준다.&lt;/li&gt;
&lt;li&gt;PUT 또한 여러 번의 요청에도, 매번 같은 리소스로 업데이트 한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;반면, 서버의 데이터를 변경하는 &lt;b&gt;&lt;span style=&quot;color: #eb5757;&quot;&gt;POST, PATCH &lt;/span&gt;와 같은 메서드는&lt;/b&gt; &lt;span style=&quot;color: #000000;&quot;&gt;호출을 할 때 마다 요청 데이터, 응답이 달라지기 때문에&lt;b&gt; 멱등하지 않다. &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 HTTP Method의 API는 멱등성 보장을 위한 추가적 구현이 요구된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;box-sizing: border-box; font-size: 15px; *** rgb(254, 137, 67): 관련글의 테두리색 입니다. ***font-family: Arial, 돋움, Dotum, AppleGothic, sans-serif; padding: 10px; margin: 0px; line-height: 1.5; background-color: #ffffff; border: 2px solid #4f84c4;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 경우&lt;b&gt; POST&lt;/b&gt; API였고, &lt;b&gt;멱등성을 보장하는 로직이 없어서 요청 시마다 값이 달라졌다. &lt;/b&gt;결과적으로요구사항에 정확하게 맞지 않는 기능이 개발되었다.&lt;/p&gt;
&lt;/div&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;API 멱등성 보장 방법&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;1) 멱등키 헤더&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;멱등키를 API 요청 헤더에 포함&lt;/b&gt;한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;서버는 이 멱등키를 통해 같은 키가 2번 이상 요청되면 중복으로 판단한 뒤, 실제로 처리하지 않는 방식으로 동작한다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;IETF에서 표준으로 제안하는 요청 헤더의 멱등키&lt;/span&gt;&lt;/a&gt;&amp;nbsp;는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;Idempotency-Key: {IDEMPOTENCY_KEY}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;a href=&quot;https://docs.tosspayments.com/blog/what-is-idempotency&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;background-color: #e6f5ff;&quot;&gt;&lt;span style=&quot;color: #0070d1;&quot;&gt;결제 취소 API의 멱등성을 구현한 토스 페이먼츠 예시&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;a href=&quot;https://docs.tosspayments.com/blog/what-is-idempotency&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;background-color: #e6f5ff;&quot;&gt;&lt;span style=&quot;color: #0070d1;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;에서는 결제 취소와 같은 API에 이 멱등성을 제공한다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4659&quot; data-origin-height=&quot;2367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNi51Z/btsGX0kpPXq/lYlYfrhfxoDWmdxCockyEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNi51Z/btsGX0kpPXq/lYlYfrhfxoDWmdxCockyEK/img.png&quot; data-alt=&quot;toss payments에서 결제 서비스에 적용한 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNi51Z/btsGX0kpPXq/lYlYfrhfxoDWmdxCockyEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNi51Z%2FbtsGX0kpPXq%2FlYlYfrhfxoDWmdxCockyEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;381&quot; data-origin-width=&quot;4659&quot; data-origin-height=&quot;2367&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;toss payments에서 결제 서비스에 적용한 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;결제취소는 POST 요청으로 진행되며, 취소 요청마다 헤더에 멱등키를 담아준다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;API 서버는 취소 요청마다 헤더에 멱등키가 있는지 확인한다. (이 확인을 통해 키의 중복을 판별하고, 멱등함을 구현하기 위해서다.)&lt;/li&gt;
&lt;li&gt;멱등키 저장을 위한 DB가 있다. 만약 요청1의 헤더에서 DB에 저장된 멱등키가 발견된다면, 이에 대한 멱등성 보장을 위해 실제 요청을 진행하지 않고 저장된 응답 데이터를 돌려준다. 없으면 새로운 요청이니까 그대로 로직을 수행한다!&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const idempotencyResponses = new Map();

let cancelReq = {
&amp;nbsp;&amp;nbsp;orderId: req.body.orderId
&amp;nbsp;&amp;nbsp;amount: req.body.amount,
};

let idempotencyKey = req.headers.idempotencyKey || null // 요청 헤더에서 멱등키를 가져옵니다.

// 멱등키가 있고 멱등 응답도 저장되어 있다면 실제 처리하지 않고 저장된 응답을 내보냅니다.
if (idempotencyKey != null &amp;amp;&amp;amp; idempotencyResponses.has(idempotencyKey)) {
&amp;nbsp;&amp;nbsp;const response = idempotencyResponses.get(idempotencyKey);
&amp;nbsp;&amp;nbsp;return res.status(response.status).json(response);
};

const result = cancelProcessor.cancel(cancelReq); // 실제로 취소를 처리합니다.

// 멱등키가 있으면 멱등응답을 저장합니다.
if (idempotencyKey != null) {
&amp;nbsp;&amp;nbsp;idempotencyResponses.set(idempotencyKey, result);
}

const responseBody = {
&amp;nbsp;&amp;nbsp;message: `결제 취소 성공`,
};

return res.status(200).json(responseBody);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;서버에서는 위와 같이 요청에 대해서 멱등키를 가져오고, DB에서 확인한 다음, 케이스 분기를 통해 응답을 한다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;2) Entity Tag&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag&quot; target=&quot;_self&quot;&gt;&lt;span&gt;MDN&lt;/span&gt;&lt;/a&gt;에서 본 방법인데, 이도 키 헤더를 사용하는 방식과 거의 비슷하다. 특정 URL의 리소스가 변경되면 새로운 Etag를 생성하고, 이들의 비교를 통해 자원이 동일한지 여부를 확인한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;896&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0UG81/btsPCKUP5RF/EDBl7X9Lnd0M4dokIPLx20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0UG81/btsPCKUP5RF/EDBl7X9Lnd0M4dokIPLx20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0UG81/btsPCKUP5RF/EDBl7X9Lnd0M4dokIPLx20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0UG81%2FbtsPCKUP5RF%2FEDBl7X9Lnd0M4dokIPLx20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;452&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;896&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;요청&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;GET /users/123&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;응답&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;HTTP/1.1 200 OK
Etag: &quot;33a64df551425fcc55e4d42a148795d9f25f89d4&quot;

{
&quot;name&quot;: &quot;cobinding&quot;,
&quot;email&quot;: &quot;cobinding@abc.com&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;If-Match를 통한 Etag 확인&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;POST /users/123
If-Match: &quot;33a64df551425fcc55e4d42a148795d9f25f89d4&quot;

{
&quot;name&quot;: &quot;cobinding123&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;간단하게 구현해보기&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Entity 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;java&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class Resource {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private Long id;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private String title;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private String content;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private String etag;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public String getEtag() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return DigestUtils.md5Hex(title + content + lastModified.getTime());
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;DigestUtils: &lt;/b&gt;Apache Commons Codec 라이브러리에서 제공하는 MD5 해시 생성 유틸리티 메서드
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;문자열을 MD5 알고리즘으로 해싱해서 32자리 헥사 문자열로 변환한다.&lt;/li&gt;
&lt;li&gt;보안성을 더 높이고 싶다면 SHA-256같은 해시를 사용&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;POST 수정 요청이 왔을 때 If-Match 헤더와 Etag 비교&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;@PostMapping(&quot;/resource&quot;)
public ResponseEntity&amp;lt;Resource&amp;gt; updateResource(@RequestBody Resource resource,@RequestHeader(value = &quot;If-Match&quot;, required = false) String ifMatch,
HttpServletRequest request) {
&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp; String currentEtag = &quot;\&quot;&quot; + resource.getEtag() + &quot;\&quot;&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (ifMatch != null &amp;amp;&amp;amp; !ifMatch.equals(currentEtag)) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return ResponseEntity.status(HttpStatus.PRECONDITION_FAILED).build();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Resource updatedResource = resourceService.updateResource(resource);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String newEtag = &quot;\&quot;&quot; + updatedResource.getEtag() + &quot;\&quot;&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return ResponseEntity.ok()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .eTag(newEtag)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .body(updatedResource);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;간단 회고&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;업무를 수행하며 새로운 개념에 대해 접할 수 있어서 재밌었다. 네트워크를 수강하고 HTTP를 따로 공부도 했지만, 직접적으로 개발하면서 배우는 지식은 훨씬 깊이있고 그 깨달음의 깊이도 다르다고 느껴진다. 앞으로도 개발 프로젝트에 도전하면서 더 깊이있는 학습과 그의 적용을 위해 노력해야겠다.&lt;/p&gt;</description>
      <category>Dev/Backend</category>
      <category>Back-end</category>
      <category>dev</category>
      <category>spring</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/279</guid>
      <comments>https://cobinding.tistory.com/279#entry279comment</comments>
      <pubDate>Fri, 26 Apr 2024 15:40:09 +0900</pubDate>
    </item>
    <item>
      <title>[Network] Transport Layer, Mux &amp;amp; Demux</title>
      <link>https://cobinding.tistory.com/278</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TCP Keywords&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;reliable data transfer&lt;/li&gt;
&lt;li&gt;flow control&lt;/li&gt;
&lt;li&gt;congestion control&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Transport Layer가 하는 일&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서로 다른 host의 프로세스 간 Logical communication 제공&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;프로토콜은 End System 즉, host에서 돌아간다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Network 계층의 라우터들을 거쳐서 데이터들이 전달되지만, Transport 계층은 이 거쳐가는 과정은 보이지 않고 두 &lt;b&gt;엔드 시스템(프로세스)&lt;/b&gt; 사이의 Logical Communication에만 집중한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Sender&lt;/b&gt;: App Layer의 메시지를 segment 단위로 메시지를 잘라서, Transport Layer의 헤더를 붙이고 Network Layer로 전송&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Receiver&lt;/b&gt;: 받은 segment를 합쳐서 메세지를 만들고 App Layer로 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; App &amp;harr; Transport가 메시지를 주고 받을 때 자르고/붙이고 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; Transport Layer의 핵심은 &lt;b&gt;Multiplexing &amp;amp; Demultiplexing&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Transport Layer vs. Network Layer&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Network: host 간 logical 통신(우편 배달)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transport: process 간 logical communication(배달이 된 우편들을 정확히 배분 ex. 12명 &amp;harr; 12명 각각의 이름 매칭)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Transport - ① reliable ② unreliable&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;①&amp;nbsp; reliable &amp;amp; in-order TCP&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;보낸 그대로&lt;/b&gt;, &lt;b&gt;순서대로&lt;/b&gt; 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside&gt;  전송 속도를 조절하는 방법 2가지가 있다.&lt;/aside&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;congestion control&lt;/li&gt;
&lt;li&gt;flow control&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  커넥션을 맺어야 통신이 가능하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;connection setup&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;② unreliable &amp;amp; unordered&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;중간에 날아갈 수 있고, 순서 상관없이 도달 &amp;rArr; 그저 &amp;ldquo;전달만&amp;rdquo;!!&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;TCP, UDP가 모두 해줄 수 없는 것은&lt;/span&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;언제까지 도달하도록 보장할 순 없다.&lt;/li&gt;
&lt;li&gt;속도 유지를 보장할 수 없다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어, 스트리밍 중에 계속 몇 bps로 속도를 유지할 것인지는 보장 X&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Multiplex &amp;amp; Demultiplex&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티플렉스: 프로세스로부터 받은 패킷에 포트 번호를 넣어 헤더를 만들어서, 하나의 통로로 하위 계층에 보냄.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;★ 디멀티: 그 반대, Transport 계층으로 오면 &lt;b&gt;전송된 세그먼트의 헤더 정보&lt;/b&gt;를 확인해서 적절한 프로세스의 문(목적지)에 도달하게끔 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;1276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7MAai/btsGUWVWnum/4spZRkoaCcpBcoDABlgEX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7MAai/btsGUWVWnum/4spZRkoaCcpBcoDABlgEX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7MAai/btsGUWVWnum/4spZRkoaCcpBcoDABlgEX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7MAai%2FbtsGUWVWnum%2F4spZRkoaCcpBcoDABlgEX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;560&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;1276&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TCP와 UDP의 Demux&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디멀티플렉스는 받은 소켓의 헤더 정보를 확인해서 적절한 프로세스와 연결해주는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; &lt;span style=&quot;color: #ef5369;&quot;&gt;TCP와 UDP는 Demux하는 방식이 다르다.&lt;/span&gt; 각각 Connection-oriented, Connectionless&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;① Connectionless Demux&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Datagram 소켓을 생성&lt;/li&gt;
&lt;li&gt;desincation IP, Port로 전송&lt;/li&gt;
&lt;li&gt;받는 호스트 측에서 UDP Segment를 확인해서, Destination Port가 같다면 OK&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 즉 (src_ip, src_p, dst_ip, &lt;b&gt;dst_pt&lt;/b&gt;) 중&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt; dst_pt만 확인해서 같은 소켓으로 보낸다.&lt;/span&gt;&lt;/b&gt; 보내는 쪽의 주소와 포트 넘버가 달라도 Desination 정보가 같으면 같은 곳으로 보내짐!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;② Connection-Oriented Demux&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(src_ip, src_p, dst_ip, dst_pt) &amp;rarr;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt; 이 네 가지 값을 하나의 튜플로 묶어, 정보로 활용한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP에서는이 네가지 정보가 담긴 튜플을 활용해서 segment를 적절한 소켓으로 전달한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 서버는 TCP를 사용하는데, 클라이언트와 커넥션을 맺을 때마다 다른 소켓을 사용한다.&amp;rarr; non-persistent HTTP는 요청마다 새 커넥션을 하기 때문에 모두 다른 소켓을 사용한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;reliable data transfer&lt;/li&gt;
&lt;li&gt;flow control&lt;/li&gt;
&lt;li&gt;congestion control&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>CS/Network</category>
      <category>CS</category>
      <category>network</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/278</guid>
      <comments>https://cobinding.tistory.com/278#entry278comment</comments>
      <pubDate>Wed, 24 Apr 2024 15:04:28 +0900</pubDate>
    </item>
    <item>
      <title>[네트워크] DNS, DNS 쿼리, DNS recode 종류</title>
      <link>https://cobinding.tistory.com/277</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스가 서로 통신할 때 알아야 하는 두 가지 정보 &amp;rarr; IP, Port&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IP: 프로세스가 돌고 있는 머신의 Network Interface Card에 부여된 Identity &amp;rarr; IPv4, host name(domain name)&lt;/li&gt;
&lt;li&gt;Port: 실행 중인 프로세스에 접근할 수 있는&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DNS&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 주소를 hostname으로 매핑(라우팅)해주는 역할&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; Network Layer에서 해야하는 일을 App-Layer에서 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; &lt;b&gt;Network Layer는 빠르게 전송만 하면 돼서, 복잡한 연산을 App-Layer에서 처리하는 것&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;distributed database &amp;rarr; not centralize
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;가용성, 중요한 서비스는 분산시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;application layer protocol&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1640&quot; data-origin-height=&quot;1218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4F5Ak/btsGS3Ph69I/YQzB0x27EFy50k4vOkTYmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4F5Ak/btsGS3Ph69I/YQzB0x27EFy50k4vOkTYmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4F5Ak/btsGS3Ph69I/YQzB0x27EFy50k4vOkTYmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4F5Ak%2FbtsGS3Ph69I%2FYQzB0x27EFy50k4vOkTYmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;483&quot; data-origin-width=&quot;1640&quot; data-origin-height=&quot;1218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DNS가 하는 일&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;host name-IP address&lt;/li&gt;
&lt;li&gt;host aliasing - canonical &amp;amp; alias names&lt;/li&gt;
&lt;li&gt;main server aliasing&lt;/li&gt;
&lt;li&gt;load distribution&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DNS 쿼리 과정(&lt;a href=&quot;http://www.google.com&quot;&gt;www.google.com&lt;/a&gt;)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 host에 대한 IP 주소를 요청을 할 때,&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;INTERATED QUERY&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Local DNS Server에 요청&lt;/li&gt;
&lt;li&gt;root DNS Server에 요청 &amp;rarr; (모르는 경우) TLD에 물어보라고 TLD 주소 알려줌&lt;/li&gt;
&lt;li&gt;TLD DNS Server에 요청 &amp;rarr; authorititative Server에 물어보라고 A Server 주소 알려줌&lt;/li&gt;
&lt;li&gt;authorititative Server는 최종 권한을 가지고 있기에, 알려줌!&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;451&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GeUIw/btsG5BRZNvd/ZV9ocNI7ZvobMHvu7oTnc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GeUIw/btsG5BRZNvd/ZV9ocNI7ZvobMHvu7oTnc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GeUIw/btsG5BRZNvd/ZV9ocNI7ZvobMHvu7oTnc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGeUIw%2FbtsG5BRZNvd%2FZV9ocNI7ZvobMHvu7oTnc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;451&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;451&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;RECURSIVE QUERY&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Local DNS Server에 요청하고, 답이 올 때까지 기다린다.&lt;span style=&quot;background-color: #f6e199;&quot;&gt; Server 들이 직접 나서서 응답-요청 과정 겪음&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;453&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bp3PZm/btsG5CXH2gi/LC6q3dZ6lyHnr5GAU1bMv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bp3PZm/btsG5CXH2gi/LC6q3dZ6lyHnr5GAU1bMv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bp3PZm/btsG5CXH2gi/LC6q3dZ6lyHnr5GAU1bMv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbp3PZm%2FbtsG5CXH2gi%2FLC6q3dZ6lyHnr5GAU1bMv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;453&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;453&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;실제로는 root DNS Server는 recursive 방식을 거부한다.&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DNS Type 4가지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서대로 권한 범위가 적다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) Local DNS Server(Default DNS Server)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Local DNS 서버는 hierarchy 구조에 포함되지 않으며, 클라이언트 가까이에 있는 캐시 서버다.&lt;/li&gt;
&lt;li&gt;DNS 쿼리를 날릴 때 가장 먼저 요청되는 default name server&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) root DNS&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전 세계 13개 기관에서 루트 서버 관리. IP 주소 찾기 위해서 항상 이 root에게 먼저 요청.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;root server가 local name server에게 return하는 방식으로 매핑&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;root부터 트리구조로 되어있으며 바로 아래 단계를 1단계 도메인이라고 한다. **최상위 도메인 TLD(Top Level Domain)**이다.&lt;/li&gt;
&lt;li&gt;최상위 도메인은 국가최상위도메인과 일반최상위도메인으로 구분된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3) TLD(Top Level Domain)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;com, org, net, edu..와 같은 탑레벨 도메인&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4) Authoritative servers&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기관에서 관장하는 서버&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DNS record 종류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS Record는 DNS에 온 &lt;b&gt;요청에 대해 어떻게 처리할 것인가를 나타낸 지침&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS 관리 서비스를 제공하는 &lt;a href=&quot;https://www.cloudflare.com/ko-kr/learning/dns/dns-records/&quot;&gt;cloudflare&lt;/a&gt;에 다양한 DNS 레코드 정보가 나와있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 4가지를 상세히 알아보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;A record&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인 주소와 서버의 IP 주소를 직접 매핑하는 방법이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, &lt;span style=&quot;color: #006dd7;&quot;&gt;cobinding.com&lt;/span&gt;이라는 도메인이 있고 EC2 등에서 설정한 서버의&lt;span style=&quot;color: #006dd7;&quot;&gt; IP 주소가 1.2.3&lt;/span&gt;이라고 가정하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가비아, cloudflare같은 서비스를 통해 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;cobinding.com-1.2.3&lt;/b&gt;&lt;/span&gt;을 직접 연동해주면, 사용자는 이 도메인 주소를 통해 서비스에 접근할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 67.4425%; height: 86px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;b&gt;도메인 이름&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 54.0073%; text-align: center;&quot;&gt;&lt;b&gt;IP 주소&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;cobinding.com&lt;/td&gt;
&lt;td style=&quot;width: 54.0073%; text-align: center;&quot;&gt;1.2.3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 A record는 &lt;span style=&quot;color: #ef5369;&quot;&gt;반드시 도메인과 IP가 1:1 매칭이 되는 것은 아니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관리자의 설정에 따라, &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;일대다 / 다대일&lt;/b&gt;&lt;/span&gt;도 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 68.372%; height: 108px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;b&gt;도메인 이름&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;b&gt;IP 주소&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;cobinding.com&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;1.2.3&lt;br /&gt;1.2.4&lt;br /&gt;1.2.6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CNAME record&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Canonical Name의 약자로, 도메인 주소를 또 다른 도메인 주소로 매핑시키는 형태의 DNS record 타입이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 host aliasing을 통해 이 개념을 이해해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;host aliasing&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS는 IP- hostname 매핑을 위해서 서버에 도메인 주소와 IP 주소를 쌍으로 저장해둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;naver.com 192.168.0.1&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;http://dev.naver.com&quot;&gt;dev.naver.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;http://naver.com&quot;&gt;naver.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;http://google.com&quot;&gt;google.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;172.17.0.1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 요청하면 이 테이블에서 정보 꺼내옴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 행을 레코드(record)라고 하며, 저장되는 타입에 따라 &lt;b&gt;A Record&lt;/b&gt;와 &lt;b&gt;CNAME&lt;/b&gt;으로 구분한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A Record: 직결 매핑. 도메인 - IP 주소가 그대로 매핑됨&lt;/li&gt;
&lt;li&gt;CNAME: 사용자가 dev.naver.com을 요청하면 서버는 naver.com을 리턴하고, 사용자는 다시 naver.com을 통해서 192.168.0.1을 응답한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;A RECORD&lt;/span&gt;는 서버의 IP 주소를 한 번에 알 수 있지만, IP 주소가 자주 바뀌면 번거로울 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;CNAME&lt;/span&gt;은 IP 주소가 자주 변경되는 환경에서 유연하게 대응할 수 있지만, 실제 IP 주소를 얻기 위해 여러 번 요청해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IP: 프로세스가 돌고 있는 머신의 Network Interface Card에 부여된 Identity &amp;rarr; IPv4, host name(domain name)&lt;/li&gt;
&lt;li&gt;Port: 실행 중인 프로세스에 접근할 수 있는 문&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>CS/Network</category>
      <category>CS</category>
      <category>network</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/277</guid>
      <comments>https://cobinding.tistory.com/277#entry277comment</comments>
      <pubDate>Wed, 24 Apr 2024 14:59:31 +0900</pubDate>
    </item>
    <item>
      <title>[네트워크] TCP/IP 5계층 | Application Layer</title>
      <link>https://cobinding.tistory.com/275</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;TCP/IP 5계층&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Application
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FTP, HTTP, SMTP 등의 프로토콜&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Transport
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP, UCP&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Network
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IP, 스위치&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Link
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이더넷&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Physical&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;여기서 7계층되면&amp;hellip;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Application - Transport 계층 사이에 두 계층 추가
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Presentation 인코딩, 디코딩, 암호화/복호화&lt;/li&gt;
&lt;li&gt;Session: 애플리케이션 간 연결 지원(SSL, TLS 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;계층 간 이동 &amp;rArr; Encapsulation&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 계층을 이동하면서 프로토콜 헤더를 붙이게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캡슐화: 송신 측에서 응용계층부터 각 계층을 들리면서 헤더 추가, 물리 계층까지 가면 최종적으로 전기 신호로 변환되어 수신 측에 도착&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;646&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzEeli/btsGTkJXfAM/Myw3ZHaH65505vETANaDDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzEeli/btsGTkJXfAM/Myw3ZHaH65505vETANaDDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzEeli/btsGTkJXfAM/Myw3ZHaH65505vETANaDDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzEeli%2FbtsGTkJXfAM%2FMyw3ZHaH65505vETANaDDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;422&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;646&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Applciation Architecture(P2P와 Client-Server 구조)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;906&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DrjZZ/btsGRCSoHyS/v87IGQ7CK5fqRh8cJUkCj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DrjZZ/btsGRCSoHyS/v87IGQ7CK5fqRh8cJUkCj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DrjZZ/btsGRCSoHyS/v87IGQ7CK5fqRh8cJUkCj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDrjZZ%2FbtsGRCSoHyS%2Fv87IGQ7CK5fqRh8cJUkCj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;320&quot; data-origin-width=&quot;906&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Client-Server 구조&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버는 항상 호스팅되어야 하고, 영구 IP 주소를 가져야 한다.&lt;/li&gt;
&lt;li&gt;클라이언트는 서버와 소통하며, 서로 직접 소통 X. 유동 IP 가진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;P2P 구조&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버가 항상 호스팅되어야 하는 것은 아니다. &amp;rarr;&lt;b&gt; peer 끼리 소통&lt;/b&gt;하는 거고,&lt;span style=&quot;color: #ef5369;&quot;&gt; 고정된 서버가 없다&lt;/span&gt;. 스스로가 서버가되고 클라이언트가 되는 구조다.&lt;/li&gt;
&lt;li&gt;self scalability 특성: 새로운 peer가 생기면 계속 서비스 스케일이 늘어난다.&lt;/li&gt;
&lt;li&gt;IP가 변동되기도 하는데, 전체적으로 관리가 어렵다.&lt;/li&gt;
&lt;li&gt;대표적인 예) 비트토렌트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Process Communication&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;같은 호스트 안에서 두 개의 프로세스는 IPC(Inter-Process Communication)한다.&lt;/li&gt;
&lt;li&gt;다른 호스트의 프로세스는 메시지 교환 &amp;rarr; 소켓 통신
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소켓은 &lt;b&gt;프로세스의 문(door)&lt;/b&gt;과 같은 역할을 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 소켓 통신은 &lt;b&gt;다른 호스트에 존재하는 프로세스간 통신&lt;/b&gt;이다! &lt;span style=&quot;background-color: #f6e199;&quot;&gt;각각의 소켓은 서로 &lt;b&gt;IP주소와 port 번호&lt;/b&gt;로 식별되고, &lt;b&gt;네트워크를 통해 서로 연결&lt;/b&gt;하고 데이터 송수신한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IP는 컴퓨터를 찾아가는 역할&lt;/li&gt;
&lt;li&gt;PORT는 많은 프로세스 중 원하는 프로세스를 찾아가는 역할&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*웹서버는 모두 80포트 사용. 네이버, 다음, 구글 등등 통일적으로 찾아가기 위해서!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;App Layer Protocol RFC&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Requests for comments: IETF에서 관리하는 기술 표준으로, 프로토콜 및 파일이 주요 주제이며 승인된 문서는 일련된 번호를 갖는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP는 RFC-2068에서 시작되었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;App Layer와 Transport Layer의 관계&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Data Integrity: 데이터의 유실없이 도착&lt;/li&gt;
&lt;li&gt;Timing: 예정된 시간 내에 도착&lt;/li&gt;
&lt;li&gt;Throughput: 데이터의 처리율(용량)이 충분&lt;/li&gt;
&lt;li&gt;Security: 안정성&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr;&lt;span style=&quot;color: #ef5369;&quot;&gt; Transport Layer가 제공하는 것은 1번 딱 하나.&lt;/span&gt; 나머지는 app 계층에서 처리.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;App Layer 주요 프로토콜&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Protocol Port Application underlying Transport Protocol&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTTP&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;web&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMTP&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;e-mail&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Telnet&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;remote terminal 접근&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FTP&lt;/td&gt;
&lt;td&gt;21/20&lt;/td&gt;
&lt;td&gt;파일 변환&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RTP&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;스트리밍&lt;/td&gt;
&lt;td&gt;TCP or UDP&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SSL(Secure Socket Layer)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷을 통해 전달되는 정보를 보호하기 위해 개발한 통신 규약 &amp;rarr; HTTPS는 HTTP를 암호화하여 전송&lt;/p&gt;</description>
      <category>CS/Network</category>
      <category>network</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/275</guid>
      <comments>https://cobinding.tistory.com/275#entry275comment</comments>
      <pubDate>Wed, 24 Apr 2024 14:49:04 +0900</pubDate>
    </item>
    <item>
      <title>[Algorithm] 이중 포문 조정 - 구간합</title>
      <link>https://cobinding.tistory.com/274</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간복잡도 줄이는 법을 학습하면서 해본 정리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.codetree.ai/missions&quot;&gt;코드트리&lt;/a&gt;를 참고하였다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 배열에서 &lt;u&gt;특정 합을 만족하며 가장 크기가 큰 구간의 크기를&lt;/u&gt;&amp;nbsp;찾기 위해 완전탐색을 하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;완전탐색 - O(N**3)&lt;/h2&gt;
&lt;pre id=&quot;code_1713337484143&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 이 배열에서 특정 구간을 골라서, 
# 합이 10이 넘지 않아야 하고,
# 배열의 크기가 가장 커야 한다. 이때의 배열 크기는?

arr = [0,6,3,2,4,9,1]
n = len(arr)
target=10

# 무식하게 구해보기
for i in range(1, n+1):
    for j in range(i, n+1):
        total = 0
        for k in range(i, j+1):
            total += arr[k]
        
        if total &amp;lt;= target:
            ans = max(ans, j-i+1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드의 복잡도는 &lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;O&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;&lt;span&gt;N**&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;)이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡도를 줄여보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구간 내 시작점과 끝 활용 - O(N**2)&lt;/h2&gt;
&lt;pre id=&quot;code_1713337863926&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 일단 모든 구간 탐색
for i in range(1,n+1):
    total=0
    for j in range(i,n+1):
        total += arr[j]
    
    # 특정합이 타겟합을 넘었을 때
    if total &amp;gt; target : break

    ans = max(ans, j-i+1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*이 경우, j는&lt;u&gt; i 값이 달라질 때마다 i에서 새로 시작&lt;/u&gt;하므로 후퇴하는 경우가 많이 발생하게 된다. ➡️&amp;nbsp;&lt;b&gt;시간복잡도는 O(N**2)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1713338374457&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# while로 표현해보면
total=0
j=0

for i in range(1,n+1):
    while j+1 &amp;lt;= n and total + arr[j+1] &amp;lt;= target:
        total += arr[j+1]
        j += 1
    
    ans = max(ans, j-i+1)
    
    # 다음 구간으로 넘어가기 전에 arr[i]의 값은 해당 구간에서 제외한다.
    total -= arr[i]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;투 포인터는 이렇게&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;i, j가 한 방향으로 전진하면서 연산을 수행&lt;/span&gt;&lt;/b&gt;하는 형태를 갖는다. ➡️&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;시간복잡도는 O(N)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 i, j 전진 테크닉은 실제로 이중 포문으로 작성되어 있지만, i와 j가 한 방향으로 진행되기 때문에 시간 복잡도는 두 for loop의 곱이 아니라 합으로 나타내진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구간 내 특정 합이 존재하고, 중복되는 값 Counting&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은&amp;nbsp;숫자가&amp;nbsp;3개&amp;nbsp;이상&amp;nbsp;있지&amp;nbsp;않은&amp;nbsp;경우&amp;nbsp;중,&amp;nbsp;가장&amp;nbsp;큰&amp;nbsp;구간의&amp;nbsp;크기&lt;/p&gt;
&lt;pre id=&quot;code_1713341837935&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### 같은 숫자가 3개 이상 있지 않은 경우 중, 가장 큰 구간의 크기
n,s=map(int,input().split())
arr=[0] + list(map(int,input().split()))


# 각 숫자마다 몇 번씩 나왔는지 카운팅하는 배열을 추가적으로 만들어 관리한다.
## cnt_arr의 인덱스는 숫자 정보, value는 해당 숫자가 등장한 개수
cnt_arr=[0]*4


ans=0
j=0
for i in range(1,n+1):
    while j+1 &amp;lt;= n and cnt_arr[arr[j+1]] != 2:
        cnt_arr[arr[j+1]]+=1
        j+=1
    
    ans=max(ans,j-i+1)
    # 하나 제거
    cnt_arr[arr[i]] -= 1&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/Algorithm</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/274</guid>
      <comments>https://cobinding.tistory.com/274#entry274comment</comments>
      <pubDate>Wed, 17 Apr 2024 18:30:38 +0900</pubDate>
    </item>
    <item>
      <title>[DB] DB스튜디오 과제 리뷰 2 | 데이터 모델링과 개념적 설계</title>
      <link>https://cobinding.tistory.com/273</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cobinding.tistory.com/270&quot;&gt;지난 1편&lt;/a&gt;에 이어서, 이번 과제도 배울 점이 많아 리뷰를 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요구사항 1&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1478&quot; data-origin-height=&quot;1104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcvuHy/btsGEWDr9TO/mDUgOHrtQ5S2jhP970kJgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcvuHy/btsGEWDr9TO/mDUgOHrtQ5S2jhP970kJgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcvuHy/btsGEWDr9TO/mDUgOHrtQ5S2jhP970kJgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcvuHy%2FbtsGEWDr9TO%2FmDUgOHrtQ5S2jhP970kJgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;486&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1478&quot; data-origin-height=&quot;1104&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 과제에서는 &lt;b&gt;DATABASE&lt;/b&gt; 설계 명목 하에 개체와 속성이 뚜렷하게 보이는 식으로 요구사항이 정의되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 원래 요구사항은 대화하는 방식으로 주어진다. 이번 과제부터 &lt;b&gt;자연어 요구사항&lt;/b&gt;이 주어졌고.. 이러한 경우는 &quot;모호성 제거&quot; 단계를 거쳐야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1310&quot; data-origin-height=&quot;1052&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dLDBAu/btsGIMZSn6e/YIEa7ZepR4LQktpr1XgsC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dLDBAu/btsGIMZSn6e/YIEa7ZepR4LQktpr1XgsC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dLDBAu/btsGIMZSn6e/YIEa7ZepR4LQktpr1XgsC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdLDBAu%2FbtsGIMZSn6e%2FYIEa7ZepR4LQktpr1XgsC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1310&quot; height=&quot;1052&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1310&quot; data-origin-height=&quot;1052&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 설계는 위와 같은데, 피드백을 받으면서 느낀 점은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요구사항이 빠졌는지에 대해 면밀히 검토하는 것은 좋다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;그런데 &lt;u&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;개체 간의 단순한 관계의 의미를 넘어서&lt;/span&gt; 응용 관점을 검토&lt;/u&gt;해보는 태도가 필요하다. 그러니까 위 설계같은 경우는 Customer와 Book 사이에 order라는 관계는 의미상으로는 완벽하다. 하지만 다음 질문에 답하기 어려운 설계다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Customer가 같은 책을 다른 날에 걸쳐 여러 번 주문하게 될 경우 어떻게 처리할 것인가?&lt;/li&gt;
&lt;li&gt;참고) 관계가 식별키를 가질 수는 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;1389&quot; data-origin-height=&quot;715&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7qFMr/btsGFqdaDoc/jjzK2PFM4FHsvNcbMd1FtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7qFMr/btsGFqdaDoc/jjzK2PFM4FHsvNcbMd1FtK/img.png&quot; data-alt=&quot;연하게 되어있는 관게는 첫 번째 단계까지만 수행하고 추후 보정을 못한 케이스다.(나의 케이스....)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7qFMr/btsGFqdaDoc/jjzK2PFM4FHsvNcbMd1FtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7qFMr%2FbtsGFqdaDoc%2FjjzK2PFM4FHsvNcbMd1FtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1389&quot; height=&quot;715&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;1389&quot; data-origin-height=&quot;715&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;연하게 되어있는 관게는 첫 번째 단계까지만 수행하고 추후 보정을 못한 케이스다.(나의 케이스....)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요구사항 2&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;822&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BqYRf/btsGGcMrors/YPUdK34x0qGhNY99r5IjK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BqYRf/btsGGcMrors/YPUdK34x0qGhNY99r5IjK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BqYRf/btsGGcMrors/YPUdK34x0qGhNY99r5IjK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBqYRf%2FbtsGGcMrors%2FYPUdK34x0qGhNY99r5IjK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1392&quot; height=&quot;822&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;822&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1608&quot; data-origin-height=&quot;1038&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tsZqx/btsGGejcQnl/ZlztDbrV040KOnMletoLR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tsZqx/btsGGejcQnl/ZlztDbrV040KOnMletoLR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tsZqx/btsGGejcQnl/ZlztDbrV040KOnMletoLR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtsZqx%2FbtsGGejcQnl%2FZlztDbrV040KOnMletoLR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1608&quot; height=&quot;1038&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1608&quot; data-origin-height=&quot;1038&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;역시나 부족한 모호성 제거.... 교수 속성에서도 주제, 상태같은 모호한 것은 정의를 하고 반영하는게 맞다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1389&quot; data-origin-height=&quot;1389&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NKHN8/btsGGW3B59t/h6HOMQHvo6gwVRNecmqJXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NKHN8/btsGGW3B59t/h6HOMQHvo6gwVRNecmqJXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NKHN8/btsGGW3B59t/h6HOMQHvo6gwVRNecmqJXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNKHN8%2FbtsGGW3B59t%2Fh6HOMQHvo6gwVRNecmqJXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;650&quot; data-origin-width=&quot;1389&quot; data-origin-height=&quot;1389&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모호성 제거 단계를 통해&lt;span style=&quot;color: #006dd7;&quot;&gt; 주소지에 관한 다양한 요구사항에 대해서&lt;/span&gt;&lt;b&gt; 도시 개체를 만들어&lt;/b&gt; 한번에 해결한다.&lt;/li&gt;
&lt;li&gt;날짜가 주는&lt;b&gt; 추상화에서 오는 모호성을 제거&lt;/b&gt;하여 요일로 표현한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1389&quot; data-origin-height=&quot;1389&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuz38p/btsGGep3tEy/GyKNWcKVp7kwKw6QPGAx91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuz38p/btsGGep3tEy/GyKNWcKVp7kwKw6QPGAx91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuz38p/btsGGep3tEy/GyKNWcKVp7kwKw6QPGAx91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcuz38p%2FbtsGGep3tEy%2FGyKNWcKVp7kwKw6QPGAx91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;650&quot; data-origin-width=&quot;1389&quot; data-origin-height=&quot;1389&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 크게 골격 스키마를 정리하고 나서 겹치는 부분은 다시 세분화하고, 여러 케이스를 따져서 구체화해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 2번은 내가 설계한 것처럼 subentity를 표현하는 것이 문제 의도라고 생각하였는데, 풀이를 듣고보니 스키마 설게 단계 각각을 잘 이해하고 수행하고 있는지 확인하는 것이 의도였다. 어떤 식으로 표현을 하든 정통하는 설계 범주가 있으니, 단계 별로 해야할 일을 주의해서 설계하도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스키마 설계 단계&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;모호성 제거&lt;/li&gt;
&lt;li&gt;제거 후, 관련 있는 문장끼리 나누어 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;집합으로 정리&lt;/b&gt;&lt;/span&gt;한다. (집합으로 정리하는게 union이나 super-sub 관계를 나타내기도 좋은 것같다..! 앞으로 설계를 할 때 적극적으로 집합 정의를 해볼 생각이다.)&lt;/li&gt;
&lt;li&gt;골격만 잡는 정도의(C-O-B 딱 이 수준의) 초기 설계 후, 여러 상황을 가정하며 세분화 진행; C-O-B에서 여러 주문은 어떻게하지? 이런식으로 의문을 잡아 나가는 것&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;모호성 제거&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 용어에 대한 추상화의 적절한 단계를 선택한다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 용어라는 것은 &lt;b&gt;추상적 용어와 구체적 용어&lt;/b&gt; 두 가지를 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, &lt;u&gt;살았던 도시와 년수&lt;/u&gt;를 더 추상적인 용어로 변경하면 &lt;u&gt;장소와 기간&lt;/u&gt;이 된다. 요구사항을 읽고 두 용어 간의 접점을 잘 잡아서 명명 및 설계해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 일반적인 개념대신 인스턴스의 사용 회피&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요 이상의 특정 용어들을 막 남발하면 이 용어 때문에 괜한 복잡한 설계를 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서 컴퓨터에는 cpu, ram, 메인보드 등이 있는데 이 하나하나를 DB에 사용하면.... 의도치 않게 난잡해질 수 있다. 따라서, 이들을 포괄하는 부품(components)과 같이 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;포괄적인/일반적인 개념으로 표현&lt;/span&gt;하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 완곡한 표현 회피&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 완곡한 표현을 사용하는 것 또한 의도치않은 오류나 불편함을 초래한다. &quot;매표창구에 앉아있는 사람&quot;은 &quot;매표원&quot;으로 교체하는 등 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;명확한 개념과 용어를 사용&lt;/span&gt;하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 표준 문장 형식의 선택&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 문장 스타일 대신 단순한 문법 범주의 문장을 사용하자.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;lt;주어&amp;gt;&amp;lt;동사&amp;gt;&amp;lt;서술부&amp;gt;&lt;/span&gt; 형식&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;lt;if&amp;gt;&amp;lt;condition&amp;gt;&amp;lt;then&amp;gt;&amp;lt;action&amp;gt;&lt;/span&gt; 등 프로그래밍 언어 형태의 문장을 활용하는 것이 좋다. (애플리케이션 개발자와 계속 소통할테니 이 편이 확실히 좋다는 느낌을 받았다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5. 용어들 사이의 명백한 참조&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조할 개념들이 요구사항에 명백히 표현되게 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번의 추상화 개념과 같이 고려하면 좋다. 예를 들어 이런 것이다. &quot;날짜(day)&quot;는 요일(day of the week)을 말하는 것인지, 날짜(day of month)를 말하는 것인지 명백하게 하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6. 용어 해설집&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 과제에서 하는 설계는 개체가 10개도 되지 않는 간단한 것들이기에 바로바로 설명이되지만 실무에서는 포괄적인 용어 해설집을 작성하여 스키마를 접하는 사람의 이해를 돕는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1389&quot; data-origin-height=&quot;1389&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bI1aqP/btsGHl2TepH/Yco5yEKpE8vV8nU5MB7Msk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bI1aqP/btsGHl2TepH/Yco5yEKpE8vV8nU5MB7Msk/img.png&quot; data-alt=&quot;요구사항 2의 모호성을 제거한 예시다. 대체[모호한 개념]로 표기되어있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bI1aqP/btsGHl2TepH/Yco5yEKpE8vV8nU5MB7Msk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbI1aqP%2FbtsGHl2TepH%2FYco5yEKpE8vV8nU5MB7Msk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;550&quot; data-origin-width=&quot;1389&quot; data-origin-height=&quot;1389&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;요구사항 2의 모호성을 제거한 예시다. 대체[모호한 개념]로 표기되어있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각보다 사소하지만 배우지 않으면 할 수 없는 것들을 배워서 가끔 충격을 받곤한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;졸업하기 전에 제대로된 DB 설계 수업을 듣고싶어서 수강했는데, 잘 했다는 생각이 든다..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과제가 많고 개념을 하나하나 공부하기 시간이 필요하지만 성장했으면 좋겟다.&lt;/p&gt;</description>
      <category>DB/DB 설계 과제</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/273</guid>
      <comments>https://cobinding.tistory.com/273#entry273comment</comments>
      <pubDate>Wed, 17 Apr 2024 13:27:22 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] public &amp;amp; private subnet 분리로 서버 운영하기</title>
      <link>https://cobinding.tistory.com/272</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 서비스를 조금 더 보안성 있게 운영하고자 한다면, 한 VPC 내의 subnet을 분리하여 사용하는 방법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트는 앞으로 동아리에서 쭉 사용할 서비스이므로 처음부터 보안에 유의하여 설계를 해야겠다 다짐했다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 포스팅을 통해 어떤 과정을 겪었는지 정리해보려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AWS에서 자체적으로 해주는 것이 많아서 생각보다 많이 간단했다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;VPC 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 계정을 생성하고 나면&lt;span style=&quot;color: #006dd7;&quot;&gt; 기본 VPC는 존재&lt;/span&gt;한다.&lt;b&gt; 별도의&lt;/b&gt; 퍼스널 서브넷 세팅을 위해 새로 VPC를 생성하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1137&quot; data-origin-height=&quot;781&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgnVsI/btsGqNFXLf5/3cOsLlvolSFH7QRslj4Xuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgnVsI/btsGqNFXLf5/3cOsLlvolSFH7QRslj4Xuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgnVsI/btsGqNFXLf5/3cOsLlvolSFH7QRslj4Xuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgnVsI%2FbtsGqNFXLf5%2F3cOsLlvolSFH7QRslj4Xuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1137&quot; height=&quot;781&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1137&quot; data-origin-height=&quot;781&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성할 리소스 옵션 중 VPC만/VPC 등 두 가지가 있는데, private subnet에 RDS를 두려면 반드시 후자를 선택해야 한다. RDS 생성 시에도 RDS subnet을 세팅하게 되는데 이때 subnet은 두 개 이상의 리전에 대한 VPC에만 설정할 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;가용영역 세팅하기&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/56TbX/btsGqhnhrEt/bCuxJSmulW5vyyOYjbezT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/56TbX/btsGqhnhrEt/bCuxJSmulW5vyyOYjbezT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/56TbX/btsGqhnhrEt/bCuxJSmulW5vyyOYjbezT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F56TbX%2FbtsGqhnhrEt%2FbCuxJSmulW5vyyOYjbezT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;732&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가용영역은 서비스를 호스팅할 지역의 2a, 2b로 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브넷 CIDR 블록은 &lt;span style=&quot;color: #006dd7;&quot;&gt;private subnet은 RDS만 둘 예정이므로 25로 비교적 작은 값으로 세팅하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후, NAT 게이트웨이와 VPC 엔드포인트는 일단 &lt;b&gt;없음&lt;/b&gt;으로 설정하고 다음 세팅으로 미루도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;생성 후 VPC 리소스맵을 통해 확인&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;840&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddL633/btsGsqQxsiH/67w0s2DyIkKG1r9U8QzeDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddL633/btsGsqQxsiH/67w0s2DyIkKG1r9U8QzeDk/img.png&quot; data-alt=&quot;세부정보 탭 옆 리소스맵&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddL633/btsGsqQxsiH/67w0s2DyIkKG1r9U8QzeDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FddL633%2FbtsGsqQxsiH%2F67w0s2DyIkKG1r9U8QzeDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1900&quot; height=&quot;840&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;840&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;세부정보 탭 옆 리소스맵&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 리전 가용영역에 public/private 서브넷이 세팅된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특히 public 라우팅 테이블은 &lt;u&gt;두 개의 public 서버와 한 번에 연&lt;/u&gt;결되어 있다.&lt;/li&gt;
&lt;li&gt;또한 외부와의 통신을 위해&lt;span style=&quot;color: #ee2323;&quot;&gt; Igw(Internet gateway)&lt;/span&gt;와 잘 연동되어있음을 확인할 수 있다.&lt;/li&gt;
&lt;li&gt;private 라우팅 테이블은 개별 서브넷에 할당되어 있으며&lt;b&gt; Igw와는 당연히 연결이 되어있지 않다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인터넷 게이트웨이란?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VPC와 인터넷 간 통신을 가능하게 하는 통로다. 이 인터넷 게이트웨이를 VPC에 연결하여 퍼블릭 서브넷으로 연결해주면 해당 서브넷이 퍼블릭 역할을 할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 VPC 등 세팅을 한꺼번에 하게되면, AWS에서 자동으로 이를 생성하고, 등록해준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 따로 Internet Gateway를 설정하고 등록하려면 아래와 같이 게이트웨이와 VPC를 연동해주면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1686&quot; data-origin-height=&quot;1176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBzuqW/btsGrP4agXQ/YGyB1ff2eKSDeqJ0ySo1kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBzuqW/btsGrP4agXQ/YGyB1ff2eKSDeqJ0ySo1kK/img.png&quot; data-alt=&quot;해당 프로젝트에 사용되지 않는 별도의 IGW&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBzuqW/btsGrP4agXQ/YGyB1ff2eKSDeqJ0ySo1kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBzuqW%2FbtsGrP4agXQ%2FYGyB1ff2eKSDeqJ0ySo1kK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;785&quot; height=&quot;548&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1686&quot; data-origin-height=&quot;1176&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해당 프로젝트에 사용되지 않는 별도의 IGW&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;보안그룹 세팅&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안 그룹은 특정 VPC에 대한 규칙이므로 처음에 반드시 보안 사항을 적용할 VPC를 선택해주어야 함을 유의하자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인바운드 &lt;span style=&quot;color: #006dd7;&quot;&gt;public&lt;/span&gt; 규칙&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부에서 VPC - public subnet 내부의 EC2로 접근 가능한 프로토콜을 세팅한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;인바운드&lt;/b&gt;에 대한 규칙을 설정한다. public 서버에 대한 보안 그룹이므로, &lt;b&gt;모든 트래픽(Ipv4: 0.0.0.0/0, Ipv6: ::/0)&lt;/b&gt;을 허용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2054&quot; data-origin-height=&quot;1394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7Xy5M/btsGrST72c4/yfdFE1XgS2qXLZkIlg6dMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7Xy5M/btsGrST72c4/yfdFE1XgS2qXLZkIlg6dMk/img.png&quot; data-alt=&quot;유형만 선택하면 프로토콜과 포트를 결정해주어서 정말 편했다..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7Xy5M/btsGrST72c4/yfdFE1XgS2qXLZkIlg6dMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7Xy5M%2FbtsGrST72c4%2FyfdFE1XgS2qXLZkIlg6dMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2054&quot; height=&quot;1394&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2054&quot; data-origin-height=&quot;1394&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;유형만 선택하면 프로토콜과 포트를 결정해주어서 정말 편했다..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*아웃바운드는 apt-get 등 패키지 설치를 위해 http, https만 허용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인바운드 &lt;span style=&quot;color: #006dd7;&quot;&gt;private&lt;/span&gt; 규칙&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 내부망에서 관리자가 EC2를 통해 RDS로 접근할 수 있는 보안 규칙을 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;610&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GEgMD/btsGtkifrwF/RtkbdyJqfzgSbP8NHiCIuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GEgMD/btsGtkifrwF/RtkbdyJqfzgSbP8NHiCIuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GEgMD/btsGtkifrwF/RtkbdyJqfzgSbP8NHiCIuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGEgMD%2FbtsGtkifrwF%2FRtkbdyJqfzgSbP8NHiCIuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;351&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;610&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB 접근 전용 SG이므로, 위와 같은 명명 규칙과 설명을 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2460&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/br9FYp/btsGrTS2kLh/hK43BE71dH7XmB2XJicyP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/br9FYp/btsGrTS2kLh/hK43BE71dH7XmB2XJicyP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/br9FYp/btsGrTS2kLh/hK43BE71dH7XmB2XJicyP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbr9FYp%2FbtsGrTS2kLh%2FhK43BE71dH7XmB2XJicyP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;740&quot; height=&quot;180&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2460&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때는&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; public 설정과 달리 트래픽 범위를 제한해야 한다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 유형/프로토콜/포트범위는 사용할 MySQL로 제한하고, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;소스 범위는 위에서 정의한 public SG로 할당한다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 제한함으로써 private한 구축이 가능하게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9zDvf/btsGspjNc67/SKNdmEOSoIFutcVe6Kr600/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9zDvf/btsGspjNc67/SKNdmEOSoIFutcVe6Kr600/img.png&quot; data-alt=&quot;세팅 완료&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9zDvf/btsGspjNc67/SKNdmEOSoIFutcVe6Kr600/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9zDvf%2FbtsGspjNc67%2FSKNdmEOSoIFutcVe6Kr600%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;342&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;세팅 완료&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GCP를 쓰다가 AWS로 프로젝트에 도전해보았는데 훨씬 자동화도 잘 되어있고 편리하다는 것을 많이 느꼈다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 과정을 통해 기반을 다져두었으니,&amp;nbsp; 다음 포스팅에서는 RDS와 EC2 등 세팅을 진행해보도록 하자.&lt;/p&gt;</description>
      <category>DevOps/AWS</category>
      <category>AWS</category>
      <category>DevOps</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/272</guid>
      <comments>https://cobinding.tistory.com/272#entry272comment</comments>
      <pubDate>Sun, 7 Apr 2024 19:06:30 +0900</pubDate>
    </item>
    <item>
      <title>[백준/1063] 킹 | 구현 실수 방지를 위한 경우의 수 체크하기</title>
      <link>https://cobinding.tistory.com/271</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1063&quot;&gt;킹&lt;/a&gt;&lt;/h2&gt;
&lt;figure id=&quot;og_1711863607707&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;1063번: 킹&quot; data-og-description=&quot;8*8크기의 체스판에 왕이 하나 있다. 킹의 현재 위치가 주어진다. 체스판에서 말의 위치는 다음과 같이 주어진다. 알파벳 하나와 숫자 하나로 이루어져 있는데, 알파벳은 열을 상징하고, 숫자는 &quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1063&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1063&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/qbF7s/hyVGRsTNKY/Alkt39cV2cbTvrMDx9Ztck/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/DOyA4/hyVGLzqmWs/yJ3pQ8VuENCvSXs4qFK2Y0/img.png?width=645&amp;amp;height=330&amp;amp;face=0_0_645_330&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1063&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1063&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/qbF7s/hyVGRsTNKY/Alkt39cV2cbTvrMDx9Ztck/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/DOyA4/hyVGLzqmWs/yJ3pQ8VuENCvSXs4qFK2Y0/img.png?width=645&amp;amp;height=330&amp;amp;face=0_0_645_330');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;1063번: 킹&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;8*8크기의 체스판에 왕이 하나 있다. 킹의 현재 위치가 주어진다. 체스판에서 말의 위치는 다음과 같이 주어진다. 알파벳 하나와 숫자 하나로 이루어져 있는데, 알파벳은 열을 상징하고, 숫자는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;알고리즘&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;킹이 움직인다. 이때 &lt;b&gt;체크해야할 조건&lt;/b&gt;은 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;격자 안에서만 움직여야 한다.&lt;/li&gt;
&lt;li&gt;돌이 있는 곳으로 움직일 경우, 돌과 함께 움직여야 한다.&lt;/li&gt;
&lt;li&gt;이때 돌 또한 격자 안에서 움직여야 한다.&lt;/li&gt;
&lt;li&gt;돌이 격자 밖으로 나가는 경우도 체크하되, &lt;span style=&quot;color: #ee2323;&quot;&gt;이때 킹이 격자 내에서 움직인다면 상관없이 움직인다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 코드로 나타내면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1711863468396&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 격자 내에서 해결해야한다.
if (0 &amp;lt;= king_row+r &amp;lt; 8 and 0 &amp;lt;= king_col+c &amp;lt; 8):
    # king이 rock이랑 같은 방향으로 갈 경우
    if (king_row+r == rock_row) and (king_col+c == rock_col):
        # 돌이 움직일 수 있다면 (격자 내라면 같이 이동)
        if (0 &amp;lt;= rock_row+r &amp;lt; 8 and 0 &amp;lt;= rock_col+c &amp;lt; 8):
            rock_row += r
            rock_col += c
            king_row += r
            king_col += c
        # 돌이 격자 밖으로 움직이는 경우
        else:
            continue
    else:
        king_row += r
        king_col += c&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;돌과 킹이 함께 움직이되 킹이 우선순위다. 이 케이스들을 if로 분기해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 코드&lt;/h2&gt;
&lt;pre id=&quot;code_1711863619726&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 행: row, 열: col

row = {'8':0, '7':1, '6':2, '5':3, '4':4, '3':5, '2':6, '1':7}
col = {'A':0, 'B':1, 'C':2, 'D':3, 'E':4, 'F':5, 'G':6, 'H':7}
move = {'R':(0,1), 'L':(0,-1), 'B':(1,0), 'T':(-1,0), 'RT':(-1,1), 'LT':(-1,-1), 'RB':(1,1), 'LB':(1,-1)}


king, rock, n = input().split()
n=int(n)
graph = [[0 for _ in range(8)] for _ in range(8)] # 8*8 체스판


king_row, king_col = row[king[1]], col[king[0]]
rock_row, rock_col = row[rock[1]], col[rock[0]]
graph[rock_row][rock_col] = 1

for _ in range(n):
    command = input()
    r,c = move[command][0], move[command][1]
    
    # 격자 내에서 해결해야한다.
    if (0 &amp;lt;= king_row+r &amp;lt; 8 and 0 &amp;lt;= king_col+c &amp;lt; 8):
        # king이 rock이랑 같은 방향으로 갈 경우
        if (king_row+r == rock_row) and (king_col+c == rock_col):
            # 돌이 움직일 수 있다면 (격자 내라면 같이 이동)
            if (0 &amp;lt;= rock_row+r &amp;lt; 8 and 0 &amp;lt;= rock_col+c &amp;lt; 8):
                rock_row += r
                rock_col += c
                king_row += r
                king_col += c
            # 돌이 격자 밖으로 움직이는 경우
            else:
                continue
        else:
            king_row += r
            king_col += c
            
            
king_position=str()
for k,v in col.items():
    if v == king_col:
        king_position += k
            
for k,v in row.items():
    if v == king_row:
        king_position += k

print(king_position)

rock_position=str()
for k,v in col.items():
    if v == rock_col:
        rock_position += k
        
for k,v in row.items():
    if v == rock_row:
        rock_position += k
    
print(rock_position)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;알게된 점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;경우의 수를 모두 손으로 작성하고, 코드로 어떻게 분기할 것인지를 중점으로 구현하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/BOJ&amp;amp;Programmers</category>
      <category>Problem Solving</category>
      <category>구현</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/271</guid>
      <comments>https://cobinding.tistory.com/271#entry271comment</comments>
      <pubDate>Sun, 31 Mar 2024 14:40:53 +0900</pubDate>
    </item>
    <item>
      <title>[DB] DB스튜디오 과제 리뷰 | 데이터 모델링과 개념적 설계</title>
      <link>https://cobinding.tistory.com/270</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB 스튜디오 과목에서 매주 요구사항에 맞게 데이터 모델링을 하고, 개념적 설계를 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 팀이 설계한 과제물에 대해 게시판을 통해 1차 토의하고, 강의시간에 교수님과 2차 토의를한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토의에서 나왔던 내용을 중심으로 과제에 대한 리뷰를 작성해보도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요구사항&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 설계의 요구사항은 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나는&lt;b&gt; UNIVERSITY DB&lt;/b&gt;로, 일반적으로 대학교에서 학생, 학부, 과목 등을 관리하는 요구사항이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6YpJq/btsF8kXuhTj/DHilevTlvDFp9cna65OkVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6YpJq/btsF8kXuhTj/DHilevTlvDFp9cna65OkVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6YpJq/btsF8kXuhTj/DHilevTlvDFp9cna65OkVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6YpJq%2FbtsF8kXuhTj%2FDHilevTlvDFp9cna65OkVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;420&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 &lt;b&gt;MAIL_ORDER DB&lt;/b&gt;로, 쉽게 말해 구매 서비스다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Btqaq/btsF5HNntPs/N3E3lpyXQJ0S4t14wsaUq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Btqaq/btsF5HNntPs/N3E3lpyXQJ0S4t14wsaUq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Btqaq/btsF5HNntPs/N3E3lpyXQJ0S4t14wsaUq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBtqaq%2FbtsF5HNntPs%2FN3E3lpyXQJ0S4t14wsaUq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;380&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개체와 속성, 관계 추출&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 ERD를 그리기 전, 텍스트로 개체와 속성, 관계를 추출하는 과제를 수행하였다. 이때 &lt;span style=&quot;color: #006dd7;&quot;&gt;composite, complex attribute&lt;/span&gt; 등의 &lt;b&gt;속성 특징과 관계를 속성으로 나타내는 것&lt;/b&gt;을 활용하였다. 특히 속성의 종류는 설계에 있어서 중요하다고 생각이 들어 &lt;a href=&quot;https://garnet-waxflower-dcc.notion.site/ERD-526073aa858741b4a916b89d3d10ee75?pvs=4&quot;&gt;여기에&lt;/a&gt; 미리 정리해두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계에서 주의할 것은 다음과 같다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;개념 설계에서는 특정 DB를 전제하지 말자.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 DB를 RDBMS로 먼저 접하는 사람이 많아서 그런지, 개념 설계 단계에 RDBMS의 특징을 보이는 팀이 종종 있었다. 예를 들어, 다른 개체(Entity)의 식별값을 통해 관계를 정의하는 케이스가 가장 많았다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;개념 설계 단계에서는 요구사항을 명확히 파악하고 청사진같은 존재이므로, &lt;span style=&quot;color: #ee2323;&quot;&gt;특정 DB를 전제하에 설계하지 않도록 주의하자.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;ER 모델에서 참조 관계는 속성이 아니라 관계로 표현된다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1394&quot; data-origin-height=&quot;975&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bloyAe/btsF5Ejc6JG/9nATBKkDUxzGDJmCwz5pi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bloyAe/btsF5Ejc6JG/9nATBKkDUxzGDJmCwz5pi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bloyAe/btsF5Ejc6JG/9nATBKkDUxzGDJmCwz5pi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbloyAe%2FbtsF5Ejc6JG%2F9nATBKkDUxzGDJmCwz5pi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;385&quot; data-origin-width=&quot;1394&quot; data-origin-height=&quot;975&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;개념과 표기법을 숙지하자.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;composite, mutiple, complex 등 다양한 속성의 종류가 있다. 그런데 이 개념들을 헷갈려하는 팀들이 많았다. 또한, ERD에서 표기하는 것까지 착각한 팀도 있었다. 위 노션 페이지에 정리한 것을 토대로 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;각각의 개념과 표기법을 숙지하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rMu1T/btsF7RVKX0u/60Tx1sGTdOoe3VA2JFzfH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rMu1T/btsF7RVKX0u/60Tx1sGTdOoe3VA2JFzfH1/img.png&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;836&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.5116%; margin-right: 10px;&quot; data-widthpercent=&quot;50.09&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rMu1T/btsF7RVKX0u/60Tx1sGTdOoe3VA2JFzfH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrMu1T%2FbtsF7RVKX0u%2F60Tx1sGTdOoe3VA2JFzfH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1178&quot; height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFyAui/btsF5SVsEsb/f0T4yXW3dVmWSM21Skv9N1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFyAui/btsF5SVsEsb/f0T4yXW3dVmWSM21Skv9N1/img.png&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;842&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.3257%;&quot; data-widthpercent=&quot;49.91&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFyAui/btsF5SVsEsb/f0T4yXW3dVmWSM21Skv9N1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFyAui%2FbtsF5SVsEsb%2Ff0T4yXW3dVmWSM21Skv9N1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1182&quot; height=&quot;842&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;STUDENT DB에 대한 개체, 속성, 관계 추출&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주소와 휴대폰 번호를 연락처라는 &lt;span style=&quot;color: #006dd7;&quot;&gt;complex attribute&lt;/span&gt;로 구조화 &lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt; AddressPhone({Phone(current, permanent), Address(current, permanent(city, state, zip-code))}) &lt;/span&gt;하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;box-sizing: border-box; font-size: 15px; *** rgb(254, 137, 67): 관련글의 테두리색 입니다. ***font-family: Arial, 돋움, Dotum, AppleGothic, sans-serif; padding: 10px; margin: 0px; line-height: 1.5; background-color: #ffffff; border: 2px solid #4f84c4;&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Course와 Section, Grade가 많이 헷갈렸는데 사실 내 과제물에서는 특이 점이 없었다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;잘못 설계한 점은 Course Entity에 section_number라는 속성을 추가함으로써 외래키를 사용했다는 점이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v6vVa/btsF8vLr4Ls/WQoi2hbx7IyRMWVzVFlkD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v6vVa/btsF8vLr4Ls/WQoi2hbx7IyRMWVzVFlkD1/img.png&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;848&quot; data-is-animation=&quot;false&quot; style=&quot;width: 45.7672%; margin-right: 10px;&quot; data-widthpercent=&quot;46.31&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v6vVa/btsF8vLr4Ls/WQoi2hbx7IyRMWVzVFlkD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv6vVa%2FbtsF8vLr4Ls%2FWQoi2hbx7IyRMWVzVFlkD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1178&quot; height=&quot;848&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nC0tc/btsF5CL4OvU/VeK2yWMcOPu8319iKKMfzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nC0tc/btsF5CL4OvU/VeK2yWMcOPu8319iKKMfzk/img.png&quot; data-origin-width=&quot;1192&quot; data-origin-height=&quot;740&quot; data-is-animation=&quot;false&quot; style=&quot;width: 53.07%;&quot; data-widthpercent=&quot;53.69&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nC0tc/btsF5CL4OvU/VeK2yWMcOPu8319iKKMfzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnC0tc%2FbtsF5CL4OvU%2FVeK2yWMcOPu8319iKKMfzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1192&quot; height=&quot;740&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;MAIL_ORDER DB에 대한 개체, 속성, 관계 추출&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;box-sizing: border-box; font-size: 15px; *** rgb(254, 137, 67): 관련글의 테두리색 입니다. ***font-family: Arial, 돋움, Dotum, AppleGothic, sans-serif; padding: 10px; margin: 0px; line-height: 1.5; background-color: #ffffff; border: 2px solid #4f84c4;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서 좀 크리티컬한 실수를 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;나는 요구사항에서 하나의 &lt;span style=&quot;color: #666666;&quot;&gt;&lt;b&gt;Order&lt;/b&gt;&lt;/span&gt;는&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Customer&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;에 의해 생성되고, 또&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Employee&lt;/span&gt;&lt;/b&gt;에 의해 관리된다는 점에 주목해서 각각의 Entity마다 해당&amp;nbsp; &amp;nbsp;정보를 가질 수 있도록 작성하였다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;데이터가 중복된다&lt;/span&gt;는 단점이 있다. 하나의 Entiry에만 정보를 잘 담아두면 된다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;이때 &lt;span style=&quot;color: #ef5369;&quot;&gt;어떤 개체에 정보를 담을 것이냐?를 유의해야 한&lt;/span&gt;다. (위 예시같은 경우는 &lt;span style=&quot;color: #666666;&quot;&gt;&lt;b&gt;EMPLOYEE&lt;/b&gt;&lt;/span&gt;에도 담을 수 있고, &lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;COUSTOMER&lt;/span&gt;&lt;/b&gt;에도 담을 수 있다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y2tPn/btsF7QCHdsp/IlMXvkTmSNenZGSDEKrgS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y2tPn/btsF7QCHdsp/IlMXvkTmSNenZGSDEKrgS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y2tPn/btsF7QCHdsp/IlMXvkTmSNenZGSDEKrgS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy2tPn%2FbtsF7QCHdsp%2FIlMXvkTmSNenZGSDEKrgS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;295&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt; ORDER&lt;/span&gt;&lt;/b&gt;를 제외하고, 다른 Entity에 정의한 관계는 모두 제거하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;b&gt;ORDER&lt;/b&gt;&lt;/span&gt; 개체가 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;part, customer, employee&lt;/span&gt;와 모두 직접적으로 연관되어있으므로 &lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;ORDER&lt;/span&gt;&lt;/b&gt;를 대표로 관계 속성을 정의하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;데이터 모델링, ERD&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계에서는 여러 표기법을 통해 그림으로 도식화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;DATA Refining&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;① Participation&lt;/u&gt; &lt;u&gt;② Cardinality&lt;/u&gt;를 고려한 Refining을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 DATA Refining이라는 것이 잘 이해가 안되었는데 &lt;a href=&quot;https://www.learn.geekinterview.com/data-warehouse/data-quality/what-is-data-refining.html&quot;&gt;GeekInterview&lt;/a&gt;에서 상세히 설명하고 있다. 좋은 자료라 첨부한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 세부 단계에서는 다음과 같은 개념이 활용된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Relationship Type, Sets and Instances&lt;/li&gt;
&lt;li&gt;Relational Model&lt;/li&gt;
&lt;li&gt;Realationships as Attributes&lt;/li&gt;
&lt;li&gt;Role Names and Recursive Relationships&lt;/li&gt;
&lt;li&gt;Constraints on Relationship Types&lt;/li&gt;
&lt;li&gt;Cardinality Ratios for Binary Relationships&lt;/li&gt;
&lt;li&gt;Participations Constraints and Existenxe Dependencies&lt;/li&gt;
&lt;li&gt;여러 개체 타입에 자주 등장하는 경우는 속성을 Entity로 승격하고, Entity로 두기에 애매한 것은 속성으로 강등한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 일단 과제 리뷰 포스팅이니 위와 같은 내용은 따로 DB 개념 등으로 포스팅할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 주의사항을 토대로 ERD를 그린 건 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;597&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MrJcy/btsF9cyvBcs/D6jklzSTBj7tiUigrGBMAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MrJcy/btsF9cyvBcs/D6jklzSTBj7tiUigrGBMAk/img.png&quot; data-alt=&quot;수정 전&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MrJcy/btsF9cyvBcs/D6jklzSTBj7tiUigrGBMAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMrJcy%2FbtsF9cyvBcs%2FD6jklzSTBj7tiUigrGBMAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1051&quot; height=&quot;597&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;597&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;수정 전&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2290&quot; data-origin-height=&quot;1284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6wKjE/btsF7KJzfyn/yxaKVTaxtRy8HhukdYR2b1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6wKjE/btsF7KJzfyn/yxaKVTaxtRy8HhukdYR2b1/img.png&quot; data-alt=&quot;피드백&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6wKjE/btsF7KJzfyn/yxaKVTaxtRy8HhukdYR2b1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6wKjE%2FbtsF7KJzfyn%2FyxaKVTaxtRy8HhukdYR2b1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2290&quot; height=&quot;1284&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2290&quot; data-origin-height=&quot;1284&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;피드백&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;box-sizing: border-box; font-size: 15px; *** rgb(254, 137, 67): 관련글의 테두리색 입니다. ***font-family: Arial, 돋움, Dotum, AppleGothic, sans-serif; padding: 10px; margin: 0px; line-height: 1.5; background-color: #ffffff; border: 2px solid #4f84c4;&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;[표기법]&lt;/span&gt; Composite 속성과 Multiple 속성의 표기를 혼동하였다. name, phone, address 속성은 multiple attribute를 의도하였으므로, 표기를 수정해주어야 한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;[설계 관점]&lt;/span&gt;Grade Weak Entity의 문법상 의미는 맞다. 그러나 문맥상 의미는 좋아보이지 않는다. 차라리 Student의 속성으로 강등하는 것이 나을 수도 있다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;[설계 관점]&lt;/span&gt; 주전공/부전공을 나타내는 관계속성은 그 의미는 맞으나, 오해의 소지가 있다. (ex. (1,2) min-max를 설정하면 부전공이 2개로 들어갈 수 있는데 이걸 구별하는 값이 없다. 차라리 major_department와 minor_department 두 관계로 구분짓자.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;[설계 관점]&lt;/span&gt; Section Entity는 개별적으로 구분이 안되며 종속적인 개체이기에 (1,n)은 맞지만 문맥상 완전 틀렸다. 학생이 수강하는 것은 Course가 아니라 Section이다. 따라서 Section의 Course의 Weak Entity이어야 하고, Section은 Studer와 연관관계 매핑이 된다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;[중복 데이터]&lt;/span&gt; Student와 Grade를 연관관계 매핑해놓고, partial key에 student, section을 주었다. 이는 중복 데이터다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;[표기법]&lt;/span&gt; Partial/Total Participation과 Chen 기법을 혼용하였다. 하나로 통일하는 것이 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;708&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcWGjX/btsF9K9sI6s/1TldOAnMGJxNQUJbynFeRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcWGjX/btsF9K9sI6s/1TldOAnMGJxNQUJbynFeRk/img.png&quot; data-alt=&quot;수정 후&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcWGjX/btsF9K9sI6s/1TldOAnMGJxNQUJbynFeRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcWGjX%2FbtsF9K9sI6s%2F1TldOAnMGJxNQUJbynFeRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;519&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;708&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;수정 후&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;속성들은 생략하고 수정해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CHgsi/btsF7xqgMdk/YdqVWc4FMIFUDQ8OkqHLCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CHgsi/btsF7xqgMdk/YdqVWc4FMIFUDQ8OkqHLCK/img.png&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.415%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CHgsi/btsF7xqgMdk/YdqVWc4FMIFUDQ8OkqHLCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCHgsi%2FbtsF7xqgMdk%2FYdqVWc4FMIFUDQ8OkqHLCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4032&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VKCEl/btsF9cel131/wNzakKD2HDHdDsmYlmKv20/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VKCEl/btsF9cel131/wNzakKD2HDHdDsmYlmKv20/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2271&quot; data-origin-height=&quot;1703&quot; data-filename=&quot;IMG_1122.jpg&quot; style=&quot;width: 49.4222%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VKCEl/btsF9cel131/wNzakKD2HDHdDsmYlmKv20/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVKCEl%2FbtsF9cel131%2FwNzakKD2HDHdDsmYlmKv20%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2271&quot; height=&quot;1703&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 생략하고 바로 ERD 도식화에 들어가서 놓치는 부분이 많을 수밖에 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;교수님께서도 이 과정을 통해 중복된 값을 제거하는 것이 중요하다고 강조하셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배운 게 많은 만큼 다음 과제는 더 명확하게 해봐야겠다~~&lt;/p&gt;</description>
      <category>DB/DB 설계 과제</category>
      <category>DB</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/270</guid>
      <comments>https://cobinding.tistory.com/270#entry270comment</comments>
      <pubDate>Wed, 27 Mar 2024 14:51:58 +0900</pubDate>
    </item>
    <item>
      <title>[개발일지] 배포 삽질기 | 심볼릭 링크, 리눅스의 Capacities, ufw, netstat</title>
      <link>https://cobinding.tistory.com/269</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 프로젝트 배포 테스트를 하는 과정에서 했던 삽질에 대해 기록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 도메인 내에서 여러 포트를 사용하고, 또 여러 웹서버를 사용하는데 제대로 서버/네트워크를 파악하지 못할 시 시도해볼만한 것들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;내가 진행한 서버의 개략적인 상황은 다음과 같다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP/HTTPS 요청&lt;/li&gt;
&lt;li&gt;80, 8085, 8080 - Tomcat, Apache, java application
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 중에서 8080은 서버(platform) 역할을 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;8081 - Node.js(React), Nginx&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;box-sizing: border-box; font-size: 15px; *** rgb(254, 137, 67): 관련글의 테두리색 입니다. ***font-family: Arial, 돋움, Dotum, AppleGothic, sans-serif; padding: 10px; margin: 0px; line-height: 1.5; background-color: #ffffff; border: 2px solid #4f84c4;&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;일단 Ningx 웹서버 위에 React 프로젝트를 올리는 건 따로 포스팅할 예정이다. 여기서는 정말 내가 했던 삽질, 날 것을 다룬다...&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;까먹지 않으려고 기록하는 용이고 필요하다면 키워드를 통해 다른 사람도 활용할 수 있도록 정리해볼 것이다.&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;포트포워딩 관련 문제가 생겼다면 체크해볼 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;application 레벨에서 port 설정이 잘 되어있는가? 예를 들어, 자바 애플리케이션은 프로퍼티 파일에 &lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt; server.port=80 &lt;/span&gt; 이런식으로 설정할 수 있다.&lt;/li&gt;
&lt;li&gt;방화벽이 잘 세팅되었는가? -&amp;gt; VPC나 ubuntu같은 경우는 ufw로 해결&lt;/li&gt;
&lt;li&gt;클라우드 환경에 배포한다면, 사용할 포트에 대해 허용해줬는가? -&amp;gt; ufw 등 리눅스 명령어로 방화벽 관리&lt;/li&gt;
&lt;li&gt;Nginx같은 웹서버라면, &lt;span style=&quot;color: #ef5369;&quot;&gt;.conf&lt;/span&gt; 등의 세팅 파일 확인 -&amp;gt; 서버의 포트가 잘 설정되었는지 직접 파일을 열어 확인할 수있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다양한 Linux - Debian / RedHat&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux는 UNIX 기반의 다중 작업을 지원하는 네트워크 OS다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux는 오픈소스이기에, 다양한 많은 버전의 리눅스가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;대표적인 Linux OOS&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 59.4186%; height: 99px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.5401%;&quot;&gt;회사&lt;/td&gt;
&lt;td style=&quot;width: 40.4599%;&quot;&gt;대표적인 Linux&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;관리 패키지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 34.5401%; height: 17px;&quot;&gt;RedHat&lt;/td&gt;
&lt;td style=&quot;width: 40.4599%; height: 17px;&quot;&gt;CentOS&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;yum&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 34.5401%; height: 17px;&quot;&gt;Debian&lt;/td&gt;
&lt;td style=&quot;width: 40.4599%; height: 17px;&quot;&gt;Ubuntu&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;apt / apt-get&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 둘 간의 차이를 명확하게 이해하지 못해서 약간의 시간 낭비를 했다.&lt;/li&gt;
&lt;li&gt;RedHat 계열의 CentOS는 2021년부로 업그레이드를 하지 않으므로, 사용에 주의하도록 하자.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔&lt;span style=&quot;background-color: #f6e199;&quot;&gt; 비교적 더 저렴한 CentOS에서 node 프로젝트를 배포하는 작업&lt;/span&gt;을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 &lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt;GLIBC_2.27&amp;rsquo; not found (required by node) &lt;/span&gt; 이런 에러가 발생하였다. 이는 해당 OS가 아래 목록의 라이브러리를 지원하지 못하는 케이스다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uEh1f/btsF1JxDyof/BC4P2qmEMhWxrdlfJuZs90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uEh1f/btsF1JxDyof/BC4P2qmEMhWxrdlfJuZs90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uEh1f/btsF1JxDyof/BC4P2qmEMhWxrdlfJuZs90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuEh1f%2FbtsF1JxDyof%2FBC4P2qmEMhWxrdlfJuZs90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;729&quot; height=&quot;107&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결하는 방법은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;OS를 업그레이드한다.&lt;/li&gt;
&lt;li&gt;OS를 바꾼다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번은 내가 정확하게 이해한 건지도 모르겠고 여러 개발 커뮤니티, 문서 등에서 2번을 추천했기에 과감히 2번으로 선택했다. 많이 귀찮아질 것을 예상했지만... 요구사항이 Node 버전은 18이상이었기에, 이를 맞추려면 불가피한 선택이었다. 나도 이 과정 때문에 알게된건데 CentOS는 더이상 업그레이드가 안되는 구버전의 linux다. 그냥 ubuntu 쓰자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;심볼릭 링크(symbolic link)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바 애플리케이션을 배포하기 위해서 자바를 다운 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후, &lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt; which java &lt;/span&gt; 등의 명령어를 통해 자바 위치를 확인할 것이다. 이때 나오는 경로는 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;심볼릭 링크&lt;/b&gt;&lt;/span&gt;일 확률이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;심볼릭 링크란&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말 그대로 symbol. 실제 주소는 따로 있고 참조하는 링크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;왜 심볼릭 링크를 쓰는가?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관리가 편하다, 공간 절약, 편의성 등등 .. 여러가지 이유가 있는데... 솔직히 잘 이해는 가지 않는다ㅠㅠ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분에 대해서 심도있게 학습한 후 정리해야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;진짜 링크 확인 방법&lt;/h4&gt;
&lt;pre id=&quot;code_1711333240503&quot; class=&quot;shell&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;readlink -f [symbolic link]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 실행 시 심볼릭 링크를 사용해도 문제는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이 개념을 알고 있으면 애플리케이션 실행 및 배포에 문제가 생겼을 때 여러모로 활용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ubuntu 서버의 방화벽&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GCP에서 방화벽을 관리하지만, ubuntu 자체에서 관리하는 방법도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 &lt;b&gt;ufw&lt;/b&gt;라는 것인데, &lt;b&gt;Uncomplicated Firewall&lt;/b&gt;의 약자로 방화벽 관리 프로그램이다. 명령어를 통해 간단하게 관리가 가능해서 편리하고 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ufw 대표적인 사용 예제&lt;/h4&gt;
&lt;pre id=&quot;code_1711333394989&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ufw 설지
apt-get install ufw

# ufw 활성화
sudo ufw enable

# 방화벽을 허용하는 명령어
ufw allow ${port_number}

# 허용된 방화벽 포트 확인
ufw status verbose&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;주의할 점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 컴퓨팅 서비스를 이용하고 ufw를 사용하고 싶다면, &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;일단 enable 한 뒤에 22번 포트부터 열어주자.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바보같이 22번 포트를 열지않고 직렬 콘솔로 해결하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 서버에 ssh 키로 접속하여 작업을 하니까.. 명심@&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Linux Capacities, &lt;span style=&quot;color: #ef5369;&quot;&gt;well known 1024 이하 포트&lt;/span&gt;는 일반 계정으로 사용이 불가능하다.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 서버들은 루트와 일반 사용자의 권한을 철저히 분리한다. 이를 Capacity라는 시스템 하에 관리를 하는데, 보안상의 문제를 방지하기 위해서다.&amp;nbsp; &lt;b&gt;&lt;span style=&quot;background: rgba(135,131,120,.15); color: #eb5757; border-radius: 3px; padding: 0.2em 0.4em;&quot;&gt;CAP_NET_BIND_SERVICE&lt;/span&gt;&lt;/b&gt; 가 대표적인 예다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1711333653196&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;setcap 'cap_net_bind_service=+ep' [허용을 원하는 파일명의 주소]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 특권은 리눅스에서 다양한 경우의 수를 따져 만든 보안 이슈일 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 유의해서 사용해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 Linux Capacities 관련해서는 따로 깊게 공부를 할 필요가 있다고 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리눅스/네트워크 정보 관련 다양한 명령어&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;netstat&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;network statistic는 네트워크 연결, 라우팅 테이블, 포트 정보 등등 다양한 네트워크 관련 정보를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어는 사용하기 어렵지 않지만, 도커처럼 사용 옵션을 파악하는 것이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 옵션&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 77.4418%; height: 219px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;옵션&lt;/td&gt;
&lt;td style=&quot;width: 53.721%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;-a&lt;/td&gt;
&lt;td style=&quot;width: 53.721%;&quot;&gt;--all과 같다. LISTEN, NOT LISTEN 소켓정보 모두 보여준다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;-n&lt;/td&gt;
&lt;td style=&quot;width: 53.721%;&quot;&gt;--numeric과 같다. 10진수의 수치 정보로 결과를 출력한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;-r&lt;/td&gt;
&lt;td style=&quot;width: 53.721%;&quot;&gt;--route와 같다. 설정된 라우팅 정보를 출력한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;-l&lt;/td&gt;
&lt;td style=&quot;width: 53.721%;&quot;&gt;--listening과 같다. listen되고 있는 정보를 출력한다.&lt;br /&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;netstat -anp | grep LISTEN &lt;span style=&quot;color: #000000;&quot;&gt;이런식으로 직접 listen 패턴을 지정할 수도 있다.&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;lsof&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List Open Files는 현재 열려있는 파일을 보여주는 명령어다. 이때 단순한 디스크 파일이 아니라 소켓, 파이프, 디바이스 등등 다양한 종류의 파일의 정보를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1711339426501&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 443 포트에 연결된 프로세스 넘버 확인
# -i 옵션은 대소문자 구별을 하지않는 값이다.
lsof -i :443

# TCP 연결 확인
lsof -i TCP

# 특정 프로세스에 의해 열린 파일
lsof -p ${PID}

# 지정 디렉토리와 하위 파일들 
lsof +D /home/cobinding&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>회고 &amp;amp; 후기/개발 일지</category>
      <category>Linux</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/269</guid>
      <comments>https://cobinding.tistory.com/269#entry269comment</comments>
      <pubDate>Mon, 25 Mar 2024 13:04:25 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] AWS CLI 설정 및 활용하기</title>
      <link>https://cobinding.tistory.com/268</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GCP를 사용하면서 &lt;a href=&quot;https://cobinding.tistory.com/216&quot;&gt;이 글&lt;/a&gt; 과 같이 google cloud SDK라는 걸 통해 콘솔에 접근하고 여러 클라우드 서비스를 이용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS도 이와 마찬가지로 SDK, CLI(명확하게 말하자면 SDK와 CLI는 다른 것이긴 하다.)를 통해 콘솔에 접근하는 서비스가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 설정하고 AWS 서비스를 이용해보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 root 계정과 그로부터 admin 권한을 할당 받은 계정이 있다고 가정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AWS CLI latest 다운&lt;/h2&gt;
&lt;pre id=&quot;code_1711261700555&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo curl &quot;https://s3.amazonaws.com/aws-cli/awscli-bundle.zip&quot; -o &quot;awscli-bundle.zip&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/cli/v1/userguide/install-macos.html&quot;&gt;해당 문서&lt;/a&gt;는 MacOS 사용자 기준으로 다운 방법을 제시하고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 위 명령어를 iterm or terminal에 입력해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 aws --version을 통해 제대로 설치가 잘 되었는지 확인하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;151&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzhEye/btsF1l4OCXQ/zY6GkpuddRwkiXQex9bKF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzhEye/btsF1l4OCXQ/zY6GkpuddRwkiXQex9bKF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzhEye/btsF1l4OCXQ/zY6GkpuddRwkiXQex9bKF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzhEye%2FbtsF1l4OCXQ%2FzY6GkpuddRwkiXQex9bKF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;655&quot; height=&quot;151&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;151&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Access keys 발급&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IAM의 &lt;b&gt;user &amp;gt; Security credentials&lt;/b&gt;로 접근하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vy1uA/btsF2OEIEg0/B752zt0t9N0D1KgwtWTTGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vy1uA/btsF2OEIEg0/B752zt0t9N0D1KgwtWTTGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vy1uA/btsF2OEIEg0/B752zt0t9N0D1KgwtWTTGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvy1uA%2FbtsF2OEIEg0%2FB752zt0t9N0D1KgwtWTTGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1834&quot; height=&quot;792&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;792&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;span style=&quot;color: #006dd7;&quot;&gt;Create access key&lt;/span&gt;를 눌러 키를 발급해준다. 나는 이미 발급을 한 상태이다. csv 파일로 다운하는 것도 지원하니, 미리 다운해두자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AWS Configure&lt;/h2&gt;
&lt;pre id=&quot;code_1711262046632&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;aws configure&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;aws cli가 잘 다운되었으므로, 명령어를 통해 계정 접근 ID와 key를 설정해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;94&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bL2kDi/btsF3epDmxY/uKr4KJmSaAmkpgIFnhdCqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bL2kDi/btsF3epDmxY/uKr4KJmSaAmkpgIFnhdCqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bL2kDi/btsF3epDmxY/uKr4KJmSaAmkpgIFnhdCqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbL2kDi%2FbtsF3epDmxY%2FuKr4KJmSaAmkpgIFnhdCqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;596&quot; height=&quot;94&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;94&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발급받은 ID와 key를 설정하고, 원하는 리전을 선태한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Default output format의 경우 설정할 것이 없어서 단순히 enter로 마무리 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;확인 테스트&lt;/h2&gt;
&lt;pre id=&quot;code_1711262191379&quot; class=&quot;applescript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;aws iam list-users&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 명령어를 터미널에 입력하면, users에 대한 JSON값이 나온다. (AWS는 모든&lt;span style=&quot;color: #ef5369;&quot;&gt; IAM 설정을 JSON으로 통제&lt;/span&gt;할 수 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 User와 Id 등등 정보가 잘 출력되면 연동 성공이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;편리하게 AWS를 활용하는 방법을 알아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GCP를 쓸 때도 가끔 CLI가 훨씬 편한 경우도 있었는데, AWS에도 있어서 다행이다.&lt;/p&gt;</description>
      <category>DevOps/AWS</category>
      <category>AWS</category>
      <author>sebinChu</author>
      <guid isPermaLink="true">https://cobinding.tistory.com/268</guid>
      <comments>https://cobinding.tistory.com/268#entry268comment</comments>
      <pubDate>Sun, 24 Mar 2024 15:37:56 +0900</pubDate>
    </item>
  </channel>
</rss>